X-Git-Url: https://git.openstreetmap.org./rails.git/blobdiff_plain/a45af012b4c0a3f3d92bbe3da4a96356c31e2b4b..a26100c7ec48bceb50b0c3b8406809dc88cd1a65:/vendor/assets/iD/iD.js diff --git a/vendor/assets/iD/iD.js b/vendor/assets/iD/iD.js index b6b040f07..929da2dd8 100644 --- a/vendor/assets/iD/iD.js +++ b/vendor/assets/iD/iD.js @@ -1,50848 +1,103912 @@ -(function(exports) { - - var bootstrap = (typeof exports.bootstrap === "object") ? - exports.bootstrap : - (exports.bootstrap = {}); - - bootstrap.tooltip = function() { - - var tooltip = function(selection) { - selection.each(setup); - }, - animation = d3.functor(false), - html = d3.functor(false), - title = function() { - var title = this.getAttribute("data-original-title"); - if (title) { - return title; - } else { - title = this.getAttribute("title"); - this.removeAttribute("title"); - this.setAttribute("data-original-title", title); - } - return title; - }, - over = "mouseenter.tooltip", - out = "mouseleave.tooltip", - placements = "top left bottom right".split(" "), - placement = d3.functor("top"); - - tooltip.title = function(_) { - if (arguments.length) { - title = d3.functor(_); - return tooltip; - } else { - return title; - } - }; - - tooltip.html = function(_) { - if (arguments.length) { - html = d3.functor(_); - return tooltip; - } else { - return html; - } - }; - - tooltip.placement = function(_) { - if (arguments.length) { - placement = d3.functor(_); - return tooltip; - } else { - return placement; - } - }; - - tooltip.show = function(selection) { - selection.each(show); - }; - - tooltip.hide = function(selection) { - selection.each(hide); - }; - - tooltip.toggle = function(selection) { - selection.each(toggle); - }; - - tooltip.destroy = function(selection) { - selection - .on(over, null) - .on(out, null) - .attr("title", function() { - return this.getAttribute("data-original-title") || this.getAttribute("title"); - }) - .attr("data-original-title", null) - .select(".tooltip") - .remove(); - }; - - function setup() { - var root = d3.select(this), - animate = animation.apply(this, arguments), - tip = root.append("div") - .attr("class", "tooltip"); - - if (animate) { - tip.classed("fade", true); - } - - // TODO "inside" checks? - - tip.append("div") - .attr("class", "tooltip-arrow"); - tip.append("div") - .attr("class", "tooltip-inner"); - - var place = placement.apply(this, arguments); - tip.classed(place, true); - - root.on(over, show); - root.on(out, hide); - } - - function show() { - var root = d3.select(this), - content = title.apply(this, arguments), - tip = root.select(".tooltip") - .classed("in", true), - markup = html.apply(this, arguments), - innercontent = tip.select(".tooltip-inner")[markup ? "html" : "text"](content), - place = placement.apply(this, arguments), - outer = getPosition(root.node()), - inner = getPosition(tip.node()), - pos; - - switch (place) { - case "top": - pos = {x: outer.x + (outer.w - inner.w) / 2, y: outer.y - inner.h}; - break; - case "right": - pos = {x: outer.x + outer.w, y: outer.y + (outer.h - inner.h) / 2}; - break; - case "left": - pos = {x: outer.x - inner.w, y: outer.y + (outer.h - inner.h) / 2}; - break; - case "bottom": - pos = {x: Math.max(0, outer.x + (outer.w - inner.w) / 2), y: outer.y + outer.h}; - break; - } - - tip.style(pos ? - {left: ~~pos.x + "px", top: ~~pos.y + "px"} : - {left: null, top: null}); - - this.tooltipVisible = true; - } - - function hide() { - d3.select(this).select(".tooltip") - .classed("in", false); - - this.tooltipVisible = false; - } - - function toggle() { - if (this.tooltipVisible) { - hide.apply(this, arguments); - } else { - show.apply(this, arguments); - } - } - - return tooltip; - }; - - function getPosition(node) { - var mode = d3.select(node).style('position'); - if (mode === 'absolute' || mode === 'static') { - return { - x: node.offsetLeft, - y: node.offsetTop, - w: node.offsetWidth, - h: node.offsetHeight - }; - } else { - return { - x: 0, - y: 0, - w: node.offsetWidth, - h: node.offsetHeight - }; - } - } - -})(this); -d3 = (function(){ - var d3 = {version: "3.2.7"}; // semver -d3.ascending = function(a, b) { - return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; -}; -d3.descending = function(a, b) { - return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; -}; -d3.min = function(array, f) { - var i = -1, - n = array.length, - a, - b; - if (arguments.length === 1) { - while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined; - while (++i < n) if ((b = array[i]) != null && a > b) a = b; - } else { - while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined; - while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; - } - return a; -}; -d3.max = function(array, f) { - var i = -1, - n = array.length, - a, - b; - if (arguments.length === 1) { - while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined; - while (++i < n) if ((b = array[i]) != null && b > a) a = b; - } else { - while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined; - while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; - } - return a; -}; -d3.extent = function(array, f) { - var i = -1, - n = array.length, - a, - b, - c; - if (arguments.length === 1) { - while (++i < n && !((a = c = array[i]) != null && a <= a)) a = c = undefined; - while (++i < n) if ((b = array[i]) != null) { - if (a > b) a = b; - if (c < b) c = b; - } - } else { - while (++i < n && !((a = c = f.call(array, array[i], i)) != null && a <= a)) a = undefined; - while (++i < n) if ((b = f.call(array, array[i], i)) != null) { - if (a > b) a = b; - if (c < b) c = b; - } - } - return [a, c]; -}; -d3.sum = function(array, f) { - var s = 0, - n = array.length, - a, - i = -1; - - if (arguments.length === 1) { - while (++i < n) if (!isNaN(a = +array[i])) s += a; - } else { - while (++i < n) if (!isNaN(a = +f.call(array, array[i], i))) s += a; - } - - return s; -}; -function d3_number(x) { - return x != null && !isNaN(x); -} - -d3.mean = function(array, f) { - var n = array.length, - a, - m = 0, - i = -1, - j = 0; - if (arguments.length === 1) { - while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j; - } else { - while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j; - } - return j ? m : undefined; -}; -// R-7 per -d3.quantile = function(values, p) { - var H = (values.length - 1) * p + 1, - h = Math.floor(H), - v = +values[h - 1], - e = H - h; - return e ? v + e * (values[h] - v) : v; -}; - -d3.median = function(array, f) { - if (arguments.length > 1) array = array.map(f); - array = array.filter(d3_number); - return array.length ? d3.quantile(array.sort(d3.ascending), .5) : undefined; -}; -d3.bisector = function(f) { - return { - left: function(a, x, lo, hi) { - if (arguments.length < 3) lo = 0; - if (arguments.length < 4) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (f.call(a, a[mid], mid) < x) lo = mid + 1; - else hi = mid; - } - return lo; - }, - right: function(a, x, lo, hi) { - if (arguments.length < 3) lo = 0; - if (arguments.length < 4) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (x < f.call(a, a[mid], mid)) hi = mid; - else lo = mid + 1; - } - return lo; - } - }; -}; - -var d3_bisector = d3.bisector(function(d) { return d; }); -d3.bisectLeft = d3_bisector.left; -d3.bisect = d3.bisectRight = d3_bisector.right; -d3.shuffle = function(array) { - var m = array.length, t, i; - while (m) { - i = Math.random() * m-- | 0; - t = array[m], array[m] = array[i], array[i] = t; - } - return array; -}; -d3.permute = function(array, indexes) { - var permutes = [], - i = -1, - n = indexes.length; - while (++i < n) permutes[i] = array[indexes[i]]; - return permutes; -}; - -d3.zip = function() { - if (!(n = arguments.length)) return []; - for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m;) { - for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n;) { - zip[j] = arguments[j][i]; - } - } - return zips; -}; - -function d3_zipLength(d) { - return d.length; -} - -d3.transpose = function(matrix) { - return d3.zip.apply(d3, matrix); -}; -d3.keys = function(map) { - var keys = []; - for (var key in map) keys.push(key); - return keys; -}; -d3.values = function(map) { - var values = []; - for (var key in map) values.push(map[key]); - return values; -}; -d3.entries = function(map) { - var entries = []; - for (var key in map) entries.push({key: key, value: map[key]}); - return entries; -}; -d3.merge = function(arrays) { - return Array.prototype.concat.apply([], arrays); -}; -d3.range = function(start, stop, step) { - if (arguments.length < 3) { - step = 1; - if (arguments.length < 2) { - stop = start; - start = 0; - } - } - if ((stop - start) / step === Infinity) throw new Error("infinite range"); - var range = [], - k = d3_range_integerScale(Math.abs(step)), - i = -1, - j; - start *= k, stop *= k, step *= k; - if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); - else while ((j = start + step * ++i) < stop) range.push(j / k); - return range; -}; - -function d3_range_integerScale(x) { - var k = 1; - while (x * k % 1) k *= 10; - return k; -} -function d3_class(ctor, properties) { - try { - for (var key in properties) { - Object.defineProperty(ctor.prototype, key, { - value: properties[key], - enumerable: false - }); - } - } catch (e) { - ctor.prototype = properties; - } -} - -d3.map = function(object) { - var map = new d3_Map; - for (var key in object) map.set(key, object[key]); - return map; -}; - -function d3_Map() {} - -d3_class(d3_Map, { - has: function(key) { - return d3_map_prefix + key in this; - }, - get: function(key) { - return this[d3_map_prefix + key]; - }, - set: function(key, value) { - return this[d3_map_prefix + key] = value; - }, - remove: function(key) { - key = d3_map_prefix + key; - return key in this && delete this[key]; - }, - keys: function() { - var keys = []; - this.forEach(function(key) { keys.push(key); }); - return keys; - }, - values: function() { - var values = []; - this.forEach(function(key, value) { values.push(value); }); - return values; - }, - entries: function() { - var entries = []; - this.forEach(function(key, value) { entries.push({key: key, value: value}); }); - return entries; - }, - forEach: function(f) { - for (var key in this) { - if (key.charCodeAt(0) === d3_map_prefixCode) { - f.call(this, key.substring(1), this[key]); - } - } - } -}); - -var d3_map_prefix = "\0", // prevent collision with built-ins - d3_map_prefixCode = d3_map_prefix.charCodeAt(0); - -d3.nest = function() { - var nest = {}, - keys = [], - sortKeys = [], - sortValues, - rollup; - - function map(mapType, array, depth) { - if (depth >= keys.length) return rollup - ? rollup.call(nest, array) : (sortValues - ? array.sort(sortValues) - : array); - - var i = -1, - n = array.length, - key = keys[depth++], - keyValue, - object, - setter, - valuesByKey = new d3_Map, - values; - - while (++i < n) { - if (values = valuesByKey.get(keyValue = key(object = array[i]))) { - values.push(object); - } else { - valuesByKey.set(keyValue, [object]); - } - } - - if (mapType) { - object = mapType(); - setter = function(keyValue, values) { - object.set(keyValue, map(mapType, values, depth)); - }; - } else { - object = {}; - setter = function(keyValue, values) { - object[keyValue] = map(mapType, values, depth); - }; - } - - valuesByKey.forEach(setter); - return object; - } - - function entries(map, depth) { - if (depth >= keys.length) return map; - - var array = [], - sortKey = sortKeys[depth++]; - - map.forEach(function(key, keyMap) { - array.push({key: key, values: entries(keyMap, depth)}); - }); - - return sortKey - ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) - : array; - } - - nest.map = function(array, mapType) { - return map(mapType, array, 0); - }; - - nest.entries = function(array) { - return entries(map(d3.map, array, 0), 0); - }; - - nest.key = function(d) { - keys.push(d); - return nest; - }; - - // Specifies the order for the most-recently specified key. - // Note: only applies to entries. Map keys are unordered! - nest.sortKeys = function(order) { - sortKeys[keys.length - 1] = order; - return nest; - }; - - // Specifies the order for leaf values. - // Applies to both maps and entries array. - nest.sortValues = function(order) { - sortValues = order; - return nest; - }; - - nest.rollup = function(f) { - rollup = f; - return nest; - }; - - return nest; -}; - -d3.set = function(array) { - var set = new d3_Set(); - if (array) for (var i = 0; i < array.length; i++) set.add(array[i]); - return set; -}; - -function d3_Set() {} - -d3_class(d3_Set, { - has: function(value) { - return d3_map_prefix + value in this; - }, - add: function(value) { - this[d3_map_prefix + value] = true; - return value; - }, - remove: function(value) { - value = d3_map_prefix + value; - return value in this && delete this[value]; - }, - values: function() { - var values = []; - this.forEach(function(value) { - values.push(value); - }); - return values; - }, - forEach: function(f) { - for (var value in this) { - if (value.charCodeAt(0) === d3_map_prefixCode) { - f.call(this, value.substring(1)); - } - } - } -}); -d3.behavior = {}; -var d3_document = document, - d3_documentElement = d3_document.documentElement, - d3_window = window; -// Copies a variable number of methods from source to target. -d3.rebind = function(target, source) { - var i = 1, n = arguments.length, method; - while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); - return target; -}; - -// Method is assumed to be a standard D3 getter-setter: -// If passed with no arguments, gets the value. -// If passed with arguments, sets the value and returns the target. -function d3_rebind(target, source, method) { - return function() { - var value = method.apply(source, arguments); - return value === source ? target : value; - }; -} - -function d3_vendorSymbol(object, name) { - if (name in object) return name; - name = name.charAt(0).toUpperCase() + name.substring(1); - for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) { - var prefixName = d3_vendorPrefixes[i] + name; - if (prefixName in object) return prefixName; - } -} - -var d3_vendorPrefixes = ["webkit", "ms", "moz", "Moz", "o", "O"]; - -var d3_array = d3_arraySlice; // conversion for NodeLists - -function d3_arrayCopy(pseudoarray) { - var i = -1, n = pseudoarray.length, array = []; - while (++i < n) array.push(pseudoarray[i]); - return array; -} - -function d3_arraySlice(pseudoarray) { - return Array.prototype.slice.call(pseudoarray); -} - -try { - d3_array(d3_documentElement.childNodes)[0].nodeType; -} catch(e) { - d3_array = d3_arrayCopy; -} -function d3_noop() {} - -d3.dispatch = function() { - var dispatch = new d3_dispatch, - i = -1, - n = arguments.length; - while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); - return dispatch; -}; - -function d3_dispatch() {} - -d3_dispatch.prototype.on = function(type, listener) { - var i = type.indexOf("."), - name = ""; - - // Extract optional namespace, e.g., "click.foo" - if (i >= 0) { - name = type.substring(i + 1); - type = type.substring(0, i); - } - - if (type) return arguments.length < 2 - ? this[type].on(name) - : this[type].on(name, listener); - - if (arguments.length === 2) { - if (listener == null) for (type in this) { - if (this.hasOwnProperty(type)) this[type].on(name, null); - } - return this; - } -}; - -function d3_dispatch_event(dispatch) { - var listeners = [], - listenerByName = new d3_Map; - - function event() { - var z = listeners, // defensive reference - i = -1, - n = z.length, - l; - while (++i < n) if (l = z[i].on) l.apply(this, arguments); - return dispatch; - } - - event.on = function(name, listener) { - var l = listenerByName.get(name), - i; - - // return the current listener, if any - if (arguments.length < 2) return l && l.on; - - // remove the old listener, if any (with copy-on-write) - if (l) { - l.on = null; - listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); - listenerByName.remove(name); - } - - // add the new listener, if any - if (listener) listeners.push(listenerByName.set(name, {on: listener})); - - return dispatch; - }; - - return event; -} - -d3.event = null; - -function d3_eventPreventDefault() { - d3.event.preventDefault(); -} - -function d3_eventCancel() { - d3.event.preventDefault(); - d3.event.stopPropagation(); -} - -function d3_eventSource() { - var e = d3.event, s; - while (s = e.sourceEvent) e = s; - return e; -} - -// Like d3.dispatch, but for custom events abstracting native UI events. These -// events have a target component (such as a brush), a target element (such as -// the svg:g element containing the brush) and the standard arguments `d` (the -// target element's data) and `i` (the selection index of the target element). -function d3_eventDispatch(target) { - var dispatch = new d3_dispatch, - i = 0, - n = arguments.length; - - while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); - - // Creates a dispatch context for the specified `thiz` (typically, the target - // DOM element that received the source event) and `argumentz` (typically, the - // data `d` and index `i` of the target element). The returned function can be - // used to dispatch an event to any registered listeners; the function takes a - // single argument as input, being the event to dispatch. The event must have - // a "type" attribute which corresponds to a type registered in the - // constructor. This context will automatically populate the "sourceEvent" and - // "target" attributes of the event, as well as setting the `d3.event` global - // for the duration of the notification. - dispatch.of = function(thiz, argumentz) { - return function(e1) { - try { - var e0 = - e1.sourceEvent = d3.event; - e1.target = target; - d3.event = e1; - dispatch[e1.type].apply(thiz, argumentz); - } finally { - d3.event = e0; - } - }; - }; - - return dispatch; -} -d3.requote = function(s) { - return s.replace(d3_requote_re, "\\$&"); -}; - -var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; -var d3_subclass = {}.__proto__? - -// Until ECMAScript supports array subclassing, prototype injection works well. -function(object, prototype) { - object.__proto__ = prototype; -}: - -// And if your browser doesn't support __proto__, we'll use direct extension. -function(object, prototype) { - for (var property in prototype) object[property] = prototype[property]; -}; - -function d3_selection(groups) { - d3_subclass(groups, d3_selectionPrototype); - return groups; -} - -var d3_select = function(s, n) { return n.querySelector(s); }, - d3_selectAll = function(s, n) { return n.querySelectorAll(s); }, - d3_selectMatcher = d3_documentElement[d3_vendorSymbol(d3_documentElement, "matchesSelector")], - d3_selectMatches = function(n, s) { return d3_selectMatcher.call(n, s); }; - -// Prefer Sizzle, if available. -if (typeof Sizzle === "function") { - d3_select = function(s, n) { return Sizzle(s, n)[0] || null; }; - d3_selectAll = function(s, n) { return Sizzle.uniqueSort(Sizzle(s, n)); }; - d3_selectMatches = Sizzle.matchesSelector; -} - -d3.selection = function() { - return d3_selectionRoot; -}; - -var d3_selectionPrototype = d3.selection.prototype = []; - - -d3_selectionPrototype.select = function(selector) { - var subgroups = [], - subgroup, - subnode, - group, - node; - - selector = d3_selection_selector(selector); - - for (var j = -1, m = this.length; ++j < m;) { - subgroups.push(subgroup = []); - subgroup.parentNode = (group = this[j]).parentNode; - for (var i = -1, n = group.length; ++i < n;) { - if (node = group[i]) { - subgroup.push(subnode = selector.call(node, node.__data__, i, j)); - if (subnode && "__data__" in node) subnode.__data__ = node.__data__; - } else { - subgroup.push(null); - } - } - } - - return d3_selection(subgroups); -}; - -function d3_selection_selector(selector) { - return typeof selector === "function" ? selector : function() { - return d3_select(selector, this); - }; -} - -d3_selectionPrototype.selectAll = function(selector) { - var subgroups = [], - subgroup, - node; - - selector = d3_selection_selectorAll(selector); - - for (var j = -1, m = this.length; ++j < m;) { - for (var group = this[j], i = -1, n = group.length; ++i < n;) { - if (node = group[i]) { - subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j))); - subgroup.parentNode = node; - } - } - } - - return d3_selection(subgroups); -}; - -function d3_selection_selectorAll(selector) { - return typeof selector === "function" ? selector : function() { - return d3_selectAll(selector, this); - }; -} -var d3_nsPrefix = { - svg: "http://www.w3.org/2000/svg", - xhtml: "http://www.w3.org/1999/xhtml", - xlink: "http://www.w3.org/1999/xlink", - xml: "http://www.w3.org/XML/1998/namespace", - xmlns: "http://www.w3.org/2000/xmlns/" -}; - -d3.ns = { - prefix: d3_nsPrefix, - qualify: function(name) { - var i = name.indexOf(":"), - prefix = name; - if (i >= 0) { - prefix = name.substring(0, i); - name = name.substring(i + 1); - } - return d3_nsPrefix.hasOwnProperty(prefix) - ? {space: d3_nsPrefix[prefix], local: name} - : name; - } -}; - -d3_selectionPrototype.attr = function(name, value) { - if (arguments.length < 2) { - - // For attr(string), return the attribute value for the first node. - if (typeof name === "string") { - var node = this.node(); - name = d3.ns.qualify(name); - return name.local - ? node.getAttributeNS(name.space, name.local) - : node.getAttribute(name); - } - - // For attr(object), the object specifies the names and values of the - // attributes to set or remove. The values may be functions that are - // evaluated for each element. - for (value in name) this.each(d3_selection_attr(value, name[value])); - return this; - } - - return this.each(d3_selection_attr(name, value)); -}; - -function d3_selection_attr(name, value) { - name = d3.ns.qualify(name); - - // For attr(string, null), remove the attribute with the specified name. - function attrNull() { - this.removeAttribute(name); - } - function attrNullNS() { - this.removeAttributeNS(name.space, name.local); - } - - // For attr(string, string), set the attribute with the specified name. - function attrConstant() { - this.setAttribute(name, value); - } - function attrConstantNS() { - this.setAttributeNS(name.space, name.local, value); - } - - // For attr(string, function), evaluate the function for each element, and set - // or remove the attribute as appropriate. - function attrFunction() { - var x = value.apply(this, arguments); - if (x == null) this.removeAttribute(name); - else this.setAttribute(name, x); - } - function attrFunctionNS() { - var x = value.apply(this, arguments); - if (x == null) this.removeAttributeNS(name.space, name.local); - else this.setAttributeNS(name.space, name.local, x); - } - - return value == null - ? (name.local ? attrNullNS : attrNull) : (typeof value === "function" - ? (name.local ? attrFunctionNS : attrFunction) - : (name.local ? attrConstantNS : attrConstant)); -} -function d3_collapse(s) { - return s.trim().replace(/\s+/g, " "); -} - -d3_selectionPrototype.classed = function(name, value) { - if (arguments.length < 2) { - - // For classed(string), return true only if the first node has the specified - // class or classes. Note that even if the browser supports DOMTokenList, it - // probably doesn't support it on SVG elements (which can be animated). - if (typeof name === "string") { - var node = this.node(), - n = (name = name.trim().split(/^|\s+/g)).length, - i = -1; - if (value = node.classList) { - while (++i < n) if (!value.contains(name[i])) return false; - } else { - value = node.getAttribute("class"); - while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; - } - return true; - } - - // For classed(object), the object specifies the names of classes to add or - // remove. The values may be functions that are evaluated for each element. - for (value in name) this.each(d3_selection_classed(value, name[value])); - return this; - } - - // Otherwise, both a name and a value are specified, and are handled as below. - return this.each(d3_selection_classed(name, value)); -}; - -function d3_selection_classedRe(name) { - return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); -} - -// Multiple class names are allowed (e.g., "foo bar"). -function d3_selection_classed(name, value) { - name = name.trim().split(/\s+/).map(d3_selection_classedName); - var n = name.length; - - function classedConstant() { - var i = -1; - while (++i < n) name[i](this, value); - } - - // When the value is a function, the function is still evaluated only once per - // element even if there are multiple class names. - function classedFunction() { - var i = -1, x = value.apply(this, arguments); - while (++i < n) name[i](this, x); - } - - return typeof value === "function" - ? classedFunction - : classedConstant; -} - -function d3_selection_classedName(name) { - var re = d3_selection_classedRe(name); - return function(node, value) { - if (c = node.classList) return value ? c.add(name) : c.remove(name); - var c = node.getAttribute("class") || ""; - if (value) { - re.lastIndex = 0; - if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name)); - } else { - node.setAttribute("class", d3_collapse(c.replace(re, " "))); - } - }; -} - -d3_selectionPrototype.style = function(name, value, priority) { - var n = arguments.length; - if (n < 3) { - - // For style(object) or style(object, string), the object specifies the - // names and values of the attributes to set or remove. The values may be - // functions that are evaluated for each element. The optional string - // specifies the priority. - if (typeof name !== "string") { - if (n < 2) value = ""; - for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); - return this; - } - - // For style(string), return the computed style value for the first node. - if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name); - - // For style(string, string) or style(string, function), use the default - // priority. The priority is ignored for style(string, null). - priority = ""; - } - - // Otherwise, a name, value and priority are specified, and handled as below. - return this.each(d3_selection_style(name, value, priority)); -}; - -function d3_selection_style(name, value, priority) { - - // For style(name, null) or style(name, null, priority), remove the style - // property with the specified name. The priority is ignored. - function styleNull() { - this.style.removeProperty(name); - } - - // For style(name, string) or style(name, string, priority), set the style - // property with the specified name, using the specified priority. - function styleConstant() { - this.style.setProperty(name, value, priority); - } - - // For style(name, function) or style(name, function, priority), evaluate the - // function for each element, and set or remove the style property as - // appropriate. When setting, use the specified priority. - function styleFunction() { - var x = value.apply(this, arguments); - if (x == null) this.style.removeProperty(name); - else this.style.setProperty(name, x, priority); - } - - return value == null - ? styleNull : (typeof value === "function" - ? styleFunction : styleConstant); -} - -d3_selectionPrototype.property = function(name, value) { - if (arguments.length < 2) { - - // For property(string), return the property value for the first node. - if (typeof name === "string") return this.node()[name]; - - // For property(object), the object specifies the names and values of the - // properties to set or remove. The values may be functions that are - // evaluated for each element. - for (value in name) this.each(d3_selection_property(value, name[value])); - return this; - } - - // Otherwise, both a name and a value are specified, and are handled as below. - return this.each(d3_selection_property(name, value)); -}; - -function d3_selection_property(name, value) { - - // For property(name, null), remove the property with the specified name. - function propertyNull() { - delete this[name]; - } - - // For property(name, string), set the property with the specified name. - function propertyConstant() { - this[name] = value; - } - - // For property(name, function), evaluate the function for each element, and - // set or remove the property as appropriate. - function propertyFunction() { - var x = value.apply(this, arguments); - if (x == null) delete this[name]; - else this[name] = x; - } - - return value == null - ? propertyNull : (typeof value === "function" - ? propertyFunction : propertyConstant); -} - -d3_selectionPrototype.text = function(value) { - return arguments.length - ? this.each(typeof value === "function" - ? function() { var v = value.apply(this, arguments); this.textContent = v == null ? "" : v; } : value == null - ? function() { if (this.textContent !== "") this.textContent = ""; } - : function() { if (this.textContent !== value) this.textContent = value; }) - : this.node().textContent; -}; - -d3_selectionPrototype.html = function(value) { - return arguments.length - ? this.each(typeof value === "function" - ? function() { var v = value.apply(this, arguments); this.innerHTML = v == null ? "" : v; } : value == null - ? function() { this.innerHTML = ""; } - : function() { this.innerHTML = value; }) - : this.node().innerHTML; -}; - -d3_selectionPrototype.append = function(name) { - name = d3_selection_creator(name); - return this.select(function() { - return this.appendChild(name.apply(this, arguments)); - }); -}; - -function d3_selection_creator(name) { - return typeof name === "function" ? name - : (name = d3.ns.qualify(name)).local ? function() { return d3_document.createElementNS(name.space, name.local); } - : function() { return d3_document.createElementNS(this.namespaceURI, name); }; -} - -d3_selectionPrototype.insert = function(name, before) { - name = d3_selection_creator(name); - before = d3_selection_selector(before); - return this.select(function() { - return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments)); - }); -}; - -// TODO remove(selector)? -// TODO remove(node)? -// TODO remove(function)? -d3_selectionPrototype.remove = function() { - return this.each(function() { - var parent = this.parentNode; - if (parent) parent.removeChild(this); - }); -}; - -d3_selectionPrototype.data = function(value, key) { - var i = -1, - n = this.length, - group, - node; - - // If no value is specified, return the first value. - if (!arguments.length) { - value = new Array(n = (group = this[0]).length); - while (++i < n) { - if (node = group[i]) { - value[i] = node.__data__; - } - } - return value; - } - - function bind(group, groupData) { - var i, - n = group.length, - m = groupData.length, - n0 = Math.min(n, m), - updateNodes = new Array(m), - enterNodes = new Array(m), - exitNodes = new Array(n), - node, - nodeData; - - if (key) { - var nodeByKeyValue = new d3_Map, - dataByKeyValue = new d3_Map, - keyValues = [], - keyValue; - - for (i = -1; ++i < n;) { - keyValue = key.call(node = group[i], node.__data__, i); - if (nodeByKeyValue.has(keyValue)) { - exitNodes[i] = node; // duplicate selection key - } else { - nodeByKeyValue.set(keyValue, node); - } - keyValues.push(keyValue); - } - - for (i = -1; ++i < m;) { - keyValue = key.call(groupData, nodeData = groupData[i], i); - if (node = nodeByKeyValue.get(keyValue)) { - updateNodes[i] = node; - node.__data__ = nodeData; - } else if (!dataByKeyValue.has(keyValue)) { // no duplicate data key - enterNodes[i] = d3_selection_dataNode(nodeData); - } - dataByKeyValue.set(keyValue, nodeData); - nodeByKeyValue.remove(keyValue); - } - - for (i = -1; ++i < n;) { - if (nodeByKeyValue.has(keyValues[i])) { - exitNodes[i] = group[i]; - } - } - } else { - for (i = -1; ++i < n0;) { - node = group[i]; - nodeData = groupData[i]; - if (node) { - node.__data__ = nodeData; - updateNodes[i] = node; - } else { - enterNodes[i] = d3_selection_dataNode(nodeData); - } - } - for (; i < m; ++i) { - enterNodes[i] = d3_selection_dataNode(groupData[i]); - } - for (; i < n; ++i) { - exitNodes[i] = group[i]; - } - } - - enterNodes.update - = updateNodes; - - enterNodes.parentNode - = updateNodes.parentNode - = exitNodes.parentNode - = group.parentNode; - - enter.push(enterNodes); - update.push(updateNodes); - exit.push(exitNodes); - } - - var enter = d3_selection_enter([]), - update = d3_selection([]), - exit = d3_selection([]); - - if (typeof value === "function") { - while (++i < n) { - bind(group = this[i], value.call(group, group.parentNode.__data__, i)); - } - } else { - while (++i < n) { - bind(group = this[i], value); - } - } - - update.enter = function() { return enter; }; - update.exit = function() { return exit; }; - return update; -}; - -function d3_selection_dataNode(data) { - return {__data__: data}; -} - -d3_selectionPrototype.datum = function(value) { - return arguments.length - ? this.property("__data__", value) - : this.property("__data__"); -}; - -d3_selectionPrototype.filter = function(filter) { - var subgroups = [], - subgroup, - group, - node; - - if (typeof filter !== "function") filter = d3_selection_filter(filter); - - for (var j = 0, m = this.length; j < m; j++) { - subgroups.push(subgroup = []); - subgroup.parentNode = (group = this[j]).parentNode; - for (var i = 0, n = group.length; i < n; i++) { - if ((node = group[i]) && filter.call(node, node.__data__, i)) { - subgroup.push(node); - } - } - } - - return d3_selection(subgroups); -}; - -function d3_selection_filter(selector) { - return function() { - return d3_selectMatches(this, selector); - }; -} - -d3_selectionPrototype.order = function() { - for (var j = -1, m = this.length; ++j < m;) { - for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0;) { - if (node = group[i]) { - if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); - next = node; - } - } - } - return this; -}; - -d3_selectionPrototype.sort = function(comparator) { - comparator = d3_selection_sortComparator.apply(this, arguments); - for (var j = -1, m = this.length; ++j < m;) this[j].sort(comparator); - return this.order(); -}; - -function d3_selection_sortComparator(comparator) { - if (!arguments.length) comparator = d3.ascending; - return function(a, b) { - return (!a - !b) || comparator(a.__data__, b.__data__); - }; -} - -d3_selectionPrototype.each = function(callback) { - return d3_selection_each(this, function(node, i, j) { - callback.call(node, node.__data__, i, j); - }); -}; - -function d3_selection_each(groups, callback) { - for (var j = 0, m = groups.length; j < m; j++) { - for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { - if (node = group[i]) callback(node, i, j); - } - } - return groups; -} - -d3_selectionPrototype.call = function(callback) { - var args = d3_array(arguments); - callback.apply(args[0] = this, args); - return this; -}; - -d3_selectionPrototype.empty = function() { - return !this.node(); -}; - -d3_selectionPrototype.node = function() { - for (var j = 0, m = this.length; j < m; j++) { - for (var group = this[j], i = 0, n = group.length; i < n; i++) { - var node = group[i]; - if (node) return node; - } - } - return null; -}; - -d3_selectionPrototype.size = function() { - var n = 0; - this.each(function() { ++n; }); - return n; -}; - -function d3_selection_enter(selection) { - d3_subclass(selection, d3_selection_enterPrototype); - return selection; -} - -var d3_selection_enterPrototype = []; - -d3.selection.enter = d3_selection_enter; -d3.selection.enter.prototype = d3_selection_enterPrototype; - -d3_selection_enterPrototype.append = d3_selectionPrototype.append; -d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; -d3_selection_enterPrototype.node = d3_selectionPrototype.node; -d3_selection_enterPrototype.call = d3_selectionPrototype.call; -d3_selection_enterPrototype.size = d3_selectionPrototype.size; - - -d3_selection_enterPrototype.select = function(selector) { - var subgroups = [], - subgroup, - subnode, - upgroup, - group, - node; - - for (var j = -1, m = this.length; ++j < m;) { - upgroup = (group = this[j]).update; - subgroups.push(subgroup = []); - subgroup.parentNode = group.parentNode; - for (var i = -1, n = group.length; ++i < n;) { - if (node = group[i]) { - subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j)); - subnode.__data__ = node.__data__; - } else { - subgroup.push(null); - } - } - } - - return d3_selection(subgroups); -}; - -d3_selection_enterPrototype.insert = function(name, before) { - if (arguments.length < 2) before = d3_selection_enterInsertBefore(this); - return d3_selectionPrototype.insert.call(this, name, before); -}; - -function d3_selection_enterInsertBefore(enter) { - var i0, j0; - return function(d, i, j) { - var group = enter[j].update, - n = group.length, - node; - if (j != j0) j0 = j, i0 = 0; - if (i >= i0) i0 = i + 1; - while (!(node = group[i0]) && ++i0 < n); - return node; - }; -} - -d3_selectionPrototype.transition = function() { - var id = d3_transitionInheritId || ++d3_transitionId, - subgroups = [], - subgroup, - node, - transition = d3_transitionInherit || {time: Date.now(), ease: d3_ease_cubicInOut, delay: 0, duration: 250}; - - for (var j = -1, m = this.length; ++j < m;) { - subgroups.push(subgroup = []); - for (var group = this[j], i = -1, n = group.length; ++i < n;) { - if (node = group[i]) d3_transitionNode(node, i, id, transition); - subgroup.push(node); - } - } - - return d3_transition(subgroups, id); -}; - -// TODO fast singleton implementation? -d3.select = function(node) { - var group = [typeof node === "string" ? d3_select(node, d3_document) : node]; - group.parentNode = d3_documentElement; - return d3_selection([group]); -}; - -d3.selectAll = function(nodes) { - var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes); - group.parentNode = d3_documentElement; - return d3_selection([group]); -}; - -var d3_selectionRoot = d3.select(d3_documentElement); - -d3_selectionPrototype.on = function(type, listener, capture) { - var n = arguments.length; - if (n < 3) { - - // For on(object) or on(object, boolean), the object specifies the event - // types and listeners to add or remove. The optional boolean specifies - // whether the listener captures events. - if (typeof type !== "string") { - if (n < 2) listener = false; - for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); - return this; - } - - // For on(string), return the listener for the first node. - if (n < 2) return (n = this.node()["__on" + type]) && n._; - - // For on(string, function), use the default capture. - capture = false; - } - - // Otherwise, a type, listener and capture are specified, and handled as below. - return this.each(d3_selection_on(type, listener, capture)); -}; - -function d3_selection_on(type, listener, capture) { - var name = "__on" + type, - i = type.indexOf("."), - wrap = d3_selection_onListener; - - if (i > 0) type = type.substring(0, i); - var filter = d3_selection_onFilters.get(type); - if (filter) type = filter, wrap = d3_selection_onFilter; - - function onRemove() { - var l = this[name]; - if (l) { - this.removeEventListener(type, l, l.$); - delete this[name]; - } - } - - function onAdd() { - var l = wrap(listener, d3_array(arguments)); - if (typeof Raven !== 'undefined') l = Raven.wrap(l); - onRemove.call(this); - this.addEventListener(type, this[name] = l, l.$ = capture); - l._ = listener; - } - - function removeAll() { - var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), - match; - for (var name in this) { - if (match = name.match(re)) { - var l = this[name]; - this.removeEventListener(match[1], l, l.$); - delete this[name]; - } - } - } - - return i - ? listener ? onAdd : onRemove - : listener ? d3_noop : removeAll; -} - -var d3_selection_onFilters = d3.map({ - mouseenter: "mouseover", - mouseleave: "mouseout" -}); - -d3_selection_onFilters.forEach(function(k) { - if ("on" + k in d3_document) d3_selection_onFilters.remove(k); -}); - -function d3_selection_onListener(listener, argumentz) { - return function(e) { - var o = d3.event; // Events can be reentrant (e.g., focus). - d3.event = e; - argumentz[0] = this.__data__; - try { - listener.apply(this, argumentz); - } finally { - d3.event = o; - } - }; -} - -function d3_selection_onFilter(listener, argumentz) { - var l = d3_selection_onListener(listener, argumentz); - return function(e) { - var target = this, related = e.relatedTarget; - if (!related || (related !== target && !(related.compareDocumentPosition(target) & 8))) { - l.call(target, e); - } - }; -} - -var d3_event_dragSelect = d3_vendorSymbol(d3_documentElement.style, "userSelect"), - d3_event_dragId = 0; - -function d3_event_dragSuppress() { - var name = ".dragsuppress-" + ++d3_event_dragId, - touchmove = "touchmove" + name, - selectstart = "selectstart" + name, - dragstart = "dragstart" + name, - click = "click" + name, - w = d3.select(d3_window).on(touchmove, d3_eventPreventDefault).on(selectstart, d3_eventPreventDefault).on(dragstart, d3_eventPreventDefault), - style = d3_documentElement.style, - select = style[d3_event_dragSelect]; - style[d3_event_dragSelect] = "none"; - return function(suppressClick) { - w.on(name, null); - style[d3_event_dragSelect] = select; - if (suppressClick) { // suppress the next click, but only if it’s immediate - function off() { w.on(click, null); } - w.on(click, function() { d3_eventCancel(); off(); }, true); - setTimeout(off, 0); - } - }; -} - -d3.mouse = function(container) { - return d3_mousePoint(container, d3_eventSource()); -}; - -// https://bugs.webkit.org/show_bug.cgi?id=44083 -var d3_mouse_bug44083 = /WebKit/.test(d3_window.navigator.userAgent) ? -1 : 0; - -function d3_mousePoint(container, e) { - var svg = container.ownerSVGElement || container; - if (svg.createSVGPoint) { - var point = svg.createSVGPoint(); - if (d3_mouse_bug44083 < 0 && (d3_window.scrollX || d3_window.scrollY)) { - svg = d3.select("body").append("svg").style({ - position: "absolute", - top: 0, - left: 0, - margin: 0, - padding: 0, - border: "none" - }, "important"); - var ctm = svg[0][0].getScreenCTM(); - d3_mouse_bug44083 = !(ctm.f || ctm.e); - svg.remove(); - } - if (d3_mouse_bug44083) { - point.x = e.pageX; - point.y = e.pageY; - } else { - point.x = e.clientX; - point.y = e.clientY; - } - point = point.matrixTransform(container.getScreenCTM().inverse()); - return [point.x, point.y]; - } - var rect = container.getBoundingClientRect(); - return [e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop]; -}; - -d3.touches = function(container, touches) { - if (arguments.length < 2) touches = d3_eventSource().touches; - return touches ? d3_array(touches).map(function(touch) { - var point = d3_mousePoint(container, touch); - point.identifier = touch.identifier; - return point; - }) : []; -}; - -d3.behavior.zoom = function() { - var translate = [0, 0], - translate0, // translate when we started zooming (to avoid drift) - scale = 1, - scaleExtent = d3_behavior_zoomInfinity, - mousedown = "mousedown.zoom", - mousemove = "mousemove.zoom", - mouseup = "mouseup.zoom", - event = d3_eventDispatch(zoom, "zoom"), - x0, - x1, - y0, - y1, - touchtime; // time of last touchstart (to detect double-tap) - - function zoom() { - this.on(mousedown, mousedowned) - .on(d3_behavior_zoomWheel + ".zoom", mousewheeled) - .on(mousemove, mousewheelreset) - .on("dblclick.zoom", dblclicked) - .on("touchstart.zoom", touchstarted); - } - - zoom.translate = function(x) { - if (!arguments.length) return translate; - translate = x.map(Number); - rescale(); - return zoom; - }; - - zoom.scale = function(x) { - if (!arguments.length) return scale; - scale = +x; - rescale(); - return zoom; - }; - - zoom.scaleExtent = function(x) { - if (!arguments.length) return scaleExtent; - scaleExtent = x == null ? d3_behavior_zoomInfinity : x.map(Number); - return zoom; - }; - - zoom.x = function(z) { - if (!arguments.length) return x1; - x1 = z; - x0 = z.copy(); - translate = [0, 0]; - scale = 1; - return zoom; - }; - - zoom.y = function(z) { - if (!arguments.length) return y1; - y1 = z; - y0 = z.copy(); - translate = [0, 0]; - scale = 1; - return zoom; - }; - - function location(p) { - return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale]; - } - - function point(l) { - return [l[0] * scale + translate[0], l[1] * scale + translate[1]]; - } - - function scaleTo(s) { - scale = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); - } - - function translateTo(p, l) { - l = point(l); - translate[0] += p[0] - l[0]; - translate[1] += p[1] - l[1]; - } - - function rescale() { - if (x1) x1.domain(x0.range().map(function(x) { return (x - translate[0]) / scale; }).map(x0.invert)); - if (y1) y1.domain(y0.range().map(function(y) { return (y - translate[1]) / scale; }).map(y0.invert)); - } - - function dispatch(event) { - rescale(); - event({type: "zoom", scale: scale, translate: translate}); - } - - function mousedowned() { - var target = this, - event_ = event.of(target, arguments), - eventTarget = d3.event.target, - dragged = 0, - w = d3.select(d3_window).on(mousemove, moved).on(mouseup, ended), - l = location(d3.mouse(target)), - dragRestore = d3_event_dragSuppress(); - - function moved() { - dragged = 1; - translateTo(d3.mouse(target), l); - dispatch(event_); - } - - function ended() { - w.on(mousemove, d3_window === target ? mousewheelreset : null).on(mouseup, null); - dragRestore(dragged && d3.event.target === eventTarget); - } - } - - function touchstarted() { - var target = this, - event_ = event.of(target, arguments), - touches = d3.touches(target), - locations = {}, - distance0 = 0, // distance² between initial touches - scale0 = scale, // scale when we started touching - now = Date.now(), - name = "zoom-" + d3.event.changedTouches[0].identifier, - touchmove = "touchmove." + name, - touchend = "touchend." + name, - w = d3.select(d3_window).on(touchmove, moved).on(touchend, ended), - t = d3.select(target).on(mousedown, null), // prevent duplicate events - dragRestore = d3_event_dragSuppress(); - - touches.forEach(function(t) { locations[t.identifier] = location(t); }); - - if (touches.length === 1) { - if (now - touchtime < 500) { // dbltap - var p = touches[0], l = location(touches[0]); - scaleTo(scale * 2); - translateTo(p, l); - d3_eventPreventDefault(); - dispatch(event_); - } - touchtime = now; - } else if (touches.length > 1) { - var p = touches[0], q = touches[1], - dx = p[0] - q[0], dy = p[1] - q[1]; - distance0 = dx * dx + dy * dy; - } - - function moved() { - var touches = d3.touches(target), - p0 = touches[0], - l0 = locations[p0.identifier]; - - if (p1 = touches[1]) { - var p1, l1 = locations[p1.identifier], - scale1 = d3.event.scale; - if (scale1 == null) { - var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1; - scale1 = distance0 && Math.sqrt(distance1 / distance0); - } - p0 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2]; - l0 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2]; - scaleTo(scale1 * scale0); - } - - touchtime = null; - translateTo(p0, l0); - dispatch(event_); - } - - function ended() { - w.on(touchmove, null).on(touchend, null); - t.on(mousedown, mousedowned); - dragRestore(); - } - } - - function mousewheeled() { - d3_eventPreventDefault(); - if (!translate0) translate0 = location(d3.mouse(this)); - scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * scale); - translateTo(d3.mouse(this), translate0); - dispatch(event.of(this, arguments)); - } - - function mousewheelreset() { - translate0 = null; - } - - function dblclicked() { - var p = d3.mouse(this), l = location(p), k = Math.log(scale) / Math.LN2; - scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1)); - translateTo(p, l); - dispatch(event.of(this, arguments)); - } - - return d3.rebind(zoom, event, "on"); -}; - -var d3_behavior_zoomInfinity = [0, Infinity]; // default scale extent - -// https://developer.mozilla.org/en-US/docs/Mozilla_event_reference/wheel -var d3_behavior_zoomDelta, d3_behavior_zoomWheel - = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); }, "wheel") - : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { return d3.event.wheelDelta; }, "mousewheel") - : (d3_behavior_zoomDelta = function() { return -d3.event.detail; }, "MozMousePixelScroll"); -function d3_functor(v) { - return typeof v === "function" ? v : function() { return v; }; -} - -d3.functor = d3_functor; - -var d3_timer_queueHead, - d3_timer_queueTail, - d3_timer_interval, // is an interval (or frame) active? - d3_timer_timeout, // is a timeout active? - d3_timer_active, // active timer object - d3_timer_frame = d3_window[d3_vendorSymbol(d3_window, "requestAnimationFrame")] || function(callback) { setTimeout(callback, 17); }; - -// The timer will continue to fire until callback returns true. -d3.timer = function(callback, delay, then) { - var n = arguments.length; - if (n < 2) delay = 0; - if (n < 3) then = Date.now(); - - // Add the callback to the tail of the queue. - var time = then + delay, timer = {callback: callback, time: time, next: null}; - if (d3_timer_queueTail) d3_timer_queueTail.next = timer; - else d3_timer_queueHead = timer; - d3_timer_queueTail = timer; - - // Start animatin'! - if (!d3_timer_interval) { - d3_timer_timeout = clearTimeout(d3_timer_timeout); - d3_timer_interval = 1; - d3_timer_frame(d3_timer_step); - } -}; - -function d3_timer_step() { - var now = d3_timer_mark(), - delay = d3_timer_sweep() - now; - if (delay > 24) { - if (isFinite(delay)) { - clearTimeout(d3_timer_timeout); - d3_timer_timeout = setTimeout(d3_timer_step, delay); - } - d3_timer_interval = 0; - } else { - d3_timer_interval = 1; - d3_timer_frame(d3_timer_step); - } -} - -d3.timer.flush = function() { - d3_timer_mark(); - d3_timer_sweep(); -}; - -function d3_timer_replace(callback, delay, then) { - var n = arguments.length; - if (n < 2) delay = 0; - if (n < 3) then = Date.now(); - d3_timer_active.callback = callback; - d3_timer_active.time = then + delay; -} - -function d3_timer_mark() { - var now = Date.now(); - d3_timer_active = d3_timer_queueHead; - while (d3_timer_active) { - if (now >= d3_timer_active.time) d3_timer_active.flush = d3_timer_active.callback(now - d3_timer_active.time); - d3_timer_active = d3_timer_active.next; - } - return now; -} - -// Flush after callbacks to avoid concurrent queue modification. -// Returns the time of the earliest active timer, post-sweep. -function d3_timer_sweep() { - var t0, - t1 = d3_timer_queueHead, - time = Infinity; - while (t1) { - if (t1.flush) { - t1 = t0 ? t0.next = t1.next : d3_timer_queueHead = t1.next; - } else { - if (t1.time < time) time = t1.time; - t1 = (t0 = t1).next; - } - } - d3_timer_queueTail = t0; - return time; -} -var π = Math.PI, - ε = 1e-6, - ε2 = ε * ε, - d3_radians = π / 180, - d3_degrees = 180 / π; - -function d3_sgn(x) { - return x > 0 ? 1 : x < 0 ? -1 : 0; -} - -function d3_acos(x) { - return x > 1 ? 0 : x < -1 ? π : Math.acos(x); -} - -function d3_asin(x) { - return x > 1 ? π / 2 : x < -1 ? -π / 2 : Math.asin(x); -} - -function d3_sinh(x) { - return (Math.exp(x) - Math.exp(-x)) / 2; -} - -function d3_cosh(x) { - return (Math.exp(x) + Math.exp(-x)) / 2; -} - -function d3_haversin(x) { - return (x = Math.sin(x / 2)) * x; -} -d3.geo = {}; -function d3_identity(d) { - return d; -} -function d3_true() { - return true; -} - -function d3_geo_spherical(cartesian) { - return [ - Math.atan2(cartesian[1], cartesian[0]), - d3_asin(cartesian[2]) - ]; -} - -function d3_geo_sphericalEqual(a, b) { - return Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε; -} - -// General spherical polygon clipping algorithm: takes a polygon, cuts it into -// visible line segments and rejoins the segments by interpolating along the -// clip edge. -function d3_geo_clipPolygon(segments, compare, inside, interpolate, listener) { - var subject = [], - clip = []; - - segments.forEach(function(segment) { - if ((n = segment.length - 1) <= 0) return; - var n, p0 = segment[0], p1 = segment[n]; - - // If the first and last points of a segment are coincident, then treat as - // a closed ring. - // TODO if all rings are closed, then the winding order of the exterior - // ring should be checked. - if (d3_geo_sphericalEqual(p0, p1)) { - listener.lineStart(); - for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]); - listener.lineEnd(); - return; - } - - var a = {point: p0, points: segment, other: null, visited: false, entry: true, subject: true}, - b = {point: p0, points: [p0], other: a, visited: false, entry: false, subject: false}; - a.other = b; - subject.push(a); - clip.push(b); - a = {point: p1, points: [p1], other: null, visited: false, entry: false, subject: true}; - b = {point: p1, points: [p1], other: a, visited: false, entry: true, subject: false}; - a.other = b; - subject.push(a); - clip.push(b); - }); - clip.sort(compare); - d3_geo_clipPolygonLinkCircular(subject); - d3_geo_clipPolygonLinkCircular(clip); - if (!subject.length) return; - - if (inside) for (var i = 1, e = !inside(clip[0].point), n = clip.length; i < n; ++i) { - clip[i].entry = (e = !e); - } - - var start = subject[0], - current, - points, - point; - while (1) { - // Find first unvisited intersection. - current = start; - while (current.visited) if ((current = current.next) === start) return; - points = current.points; - listener.lineStart(); - do { - current.visited = current.other.visited = true; - if (current.entry) { - if (current.subject) { - for (var i = 0; i < points.length; i++) listener.point((point = points[i])[0], point[1]); - } else { - interpolate(current.point, current.next.point, 1, listener); - } - current = current.next; - } else { - if (current.subject) { - points = current.prev.points; - for (var i = points.length; --i >= 0;) listener.point((point = points[i])[0], point[1]); - } else { - interpolate(current.point, current.prev.point, -1, listener); - } - current = current.prev; - } - current = current.other; - points = current.points; - } while (!current.visited); - listener.lineEnd(); - } -} - -function d3_geo_clipPolygonLinkCircular(array) { - if (!(n = array.length)) return; - var n, - i = 0, - a = array[0], - b; - while (++i < n) { - a.next = b = array[i]; - b.prev = a; - a = b; - } - a.next = b = array[0]; - b.prev = a; -} - -function d3_geo_clip(pointVisible, clipLine, interpolate, polygonContains) { - return function(listener) { - var line = clipLine(listener); - - var clip = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - clip.point = pointRing; - clip.lineStart = ringStart; - clip.lineEnd = ringEnd; - segments = []; - polygon = []; - listener.polygonStart(); - }, - polygonEnd: function() { - clip.point = point; - clip.lineStart = lineStart; - clip.lineEnd = lineEnd; - - segments = d3.merge(segments); - if (segments.length) { - d3_geo_clipPolygon(segments, d3_geo_clipSort, null, interpolate, listener); - } else if (polygonContains(polygon)) { - listener.lineStart(); - interpolate(null, null, 1, listener); - listener.lineEnd(); - } - listener.polygonEnd(); - segments = polygon = null; - }, - sphere: function() { - listener.polygonStart(); - listener.lineStart(); - interpolate(null, null, 1, listener); - listener.lineEnd(); - listener.polygonEnd(); - } - }; - - function point(λ, φ) { if (pointVisible(λ, φ)) listener.point(λ, φ); } - function pointLine(λ, φ) { line.point(λ, φ); } - function lineStart() { clip.point = pointLine; line.lineStart(); } - function lineEnd() { clip.point = point; line.lineEnd(); } - - var segments; - - var buffer = d3_geo_clipBufferListener(), - ringListener = clipLine(buffer), - polygon, - ring; - - function pointRing(λ, φ) { - ringListener.point(λ, φ); - ring.push([λ, φ]); - } - - function ringStart() { - ringListener.lineStart(); - ring = []; - } - - function ringEnd() { - pointRing(ring[0][0], ring[0][1]); - ringListener.lineEnd(); - - var clean = ringListener.clean(), - ringSegments = buffer.buffer(), - segment, - n = ringSegments.length; - - ring.pop(); - polygon.push(ring); - ring = null; - - if (!n) return; - - // No intersections. - if (clean & 1) { - segment = ringSegments[0]; - var n = segment.length - 1, - i = -1, - point; - listener.lineStart(); - while (++i < n) listener.point((point = segment[i])[0], point[1]); - listener.lineEnd(); - return; - } - - // Rejoin connected segments. - // TODO reuse bufferListener.rejoin()? - if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); - - segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); - } - - return clip; - }; -} - -function d3_geo_clipSegmentLength1(segment) { - return segment.length > 1; -} - -function d3_geo_clipBufferListener() { - var lines = [], - line; - return { - lineStart: function() { lines.push(line = []); }, - point: function(λ, φ) { line.push([λ, φ]); }, - lineEnd: d3_noop, - buffer: function() { - var buffer = lines; - lines = []; - line = null; - return buffer; - }, - rejoin: function() { - if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); - } - }; -} - -// Intersection points are sorted along the clip edge. For both antimeridian -// cutting and circle clipping, the same comparison is used. -function d3_geo_clipSort(a, b) { - return ((a = a.point)[0] < 0 ? a[1] - π / 2 - ε : π / 2 - a[1]) - - ((b = b.point)[0] < 0 ? b[1] - π / 2 - ε : π / 2 - b[1]); -} -// Adds floating point numbers with twice the normal precision. -// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and -// Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3) -// 305–363 (1997). -// Code adapted from GeographicLib by Charles F. F. Karney, -// http://geographiclib.sourceforge.net/ -// See lib/geographiclib/LICENSE for details. - -function d3_adder() {} - -d3_adder.prototype = { - s: 0, // rounded value - t: 0, // exact error - add: function(y) { - d3_adderSum(y, this.t, d3_adderTemp); - d3_adderSum(d3_adderTemp.s, this.s, this); - if (this.s) this.t += d3_adderTemp.t; - else this.s = d3_adderTemp.t; - }, - reset: function() { - this.s = this.t = 0; - }, - valueOf: function() { - return this.s; - } -}; - -var d3_adderTemp = new d3_adder; - -function d3_adderSum(a, b, o) { - var x = o.s = a + b, // a + b - bv = x - a, av = x - bv; // b_virtual & a_virtual - o.t = (a - av) + (b - bv); // a_roundoff + b_roundoff -} - -d3.geo.stream = function(object, listener) { - if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) { - d3_geo_streamObjectType[object.type](object, listener); - } else { - d3_geo_streamGeometry(object, listener); - } -}; - -function d3_geo_streamGeometry(geometry, listener) { - if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { - d3_geo_streamGeometryType[geometry.type](geometry, listener); - } -} - -var d3_geo_streamObjectType = { - Feature: function(feature, listener) { - d3_geo_streamGeometry(feature.geometry, listener); - }, - FeatureCollection: function(object, listener) { - var features = object.features, i = -1, n = features.length; - while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); - } -}; - -var d3_geo_streamGeometryType = { - Sphere: function(object, listener) { - listener.sphere(); - }, - Point: function(object, listener) { - var coordinate = object.coordinates; - listener.point(coordinate[0], coordinate[1]); - }, - MultiPoint: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length, coordinate; - while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1]); - }, - LineString: function(object, listener) { - d3_geo_streamLine(object.coordinates, listener, 0); - }, - MultiLineString: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); - }, - Polygon: function(object, listener) { - d3_geo_streamPolygon(object.coordinates, listener); - }, - MultiPolygon: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); - }, - GeometryCollection: function(object, listener) { - var geometries = object.geometries, i = -1, n = geometries.length; - while (++i < n) d3_geo_streamGeometry(geometries[i], listener); - } -}; - -function d3_geo_streamLine(coordinates, listener, closed) { - var i = -1, n = coordinates.length - closed, coordinate; - listener.lineStart(); - while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1]); - listener.lineEnd(); -} - -function d3_geo_streamPolygon(coordinates, listener) { - var i = -1, n = coordinates.length; - listener.polygonStart(); - while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); - listener.polygonEnd(); -} - -d3.geo.area = function(object) { - d3_geo_areaSum = 0; - d3.geo.stream(object, d3_geo_area); - return d3_geo_areaSum; -}; - -var d3_geo_areaSum, - d3_geo_areaRingSum = new d3_adder; - -var d3_geo_area = { - sphere: function() { d3_geo_areaSum += 4 * π; }, - point: d3_noop, - lineStart: d3_noop, - lineEnd: d3_noop, - - // Only count area for polygon rings. - polygonStart: function() { - d3_geo_areaRingSum.reset(); - d3_geo_area.lineStart = d3_geo_areaRingStart; - }, - polygonEnd: function() { - var area = 2 * d3_geo_areaRingSum; - d3_geo_areaSum += area < 0 ? 4 * π + area : area; - d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; - } -}; - -function d3_geo_areaRingStart() { - var λ00, φ00, λ0, cosφ0, sinφ0; // start point and previous point - - // For the first point, … - d3_geo_area.point = function(λ, φ) { - d3_geo_area.point = nextPoint; - λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), sinφ0 = Math.sin(φ); - }; - - // For subsequent points, … - function nextPoint(λ, φ) { - λ *= d3_radians; - φ = φ * d3_radians / 2 + π / 4; // half the angular distance from south pole - - // Spherical excess E for a spherical triangle with vertices: south pole, - // previous point, current point. Uses a formula derived from Cagnoli’s - // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2). - var dλ = λ - λ0, - cosφ = Math.cos(φ), - sinφ = Math.sin(φ), - k = sinφ0 * sinφ, - u = cosφ0 * cosφ + k * Math.cos(dλ), - v = k * Math.sin(dλ); - d3_geo_areaRingSum.add(Math.atan2(v, u)); - - // Advance the previous points. - λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; - } - - // For the last point, return to the start. - d3_geo_area.lineEnd = function() { - nextPoint(λ00, φ00); - }; -} -// TODO -// cross and scale return new vectors, -// whereas add and normalize operate in-place - -function d3_geo_cartesian(spherical) { - var λ = spherical[0], - φ = spherical[1], - cosφ = Math.cos(φ); - return [ - cosφ * Math.cos(λ), - cosφ * Math.sin(λ), - Math.sin(φ) - ]; -} - -function d3_geo_cartesianDot(a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; -} - -function d3_geo_cartesianCross(a, b) { - return [ - a[1] * b[2] - a[2] * b[1], - a[2] * b[0] - a[0] * b[2], - a[0] * b[1] - a[1] * b[0] - ]; -} - -function d3_geo_cartesianAdd(a, b) { - a[0] += b[0]; - a[1] += b[1]; - a[2] += b[2]; -} - -function d3_geo_cartesianScale(vector, k) { - return [ - vector[0] * k, - vector[1] * k, - vector[2] * k - ]; -} - -function d3_geo_cartesianNormalize(d) { - var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); - d[0] /= l; - d[1] /= l; - d[2] /= l; -} - -function d3_geo_pointInPolygon(point, polygon) { - var meridian = point[0], - parallel = point[1], - meridianNormal = [Math.sin(meridian), -Math.cos(meridian), 0], - polarAngle = 0, - polar = false, - southPole = false, - winding = 0; - d3_geo_areaRingSum.reset(); - - for (var i = 0, n = polygon.length; i < n; ++i) { - var ring = polygon[i], - m = ring.length; - if (!m) continue; - var point0 = ring[0], - λ0 = point0[0], - φ0 = point0[1] / 2 + π / 4, - sinφ0 = Math.sin(φ0), - cosφ0 = Math.cos(φ0), - j = 1; - - while (true) { - if (j === m) j = 0; - point = ring[j]; - var λ = point[0], - φ = point[1] / 2 + π / 4, - sinφ = Math.sin(φ), - cosφ = Math.cos(φ), - dλ = λ - λ0, - antimeridian = Math.abs(dλ) > π, - k = sinφ0 * sinφ; - d3_geo_areaRingSum.add(Math.atan2(k * Math.sin(dλ), cosφ0 * cosφ + k * Math.cos(dλ))); - - if (Math.abs(φ) < ε) southPole = true; - polarAngle += antimeridian ? dλ + (dλ >= 0 ? 2 : -2) * π : dλ; - - // Are the longitudes either side of the point's meridian, and are the - // latitudes smaller than the parallel? - if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { - var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point)); - d3_geo_cartesianNormalize(arc); - var intersection = d3_geo_cartesianCross(meridianNormal, arc); - d3_geo_cartesianNormalize(intersection); - var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]); - if (parallel > φarc) { - winding += antimeridian ^ dλ >= 0 ? 1 : -1; - } - } - if (!j++) break; - λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; - } - if (Math.abs(polarAngle) > ε) polar = true; - } - - // First, determine whether the South pole is inside or outside: - // - // It is inside if: - // * the polygon doesn't wind around it, and its area is negative (counter-clockwise). - // * otherwise, if the polygon winds around it in a clockwise direction. - // - // Second, count the (signed) number of times a segment crosses a meridian - // from the point to the South pole. If it is zero, then the point is the - // same side as the South pole. - - return (!southPole && !polar && d3_geo_areaRingSum < 0 || polarAngle < -ε) ^ (winding & 1); -} - -var d3_geo_clipAntimeridian = d3_geo_clip( - d3_true, - d3_geo_clipAntimeridianLine, - d3_geo_clipAntimeridianInterpolate, - d3_geo_clipAntimeridianPolygonContains); - -// Takes a line and cuts into visible segments. Return values: -// 0: there were intersections or the line was empty. -// 1: no intersections. -// 2: there were intersections, and the first and last segments should be -// rejoined. -function d3_geo_clipAntimeridianLine(listener) { - var λ0 = NaN, - φ0 = NaN, - sλ0 = NaN, - clean; // no intersections - - return { - lineStart: function() { - listener.lineStart(); - clean = 1; - }, - point: function(λ1, φ1) { - var sλ1 = λ1 > 0 ? π : -π, - dλ = Math.abs(λ1 - λ0); - if (Math.abs(dλ - π) < ε) { // line crosses a pole - listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? π / 2 : -π / 2); - listener.point(sλ0, φ0); - listener.lineEnd(); - listener.lineStart(); - listener.point(sλ1, φ0); - listener.point( λ1, φ0); - clean = 0; - } else if (sλ0 !== sλ1 && dλ >= π) { // line crosses antimeridian - // handle degeneracies - if (Math.abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; - if (Math.abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; - φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); - listener.point(sλ0, φ0); - listener.lineEnd(); - listener.lineStart(); - listener.point(sλ1, φ0); - clean = 0; - } - listener.point(λ0 = λ1, φ0 = φ1); - sλ0 = sλ1; - }, - lineEnd: function() { - listener.lineEnd(); - λ0 = φ0 = NaN; - }, - // if there are intersections, we always rejoin the first and last segments. - clean: function() { return 2 - clean; } - }; -} - -function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { - var cosφ0, - cosφ1, - sinλ0_λ1 = Math.sin(λ0 - λ1); - return Math.abs(sinλ0_λ1) > ε - ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) - / (cosφ0 * cosφ1 * sinλ0_λ1)) - : (φ0 + φ1) / 2; -} - -function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { - var φ; - if (from == null) { - φ = direction * π / 2; - listener.point(-π, φ); - listener.point( 0, φ); - listener.point( π, φ); - listener.point( π, 0); - listener.point( π, -φ); - listener.point( 0, -φ); - listener.point(-π, -φ); - listener.point(-π, 0); - listener.point(-π, φ); - } else if (Math.abs(from[0] - to[0]) > ε) { - var s = (from[0] < to[0] ? 1 : -1) * π; - φ = direction * s / 2; - listener.point(-s, φ); - listener.point( 0, φ); - listener.point( s, φ); - } else { - listener.point(to[0], to[1]); - } -} - -var d3_geo_clipAntimeridianPoint = [-π, 0]; - -function d3_geo_clipAntimeridianPolygonContains(polygon) { - return d3_geo_pointInPolygon(d3_geo_clipAntimeridianPoint, polygon); -} - -function d3_geo_equirectangular(λ, φ) { - return [λ, φ]; -} - -(d3.geo.equirectangular = function() { - return d3_geo_projection(d3_geo_equirectangular); -}).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular; - -d3.geo.rotation = function(rotate) { - rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0); - - function forward(coordinates) { - coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians); - return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; - } - - forward.invert = function(coordinates) { - coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians); - return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; - }; - - return forward; -}; - -// Note: |δλ| must be < 2π -function d3_geo_rotation(δλ, δφ, δγ) { - return δλ ? (δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) - : d3_geo_rotationλ(δλ)) - : (δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) - : d3_geo_equirectangular); -} - -function d3_geo_forwardRotationλ(δλ) { - return function(λ, φ) { - return λ += δλ, [λ > π ? λ - 2 * π : λ < -π ? λ + 2 * π : λ, φ]; - }; -} - -function d3_geo_rotationλ(δλ) { - var rotation = d3_geo_forwardRotationλ(δλ); - rotation.invert = d3_geo_forwardRotationλ(-δλ); - return rotation; -} - -function d3_geo_rotationφγ(δφ, δγ) { - var cosδφ = Math.cos(δφ), - sinδφ = Math.sin(δφ), - cosδγ = Math.cos(δγ), - sinδγ = Math.sin(δγ); - - function rotation(λ, φ) { - var cosφ = Math.cos(φ), - x = Math.cos(λ) * cosφ, - y = Math.sin(λ) * cosφ, - z = Math.sin(φ), - k = z * cosδφ + x * sinδφ; - return [ - Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), - d3_asin(k * cosδγ + y * sinδγ) - ]; - } - - rotation.invert = function(λ, φ) { - var cosφ = Math.cos(φ), - x = Math.cos(λ) * cosφ, - y = Math.sin(λ) * cosφ, - z = Math.sin(φ), - k = z * cosδγ - y * sinδγ; - return [ - Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), - d3_asin(k * cosδφ - x * sinδφ) - ]; - }; - - return rotation; -} - -d3.geo.circle = function() { - var origin = [0, 0], - angle, - precision = 6, - interpolate; - - function circle() { - var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, - rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, - ring = []; - - interpolate(null, null, 1, { - point: function(x, y) { - ring.push(x = rotate(x, y)); - x[0] *= d3_degrees, x[1] *= d3_degrees; - } - }); - - return {type: "Polygon", coordinates: [ring]}; - } - - circle.origin = function(x) { - if (!arguments.length) return origin; - origin = x; - return circle; - }; - - circle.angle = function(x) { - if (!arguments.length) return angle; - interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians); - return circle; - }; - - circle.precision = function(_) { - if (!arguments.length) return precision; - interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians); - return circle; - }; - - return circle.angle(90); -}; - -// Interpolates along a circle centered at [0°, 0°], with a given radius and -// precision. -function d3_geo_circleInterpolate(radius, precision) { - var cr = Math.cos(radius), - sr = Math.sin(radius); - return function(from, to, direction, listener) { - if (from != null) { - from = d3_geo_circleAngle(cr, from); - to = d3_geo_circleAngle(cr, to); - if (direction > 0 ? from < to: from > to) from += direction * 2 * π; - } else { - from = radius + direction * 2 * π; - to = radius; - } - var point; - for (var step = direction * precision, t = from; direction > 0 ? t > to : t < to; t -= step) { - listener.point((point = d3_geo_spherical([ - cr, - -sr * Math.cos(t), - -sr * Math.sin(t) - ]))[0], point[1]); - } - }; -} - -// Signed angle of a cartesian point relative to [cr, 0, 0]. -function d3_geo_circleAngle(cr, point) { - var a = d3_geo_cartesian(point); - a[0] -= cr; - d3_geo_cartesianNormalize(a); - var angle = d3_acos(-a[1]); - return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); -} - -// Clip features against a small circle centered at [0°, 0°]. -function d3_geo_clipCircle(radius) { - var cr = Math.cos(radius), - smallRadius = cr > 0, - point = [radius, 0], - notHemisphere = Math.abs(cr) > ε, // TODO optimise for this common case - interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians); - - return d3_geo_clip(visible, clipLine, interpolate, polygonContains); - - function visible(λ, φ) { - return Math.cos(λ) * Math.cos(φ) > cr; - } - - // Takes a line and cuts into visible segments. Return values used for - // polygon clipping: - // 0: there were intersections or the line was empty. - // 1: no intersections. - // 2: there were intersections, and the first and last segments should be - // rejoined. - function clipLine(listener) { - var point0, // previous point - c0, // code for previous point - v0, // visibility of previous point - v00, // visibility of first point - clean; // no intersections - return { - lineStart: function() { - v00 = v0 = false; - clean = 1; - }, - point: function(λ, φ) { - var point1 = [λ, φ], - point2, - v = visible(λ, φ), - c = smallRadius - ? v ? 0 : code(λ, φ) - : v ? code(λ + (λ < 0 ? π : -π), φ) : 0; - if (!point0 && (v00 = v0 = v)) listener.lineStart(); - // Handle degeneracies. - // TODO ignore if not clipping polygons. - if (v !== v0) { - point2 = intersect(point0, point1); - if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) { - point1[0] += ε; - point1[1] += ε; - v = visible(point1[0], point1[1]); - } - } - if (v !== v0) { - clean = 0; - if (v) { - // outside going in - listener.lineStart(); - point2 = intersect(point1, point0); - listener.point(point2[0], point2[1]); - } else { - // inside going out - point2 = intersect(point0, point1); - listener.point(point2[0], point2[1]); - listener.lineEnd(); - } - point0 = point2; - } else if (notHemisphere && point0 && smallRadius ^ v) { - var t; - // If the codes for two points are different, or are both zero, - // and there this segment intersects with the small circle. - if (!(c & c0) && (t = intersect(point1, point0, true))) { - clean = 0; - if (smallRadius) { - listener.lineStart(); - listener.point(t[0][0], t[0][1]); - listener.point(t[1][0], t[1][1]); - listener.lineEnd(); - } else { - listener.point(t[1][0], t[1][1]); - listener.lineEnd(); - listener.lineStart(); - listener.point(t[0][0], t[0][1]); - } - } - } - if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) { - listener.point(point1[0], point1[1]); - } - point0 = point1, v0 = v, c0 = c; - }, - lineEnd: function() { - if (v0) listener.lineEnd(); - point0 = null; - }, - // Rejoin first and last segments if there were intersections and the first - // and last points were visible. - clean: function() { return clean | ((v00 && v0) << 1); } - }; - } - - // Intersects the great circle between a and b with the clip circle. - function intersect(a, b, two) { - var pa = d3_geo_cartesian(a), - pb = d3_geo_cartesian(b); - - // We have two planes, n1.p = d1 and n2.p = d2. - // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2). - var n1 = [1, 0, 0], // normal - n2 = d3_geo_cartesianCross(pa, pb), - n2n2 = d3_geo_cartesianDot(n2, n2), - n1n2 = n2[0], // d3_geo_cartesianDot(n1, n2), - determinant = n2n2 - n1n2 * n1n2; - - // Two polar points. - if (!determinant) return !two && a; - - var c1 = cr * n2n2 / determinant, - c2 = -cr * n1n2 / determinant, - n1xn2 = d3_geo_cartesianCross(n1, n2), - A = d3_geo_cartesianScale(n1, c1), - B = d3_geo_cartesianScale(n2, c2); - d3_geo_cartesianAdd(A, B); - - // Solve |p(t)|^2 = 1. - var u = n1xn2, - w = d3_geo_cartesianDot(A, u), - uu = d3_geo_cartesianDot(u, u), - t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1); - - if (t2 < 0) return; - - var t = Math.sqrt(t2), - q = d3_geo_cartesianScale(u, (-w - t) / uu); - d3_geo_cartesianAdd(q, A); - q = d3_geo_spherical(q); - if (!two) return q; - - // Two intersection points. - var λ0 = a[0], - λ1 = b[0], - φ0 = a[1], - φ1 = b[1], - z; - if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z; - var δλ = λ1 - λ0, - polar = Math.abs(δλ - π) < ε, - meridian = polar || δλ < ε; - - if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z; - - // Check that the first point is between a and b. - if (meridian - ? polar - ? φ0 + φ1 > 0 ^ q[1] < (Math.abs(q[0] - λ0) < ε ? φ0 : φ1) - : φ0 <= q[1] && q[1] <= φ1 - : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) { - var q1 = d3_geo_cartesianScale(u, (-w + t) / uu); - d3_geo_cartesianAdd(q1, A); - return [q, d3_geo_spherical(q1)]; - } - } - - // Generates a 4-bit vector representing the location of a point relative to - // the small circle's bounding box. - function code(λ, φ) { - var r = smallRadius ? radius : π - radius, - code = 0; - if (λ < -r) code |= 1; // left - else if (λ > r) code |= 2; // right - if (φ < -r) code |= 4; // below - else if (φ > r) code |= 8; // above - return code; - } - - function polygonContains(polygon) { - return d3_geo_pointInPolygon(point, polygon); - } -} - -var d3_geo_clipViewMAX = 1e9; - -function d3_geo_clipView(x0, y0, x1, y1) { - return function(listener) { - var listener_ = listener, - bufferListener = d3_geo_clipBufferListener(), - segments, - polygon, - ring; - - var clip = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - listener = bufferListener; - segments = []; - polygon = []; - }, - polygonEnd: function() { - listener = listener_; - if ((segments = d3.merge(segments)).length) { - listener.polygonStart(); - d3_geo_clipPolygon(segments, compare, inside, interpolate, listener); - listener.polygonEnd(); - } else if (insidePolygon([x0, y0])) { - listener.polygonStart(), listener.lineStart(); - interpolate(null, null, 1, listener); - listener.lineEnd(), listener.polygonEnd(); - } - segments = polygon = ring = null; - } - }; - - function inside(point) { - var a = corner(point, -1), - i = insidePolygon([a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0]); - return i; - } - - function insidePolygon(p) { - var wn = 0, // the winding number counter - n = polygon.length, - y = p[1]; - - for (var i = 0; i < n; ++i) { - for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) { - b = v[j]; - if (a[1] <= y) { - if (b[1] > y && isLeft(a, b, p) > 0) ++wn; - } else { - if (b[1] <= y && isLeft(a, b, p) < 0) --wn; - } - a = b; - } - } - return wn !== 0; - } - - function isLeft(a, b, c) { - return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]); - } - - function interpolate(from, to, direction, listener) { - var a = 0, a1 = 0; - if (from == null || - (a = corner(from, direction)) !== (a1 = corner(to, direction)) || - comparePoints(from, to) < 0 ^ direction > 0) { - do { - listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); - } while ((a = (a + direction + 4) % 4) !== a1); - } else { - listener.point(to[0], to[1]); - } - } - - function visible(x, y) { - return x0 <= x && x <= x1 && y0 <= y && y <= y1; - } - - function point(x, y) { - if (visible(x, y)) listener.point(x, y); - } - - var x__, y__, v__, // first point - x_, y_, v_, // previous point - first; - - function lineStart() { - clip.point = linePoint; - if (polygon) polygon.push(ring = []); - first = true; - v_ = false; - x_ = y_ = NaN; - } - - function lineEnd() { - // TODO rather than special-case polygons, simply handle them separately. - // Ideally, coincident intersection points should be jittered to avoid - // clipping issues. - if (segments) { - linePoint(x__, y__); - if (v__ && v_) bufferListener.rejoin(); - segments.push(bufferListener.buffer()); - } - clip.point = point; - if (v_) listener.lineEnd(); - } - - function linePoint(x, y) { - x = Math.max(-d3_geo_clipViewMAX, Math.min(d3_geo_clipViewMAX, x)); - y = Math.max(-d3_geo_clipViewMAX, Math.min(d3_geo_clipViewMAX, y)); - var v = visible(x, y); - if (polygon) ring.push([x, y]); - if (first) { - x__ = x, y__ = y, v__ = v; - first = false; - if (v) { - listener.lineStart(); - listener.point(x, y); - } - } else { - if (v && v_) listener.point(x, y); - else { - var a = [x_, y_], - b = [x, y]; - if (clipLine(a, b)) { - if (!v_) { - listener.lineStart(); - listener.point(a[0], a[1]); - } - listener.point(b[0], b[1]); - if (!v) listener.lineEnd(); - } else if (v) { - listener.lineStart(); - listener.point(x, y); - } - } - } - x_ = x, y_ = y, v_ = v; - } - - return clip; - }; - - function corner(p, direction) { - return Math.abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 - : Math.abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 - : Math.abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 - : direction > 0 ? 3 : 2; // Math.abs(p[1] - y1) < ε - } - - function compare(a, b) { - return comparePoints(a.point, b.point); - } - - function comparePoints(a, b) { - var ca = corner(a, 1), - cb = corner(b, 1); - return ca !== cb ? ca - cb - : ca === 0 ? b[1] - a[1] - : ca === 1 ? a[0] - b[0] - : ca === 2 ? a[1] - b[1] - : b[0] - a[0]; - } - - // Liang–Barsky line clipping. - function clipLine(a, b) { - var dx = b[0] - a[0], - dy = b[1] - a[1], - t = [0, 1]; - - if (Math.abs(dx) < ε && Math.abs(dy) < ε) return x0 <= a[0] && a[0] <= x1 && y0 <= a[1] && a[1] <= y1; - - if (d3_geo_clipViewT(x0 - a[0], dx, t) && - d3_geo_clipViewT(a[0] - x1, -dx, t) && - d3_geo_clipViewT(y0 - a[1], dy, t) && - d3_geo_clipViewT(a[1] - y1, -dy, t)) { - if (t[1] < 1) { - b[0] = a[0] + t[1] * dx; - b[1] = a[1] + t[1] * dy; - } - if (t[0] > 0) { - a[0] += t[0] * dx; - a[1] += t[0] * dy; - } - return true; - } - - return false; - } -} - -function d3_geo_clipViewT(num, denominator, t) { - if (Math.abs(denominator) < ε) return num <= 0; - - var u = num / denominator; - - if (denominator > 0) { - if (u > t[1]) return false; - if (u > t[0]) t[0] = u; - } else { - if (u < t[0]) return false; - if (u < t[1]) t[1] = u; - } - return true; -} -function d3_geo_compose(a, b) { - - function compose(x, y) { - return x = a(x, y), b(x[0], x[1]); - } - - if (a.invert && b.invert) compose.invert = function(x, y) { - return x = b.invert(x, y), x && a.invert(x[0], x[1]); - }; - - return compose; -} - -function d3_geo_conic(projectAt) { - var φ0 = 0, - φ1 = π / 3, - m = d3_geo_projectionMutator(projectAt), - p = m(φ0, φ1); - - p.parallels = function(_) { - if (!arguments.length) return [φ0 / π * 180, φ1 / π * 180]; - return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180); - }; - - return p; -} - -function d3_geo_conicEqualArea(φ0, φ1) { - var sinφ0 = Math.sin(φ0), - n = (sinφ0 + Math.sin(φ1)) / 2, - C = 1 + sinφ0 * (2 * n - sinφ0), - ρ0 = Math.sqrt(C) / n; - - function forward(λ, φ) { - var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; - return [ - ρ * Math.sin(λ *= n), - ρ0 - ρ * Math.cos(λ) - ]; - } - - forward.invert = function(x, y) { - var ρ0_y = ρ0 - y; - return [ - Math.atan2(x, ρ0_y) / n, - d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) - ]; - }; - - return forward; -} - -(d3.geo.conicEqualArea = function() { - return d3_geo_conic(d3_geo_conicEqualArea); -}).raw = d3_geo_conicEqualArea; - -// ESRI:102003 -d3.geo.albers = function() { - return d3.geo.conicEqualArea() - .rotate([96, 0]) - .center([-.6, 38.7]) - .parallels([29.5, 45.5]) - .scale(1070); -}; - -// A composite projection for the United States, configured by default for -// 960×500. Also works quite well at 960×600 with scale 1285. The set of -// standard parallels for each region comes from USGS, which is published here: -// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers -d3.geo.albersUsa = function() { - var lower48 = d3.geo.albers(); - - // EPSG:3338 - var alaska = d3.geo.conicEqualArea() - .rotate([154, 0]) - .center([-2, 58.5]) - .parallels([55, 65]); - - // ESRI:102007 - var hawaii = d3.geo.conicEqualArea() - .rotate([157, 0]) - .center([-3, 19.9]) - .parallels([8, 18]); - - var point, - pointStream = {point: function(x, y) { point = [x, y]; }}, - lower48Point, - alaskaPoint, - hawaiiPoint; - - function albersUsa(coordinates) { - var x = coordinates[0], y = coordinates[1]; - point = null; - (lower48Point(x, y), point) - || (alaskaPoint(x, y), point) - || hawaiiPoint(x, y); - return point; - } - - albersUsa.invert = function(coordinates) { - var k = lower48.scale(), - t = lower48.translate(), - x = (coordinates[0] - t[0]) / k, - y = (coordinates[1] - t[1]) / k; - return (y >= .120 && y < .234 && x >= -.425 && x < -.214 ? alaska - : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii - : lower48).invert(coordinates); - }; - - // A naïve multi-projection stream. - // The projections must have mutually exclusive clip regions on the sphere, - // as this will avoid emitting interleaving lines and polygons. - albersUsa.stream = function(stream) { - var lower48Stream = lower48.stream(stream), - alaskaStream = alaska.stream(stream), - hawaiiStream = hawaii.stream(stream); - return { - point: function(x, y) { - lower48Stream.point(x, y); - alaskaStream.point(x, y); - hawaiiStream.point(x, y); - }, - sphere: function() { - lower48Stream.sphere(); - alaskaStream.sphere(); - hawaiiStream.sphere(); - }, - lineStart: function() { - lower48Stream.lineStart(); - alaskaStream.lineStart(); - hawaiiStream.lineStart(); - }, - lineEnd: function() { - lower48Stream.lineEnd(); - alaskaStream.lineEnd(); - hawaiiStream.lineEnd(); - }, - polygonStart: function() { - lower48Stream.polygonStart(); - alaskaStream.polygonStart(); - hawaiiStream.polygonStart(); - }, - polygonEnd: function() { - lower48Stream.polygonEnd(); - alaskaStream.polygonEnd(); - hawaiiStream.polygonEnd(); - } - }; - }; - - albersUsa.precision = function(_) { - if (!arguments.length) return lower48.precision(); - lower48.precision(_); - alaska.precision(_); - hawaii.precision(_); - return albersUsa; - }; - - albersUsa.scale = function(_) { - if (!arguments.length) return lower48.scale(); - lower48.scale(_); - alaska.scale(_ * .35); - hawaii.scale(_); - return albersUsa.translate(lower48.translate()); - }; - - albersUsa.translate = function(_) { - if (!arguments.length) return lower48.translate(); - var k = lower48.scale(), x = +_[0], y = +_[1]; - - lower48Point = lower48 - .translate(_) - .clipExtent([[x - .455 * k, y - .238 * k], [x + .455 * k, y + .238 * k]]) - .stream(pointStream).point; - - alaskaPoint = alaska - .translate([x - .307 * k, y + .201 * k]) - .clipExtent([[x - .425 * k + ε, y + .120 * k + ε], [x - .214 * k - ε, y + .234 * k - ε]]) - .stream(pointStream).point; - - hawaiiPoint = hawaii - .translate([x - .205 * k, y + .212 * k]) - .clipExtent([[x - .214 * k + ε, y + .166 * k + ε], [x - .115 * k - ε, y + .234 * k - ε]]) - .stream(pointStream).point; - - return albersUsa; - }; - - return albersUsa.scale(1070); -}; - -d3.geo.bounds = (function() { - var λ0, φ0, λ1, φ1, // bounds - λ_, // previous λ-coordinate - λ__, φ__, // first point - p0, // previous 3D point - dλSum, - ranges, - range; - - var bound = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - - polygonStart: function() { - bound.point = ringPoint; - bound.lineStart = ringStart; - bound.lineEnd = ringEnd; - dλSum = 0; - d3_geo_area.polygonStart(); - }, - polygonEnd: function() { - d3_geo_area.polygonEnd(); - bound.point = point; - bound.lineStart = lineStart; - bound.lineEnd = lineEnd; - if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); - else if (dλSum > ε) φ1 = 90; - else if (dλSum < -ε) φ0 = -90; - range[0] = λ0, range[1] = λ1; - } - }; - - function point(λ, φ) { - ranges.push(range = [λ0 = λ, λ1 = λ]); - if (φ < φ0) φ0 = φ; - if (φ > φ1) φ1 = φ; - } - - function linePoint(λ, φ) { - var p = d3_geo_cartesian([λ * d3_radians, φ * d3_radians]); - if (p0) { - var normal = d3_geo_cartesianCross(p0, p), - equatorial = [normal[1], -normal[0], 0], - inflection = d3_geo_cartesianCross(equatorial, normal); - d3_geo_cartesianNormalize(inflection); - inflection = d3_geo_spherical(inflection); - var dλ = λ - λ_, - s = dλ > 0 ? 1 : -1, - λi = inflection[0] * d3_degrees * s, - antimeridian = Math.abs(dλ) > 180; - if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) { - var φi = inflection[1] * d3_degrees; - if (φi > φ1) φ1 = φi; - } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) { - var φi = -inflection[1] * d3_degrees; - if (φi < φ0) φ0 = φi; - } else { - if (φ < φ0) φ0 = φ; - if (φ > φ1) φ1 = φ; - } - if (antimeridian) { - if (λ < λ_) { - if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; - } else { - if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; - } - } else { - if (λ1 >= λ0) { - if (λ < λ0) λ0 = λ; - if (λ > λ1) λ1 = λ; - } else { - if (λ > λ_) { - if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; - } else { - if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; - } - } - } - } else { - point(λ, φ); - } - p0 = p, λ_ = λ; - } - - function lineStart() { bound.point = linePoint; } - function lineEnd() { - range[0] = λ0, range[1] = λ1; - bound.point = point; - p0 = null; - } - - function ringPoint(λ, φ) { - if (p0) { - var dλ = λ - λ_; - dλSum += Math.abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ; - } else λ__ = λ, φ__ = φ; - d3_geo_area.point(λ, φ); - linePoint(λ, φ); - } - - function ringStart() { - d3_geo_area.lineStart(); - } - - function ringEnd() { - ringPoint(λ__, φ__); - d3_geo_area.lineEnd(); - if (Math.abs(dλSum) > ε) λ0 = -(λ1 = 180); - range[0] = λ0, range[1] = λ1; - p0 = null; - } - - // Finds the left-right distance between two longitudes. - // This is almost the same as (λ1 - λ0 + 360°) % 360°, except that we want - // the distance between ±180° to be 360°. - function angle(λ0, λ1) { return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; } - - function compareRanges(a, b) { return a[0] - b[0]; } - - function withinRange(x, range) { - return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; - } - - return function(feature) { - φ1 = λ1 = -(λ0 = φ0 = Infinity); - ranges = []; - - d3.geo.stream(feature, bound); - - var n = ranges.length; - if (n) { - // First, sort ranges by their minimum longitudes. - ranges.sort(compareRanges); - - // Then, merge any ranges that overlap. - for (var i = 1, a = ranges[0], b, merged = [a]; i < n; ++i) { - b = ranges[i]; - if (withinRange(b[0], a) || withinRange(b[1], a)) { - if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; - if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; - } else { - merged.push(a = b); - } - } - - // Finally, find the largest gap between the merged ranges. - // The final bounding box will be the inverse of this gap. - var best = -Infinity, dλ; - for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) { - b = merged[i]; - if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1]; - } - } - ranges = range = null; - - return λ0 === Infinity || φ0 === Infinity - ? [[NaN, NaN], [NaN, NaN]] - : [[λ0, φ0], [λ1, φ1]]; - }; -})(); - -d3.geo.centroid = function(object) { - d3_geo_centroidW0 = d3_geo_centroidW1 = - d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = - d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = - d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; - d3.geo.stream(object, d3_geo_centroid); - - var x = d3_geo_centroidX2, - y = d3_geo_centroidY2, - z = d3_geo_centroidZ2, - m = x * x + y * y + z * z; - - // If the area-weighted centroid is undefined, fall back to length-weighted centroid. - if (m < ε2) { - x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1; - // If the feature has zero length, fall back to arithmetic mean of point vectors. - if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0; - m = x * x + y * y + z * z; - // If the feature still has an undefined centroid, then return. - if (m < ε2) return [NaN, NaN]; - } - - return [Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees]; -}; - -var d3_geo_centroidW0, - d3_geo_centroidW1, - d3_geo_centroidX0, - d3_geo_centroidY0, - d3_geo_centroidZ0, - d3_geo_centroidX1, - d3_geo_centroidY1, - d3_geo_centroidZ1, - d3_geo_centroidX2, - d3_geo_centroidY2, - d3_geo_centroidZ2; - -var d3_geo_centroid = { - sphere: d3_noop, - point: d3_geo_centroidPoint, - lineStart: d3_geo_centroidLineStart, - lineEnd: d3_geo_centroidLineEnd, - polygonStart: function() { - d3_geo_centroid.lineStart = d3_geo_centroidRingStart; - }, - polygonEnd: function() { - d3_geo_centroid.lineStart = d3_geo_centroidLineStart; - } -}; - -// Arithmetic mean of Cartesian vectors. -function d3_geo_centroidPoint(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)); -} - -function d3_geo_centroidPointXYZ(x, y, z) { - ++d3_geo_centroidW0; - d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0; - d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0; - d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0; -} - -function d3_geo_centroidLineStart() { - var x0, y0, z0; // previous point - - d3_geo_centroid.point = function(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - x0 = cosφ * Math.cos(λ); - y0 = cosφ * Math.sin(λ); - z0 = Math.sin(φ); - d3_geo_centroid.point = nextPoint; - d3_geo_centroidPointXYZ(x0, y0, z0); - }; - - function nextPoint(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians), - x = cosφ * Math.cos(λ), - y = cosφ * Math.sin(λ), - z = Math.sin(φ), - w = Math.atan2( - Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), - x0 * x + y0 * y + z0 * z); - d3_geo_centroidW1 += w; - d3_geo_centroidX1 += w * (x0 + (x0 = x)); - d3_geo_centroidY1 += w * (y0 + (y0 = y)); - d3_geo_centroidZ1 += w * (z0 + (z0 = z)); - d3_geo_centroidPointXYZ(x0, y0, z0); - } -} - -function d3_geo_centroidLineEnd() { - d3_geo_centroid.point = d3_geo_centroidPoint; -} - -// See J. E. Brock, The Inertia Tensor for a Spherical Triangle, -// J. Applied Mechanics 42, 239 (1975). -function d3_geo_centroidRingStart() { - var λ00, φ00, // first point - x0, y0, z0; // previous point - - d3_geo_centroid.point = function(λ, φ) { - λ00 = λ, φ00 = φ; - d3_geo_centroid.point = nextPoint; - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - x0 = cosφ * Math.cos(λ); - y0 = cosφ * Math.sin(λ); - z0 = Math.sin(φ); - d3_geo_centroidPointXYZ(x0, y0, z0); - }; - - d3_geo_centroid.lineEnd = function() { - nextPoint(λ00, φ00); - d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; - d3_geo_centroid.point = d3_geo_centroidPoint; - }; - - function nextPoint(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians), - x = cosφ * Math.cos(λ), - y = cosφ * Math.sin(λ), - z = Math.sin(φ), - cx = y0 * z - z0 * y, - cy = z0 * x - x0 * z, - cz = x0 * y - y0 * x, - m = Math.sqrt(cx * cx + cy * cy + cz * cz), - u = x0 * x + y0 * y + z0 * z, - v = m && -d3_acos(u) / m, // area weight - w = Math.atan2(m, u); // line weight - d3_geo_centroidX2 += v * cx; - d3_geo_centroidY2 += v * cy; - d3_geo_centroidZ2 += v * cz; - d3_geo_centroidW1 += w; - d3_geo_centroidX1 += w * (x0 + (x0 = x)); - d3_geo_centroidY1 += w * (y0 + (y0 = y)); - d3_geo_centroidZ1 += w * (z0 + (z0 = z)); - d3_geo_centroidPointXYZ(x0, y0, z0); - } -} - -// TODO Unify this code with d3.geom.polygon area? - -var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { - point: d3_noop, - lineStart: d3_noop, - lineEnd: d3_noop, - - // Only count area for polygon rings. - polygonStart: function() { - d3_geo_pathAreaPolygon = 0; - d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; - }, - polygonEnd: function() { - d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; - d3_geo_pathAreaSum += Math.abs(d3_geo_pathAreaPolygon / 2); - } -}; - -function d3_geo_pathAreaRingStart() { - var x00, y00, x0, y0; - - // For the first point, … - d3_geo_pathArea.point = function(x, y) { - d3_geo_pathArea.point = nextPoint; - x00 = x0 = x, y00 = y0 = y; - }; - - // For subsequent points, … - function nextPoint(x, y) { - d3_geo_pathAreaPolygon += y0 * x - x0 * y; - x0 = x, y0 = y; - } - - // For the last point, return to the start. - d3_geo_pathArea.lineEnd = function() { - nextPoint(x00, y00); - }; -} - -var d3_geo_pathBoundsX0, - d3_geo_pathBoundsY0, - d3_geo_pathBoundsX1, - d3_geo_pathBoundsY1; - -var d3_geo_pathBounds = { - point: d3_geo_pathBoundsPoint, - lineStart: d3_noop, - lineEnd: d3_noop, - polygonStart: d3_noop, - polygonEnd: d3_noop -}; - -function d3_geo_pathBoundsPoint(x, y) { - if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x; - if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x; - if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y; - if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y; -} -function d3_geo_pathBuffer() { - var pointCircle = d3_geo_pathBufferCircle(4.5), - buffer = []; - - var stream = { - point: point, - - // While inside a line, override point to moveTo then lineTo. - lineStart: function() { stream.point = pointLineStart; }, - lineEnd: lineEnd, - - // While inside a polygon, override lineEnd to closePath. - polygonStart: function() { stream.lineEnd = lineEndPolygon; }, - polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; }, - - pointRadius: function(_) { - pointCircle = d3_geo_pathBufferCircle(_); - return stream; - }, - - result: function() { - if (buffer.length) { - var result = buffer.join(""); - buffer = []; - return result; - } - } - }; - - function point(x, y) { - buffer.push("M", x, ",", y, pointCircle); - } - - function pointLineStart(x, y) { - buffer.push("M", x, ",", y); - stream.point = pointLine; - } - - function pointLine(x, y) { - buffer.push("L", x, ",", y); - } - - function lineEnd() { - stream.point = point; - } - - function lineEndPolygon() { - buffer.push("Z"); - } - - return stream; -} - -function d3_geo_pathBufferCircle(radius) { - return "m0," + radius - + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius - + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius - + "z"; -} - -// TODO Unify this code with d3.geom.polygon centroid? -// TODO Enforce positive area for exterior, negative area for interior? - -var d3_geo_pathCentroid = { - point: d3_geo_pathCentroidPoint, - - // For lines, weight by length. - lineStart: d3_geo_pathCentroidLineStart, - lineEnd: d3_geo_pathCentroidLineEnd, - - // For polygons, weight by area. - polygonStart: function() { - d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; - }, - polygonEnd: function() { - d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; - d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; - d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; - } -}; - -function d3_geo_pathCentroidPoint(x, y) { - d3_geo_centroidX0 += x; - d3_geo_centroidY0 += y; - ++d3_geo_centroidZ0; -} - -function d3_geo_pathCentroidLineStart() { - var x0, y0; - - d3_geo_pathCentroid.point = function(x, y) { - d3_geo_pathCentroid.point = nextPoint; - d3_geo_pathCentroidPoint(x0 = x, y0 = y); - }; - - function nextPoint(x, y) { - var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); - d3_geo_centroidX1 += z * (x0 + x) / 2; - d3_geo_centroidY1 += z * (y0 + y) / 2; - d3_geo_centroidZ1 += z; - d3_geo_pathCentroidPoint(x0 = x, y0 = y); - } -} - -function d3_geo_pathCentroidLineEnd() { - d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; -} - -function d3_geo_pathCentroidRingStart() { - var x00, y00, x0, y0; - - // For the first point, … - d3_geo_pathCentroid.point = function(x, y) { - d3_geo_pathCentroid.point = nextPoint; - d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y); - }; - - // For subsequent points, … - function nextPoint(x, y) { - var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); - d3_geo_centroidX1 += z * (x0 + x) / 2; - d3_geo_centroidY1 += z * (y0 + y) / 2; - d3_geo_centroidZ1 += z; - - z = y0 * x - x0 * y; - d3_geo_centroidX2 += z * (x0 + x); - d3_geo_centroidY2 += z * (y0 + y); - d3_geo_centroidZ2 += z * 3; - d3_geo_pathCentroidPoint(x0 = x, y0 = y); - } - - // For the last point, return to the start. - d3_geo_pathCentroid.lineEnd = function() { - nextPoint(x00, y00); - }; -} - -function d3_geo_pathContext(context) { - var pointRadius = 4.5; - - var stream = { - point: point, - - // While inside a line, override point to moveTo then lineTo. - lineStart: function() { stream.point = pointLineStart; }, - lineEnd: lineEnd, - - // While inside a polygon, override lineEnd to closePath. - polygonStart: function() { stream.lineEnd = lineEndPolygon; }, - polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; }, - - pointRadius: function(_) { - pointRadius = _; - return stream; - }, - - result: d3_noop - }; - - function point(x, y) { - context.moveTo(x, y); - context.arc(x, y, pointRadius, 0, 2 * π); - } - - function pointLineStart(x, y) { - context.moveTo(x, y); - stream.point = pointLine; - } - - function pointLine(x, y) { - context.lineTo(x, y); - } - - function lineEnd() { - stream.point = point; - } - - function lineEndPolygon() { - context.closePath(); - } - - return stream; -} - -function d3_geo_resample(project) { - var δ2 = .5, // precision, px² - cosMinDistance = Math.cos(30 * d3_radians), // cos(minimum angular distance) - maxDepth = 16; - - function resample(stream) { - var λ00, φ00, x00, y00, a00, b00, c00, // first point - λ0, x0, y0, a0, b0, c0; // previous point - - var resample = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { stream.polygonStart(); resample.lineStart = ringStart; }, - polygonEnd: function() { stream.polygonEnd(); resample.lineStart = lineStart; } - }; - - function point(x, y) { - x = project(x, y); - stream.point(x[0], x[1]); - } - - function lineStart() { - x0 = NaN; - resample.point = linePoint; - stream.lineStart(); - } - - function linePoint(λ, φ) { - var c = d3_geo_cartesian([λ, φ]), p = project(λ, φ); - resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); - stream.point(x0, y0); - } - - function lineEnd() { - resample.point = point; - stream.lineEnd(); - } - - function ringStart() { - lineStart(); - resample.point = ringPoint; - resample.lineEnd = ringEnd; - } - - function ringPoint(λ, φ) { - linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; - resample.point = linePoint; - } - - function ringEnd() { - resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); - resample.lineEnd = lineEnd; - lineEnd(); - } - - return resample; - } - - function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { - var dx = x1 - x0, - dy = y1 - y0, - d2 = dx * dx + dy * dy; - if (d2 > 4 * δ2 && depth--) { - var a = a0 + a1, - b = b0 + b1, - c = c0 + c1, - m = Math.sqrt(a * a + b * b + c * c), - φ2 = Math.asin(c /= m), - λ2 = Math.abs(Math.abs(c) - 1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), - p = project(λ2, φ2), - x2 = p[0], - y2 = p[1], - dx2 = x2 - x0, - dy2 = y2 - y0, - dz = dy * dx2 - dx * dy2; - if (dz * dz / d2 > δ2 // perpendicular projected distance - || Math.abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 // midpoint close to an end - || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance - resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); - stream.point(x2, y2); - resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); - } - } - } - - resample.precision = function(_) { - if (!arguments.length) return Math.sqrt(δ2); - maxDepth = (δ2 = _ * _) > 0 && 16; - return resample; - }; - - return resample; -} - -d3.geo.path = function() { - var pointRadius = 4.5, - projection, - context, - projectStream, - contextStream, - cacheStream; - - function path(object) { - if (object) { - if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); - if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream); - d3.geo.stream(object, cacheStream); - } - return contextStream.result(); - } - - path.area = function(object) { - d3_geo_pathAreaSum = 0; - d3.geo.stream(object, projectStream(d3_geo_pathArea)); - return d3_geo_pathAreaSum; - }; - - path.centroid = function(object) { - d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = - d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = - d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; - d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); - return d3_geo_centroidZ2 ? [d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2] - : d3_geo_centroidZ1 ? [d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1] - : d3_geo_centroidZ0 ? [d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0] - : [NaN, NaN]; - }; - - path.bounds = function(object) { - d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity); - d3.geo.stream(object, projectStream(d3_geo_pathBounds)); - return [[d3_geo_pathBoundsX0, d3_geo_pathBoundsY0], [d3_geo_pathBoundsX1, d3_geo_pathBoundsY1]]; - }; - - path.projection = function(_) { - if (!arguments.length) return projection; - projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; - return reset(); - }; - - path.context = function(_) { - if (!arguments.length) return context; - contextStream = (context = _) == null ? new d3_geo_pathBuffer : new d3_geo_pathContext(_); - if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); - return reset(); - }; - - path.pointRadius = function(_) { - if (!arguments.length) return pointRadius; - pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); - return path; - }; - - function reset() { - cacheStream = null; - return path; - } - - return path.projection(d3.geo.albersUsa()).context(null); -}; - -function d3_geo_pathProjectStream(project) { - var resample = d3_geo_resample(function(λ, φ) { return project([λ * d3_degrees, φ * d3_degrees]); }); - return function(stream) { - stream = resample(stream); - return { - point: function(λ, φ) { stream.point(λ * d3_radians, φ * d3_radians); }, - sphere: function() { stream.sphere(); }, - lineStart: function() { stream.lineStart(); }, - lineEnd: function() { stream.lineEnd(); }, - polygonStart: function() { stream.polygonStart(); }, - polygonEnd: function() { stream.polygonEnd(); } - }; - }; -} - -d3.geo.projection = d3_geo_projection; -d3.geo.projectionMutator = d3_geo_projectionMutator; - -function d3_geo_projection(project) { - return d3_geo_projectionMutator(function() { return project; })(); -} - -function d3_geo_projectionMutator(projectAt) { - var project, - rotate, - projectRotate, - projectResample = d3_geo_resample(function(x, y) { x = project(x, y); return [x[0] * k + δx, δy - x[1] * k]; }), - k = 150, // scale - x = 480, y = 250, // translate - λ = 0, φ = 0, // center - δλ = 0, δφ = 0, δγ = 0, // rotate - δx, δy, // center - preclip = d3_geo_clipAntimeridian, - postclip = d3_identity, - clipAngle = null, - clipExtent = null, - stream; - - function projection(point) { - point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); - return [point[0] * k + δx, δy - point[1] * k]; - } - - function invert(point) { - point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); - return point && [point[0] * d3_degrees, point[1] * d3_degrees]; - } - - projection.stream = function(output) { - if (stream) stream.valid = false; - stream = d3_geo_projectionRadiansRotate(rotate, preclip(projectResample(postclip(output)))); - stream.valid = true; // allow caching by d3.geo.path - return stream; - }; - - projection.clipAngle = function(_) { - if (!arguments.length) return clipAngle; - preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians); - return invalidate(); - }; - - projection.clipExtent = function(_) { - if (!arguments.length) return clipExtent; - clipExtent = _; - postclip = _ == null ? d3_identity : d3_geo_clipView(_[0][0], _[0][1], _[1][0], _[1][1]); - return invalidate(); - }; - - projection.scale = function(_) { - if (!arguments.length) return k; - k = +_; - return reset(); - }; - - projection.translate = function(_) { - if (!arguments.length) return [x, y]; - x = +_[0]; - y = +_[1]; - return reset(); - }; - - projection.center = function(_) { - if (!arguments.length) return [λ * d3_degrees, φ * d3_degrees]; - λ = _[0] % 360 * d3_radians; - φ = _[1] % 360 * d3_radians; - return reset(); - }; - - projection.rotate = function(_) { - if (!arguments.length) return [δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees]; - δλ = _[0] % 360 * d3_radians; - δφ = _[1] % 360 * d3_radians; - δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; - return reset(); - }; - - d3.rebind(projection, projectResample, "precision"); - - function reset() { - projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); - var center = project(λ, φ); - δx = x - center[0] * k; - δy = y + center[1] * k; - return invalidate(); - } - - function invalidate() { - if (stream) { - stream.valid = false; - stream = null; - } - return projection; - } - - return function() { - project = projectAt.apply(this, arguments); - projection.invert = project.invert && invert; - return reset(); - }; -} - -function d3_geo_projectionRadiansRotate(rotate, stream) { - return { - point: function(x, y) { - y = rotate(x * d3_radians, y * d3_radians), x = y[0]; - stream.point(x > π ? x - 2 * π : x < -π ? x + 2 * π : x, y[1]); - }, - sphere: function() { stream.sphere(); }, - lineStart: function() { stream.lineStart(); }, - lineEnd: function() { stream.lineEnd(); }, - polygonStart: function() { stream.polygonStart(); }, - polygonEnd: function() { stream.polygonEnd(); } - }; -} - -function d3_geo_mercator(λ, φ) { - return [λ, Math.log(Math.tan(π / 4 + φ / 2))]; -} - -d3_geo_mercator.invert = function(x, y) { - return [x, 2 * Math.atan(Math.exp(y)) - π / 2]; -}; - -function d3_geo_mercatorProjection(project) { - var m = d3_geo_projection(project), - scale = m.scale, - translate = m.translate, - clipExtent = m.clipExtent, - clipAuto; - - m.scale = function() { - var v = scale.apply(m, arguments); - return v === m ? (clipAuto ? m.clipExtent(null) : m) : v; - }; - - m.translate = function() { - var v = translate.apply(m, arguments); - return v === m ? (clipAuto ? m.clipExtent(null) : m) : v; - }; - - m.clipExtent = function(_) { - var v = clipExtent.apply(m, arguments); - if (v === m) { - if (clipAuto = _ == null) { - var k = π * scale(), t = translate(); - clipExtent([[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]]); - } - } else if (clipAuto) { - v = null; - } - return v; - }; - - return m.clipExtent(null); -} - -(d3.geo.mercator = function() { - return d3_geo_mercatorProjection(d3_geo_mercator); -}).raw = d3_geo_mercator; -d3.geom = {}; - -d3.geom.polygon = function(coordinates) { - d3_subclass(coordinates, d3_geom_polygonPrototype); - return coordinates; -}; - -var d3_geom_polygonPrototype = d3.geom.polygon.prototype = []; - -d3_geom_polygonPrototype.area = function() { - var i = -1, - n = this.length, - a, - b = this[n - 1], - area = 0; - - while (++i < n) { - a = b; - b = this[i]; - area += a[1] * b[0] - a[0] * b[1]; - } - - return area * .5; -}; - -d3_geom_polygonPrototype.centroid = function(k) { - var i = -1, - n = this.length, - x = 0, - y = 0, - a, - b = this[n - 1], - c; - - if (!arguments.length) k = -1 / (6 * this.area()); - - while (++i < n) { - a = b; - b = this[i]; - c = a[0] * b[1] - b[0] * a[1]; - x += (a[0] + b[0]) * c; - y += (a[1] + b[1]) * c; - } - - return [x * k, y * k]; -}; - -// The Sutherland-Hodgman clipping algorithm. -// Note: requires the clip polygon to be counterclockwise and convex. -d3_geom_polygonPrototype.clip = function(subject) { - var input, - closed = d3_geom_polygonClosed(subject), - i = -1, - n = this.length - d3_geom_polygonClosed(this), - j, - m, - a = this[n - 1], - b, - c, - d; - - while (++i < n) { - input = subject.slice(); - subject.length = 0; - b = this[i]; - c = input[(m = input.length - closed) - 1]; - j = -1; - while (++j < m) { - d = input[j]; - if (d3_geom_polygonInside(d, a, b)) { - if (!d3_geom_polygonInside(c, a, b)) { - subject.push(d3_geom_polygonIntersect(c, d, a, b)); - } - subject.push(d); - } else if (d3_geom_polygonInside(c, a, b)) { - subject.push(d3_geom_polygonIntersect(c, d, a, b)); - } - c = d; - } - if (closed) subject.push(subject[0]); - a = b; - } - - return subject; -}; - -function d3_geom_polygonInside(p, a, b) { - return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); -} - -// Intersect two infinite lines cd and ab. -function d3_geom_polygonIntersect(c, d, a, b) { - var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, - y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, - ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); - return [x1 + ua * x21, y1 + ua * y21]; -} - -// Returns true if the polygon is closed. -function d3_geom_polygonClosed(coordinates) { - var a = coordinates[0], - b = coordinates[coordinates.length - 1]; - return !(a[0] - b[0] || a[1] - b[1]); -} - -var d3_ease_default = function() { return d3_identity; }; - -var d3_ease = d3.map({ - linear: d3_ease_default, - poly: d3_ease_poly, - quad: function() { return d3_ease_quad; }, - cubic: function() { return d3_ease_cubic; }, - sin: function() { return d3_ease_sin; }, - exp: function() { return d3_ease_exp; }, - circle: function() { return d3_ease_circle; }, - elastic: d3_ease_elastic, - back: d3_ease_back, - bounce: function() { return d3_ease_bounce; } -}); - -var d3_ease_mode = d3.map({ - "in": d3_identity, - "out": d3_ease_reverse, - "in-out": d3_ease_reflect, - "out-in": function(f) { return d3_ease_reflect(d3_ease_reverse(f)); } -}); - -d3.ease = function(name) { - var i = name.indexOf("-"), - t = i >= 0 ? name.substring(0, i) : name, - m = i >= 0 ? name.substring(i + 1) : "in"; - t = d3_ease.get(t) || d3_ease_default; - m = d3_ease_mode.get(m) || d3_identity; - return d3_ease_clamp(m(t.apply(null, Array.prototype.slice.call(arguments, 1)))); -}; - -function d3_ease_clamp(f) { - return function(t) { - return t <= 0 ? 0 : t >= 1 ? 1 : f(t); - }; -} - -function d3_ease_reverse(f) { - return function(t) { - return 1 - f(1 - t); - }; -} - -function d3_ease_reflect(f) { - return function(t) { - return .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t))); - }; -} - -function d3_ease_quad(t) { - return t * t; -} - -function d3_ease_cubic(t) { - return t * t * t; -} - -// Optimized clamp(reflect(poly(3))). -function d3_ease_cubicInOut(t) { - if (t <= 0) return 0; - if (t >= 1) return 1; - var t2 = t * t, t3 = t2 * t; - return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); -} - -function d3_ease_poly(e) { - return function(t) { - return Math.pow(t, e); - }; -} - -function d3_ease_sin(t) { - return 1 - Math.cos(t * π / 2); -} - -function d3_ease_exp(t) { - return Math.pow(2, 10 * (t - 1)); -} - -function d3_ease_circle(t) { - return 1 - Math.sqrt(1 - t * t); -} - -function d3_ease_elastic(a, p) { - var s; - if (arguments.length < 2) p = 0.45; - if (arguments.length) s = p / (2 * π) * Math.asin(1 / a); - else a = 1, s = p / 4; - return function(t) { - return 1 + a * Math.pow(2, 10 * -t) * Math.sin((t - s) * 2 * π / p); - }; -} - -function d3_ease_back(s) { - if (!s) s = 1.70158; - return function(t) { - return t * t * ((s + 1) * t - s); - }; -} - -function d3_ease_bounce(t) { - return t < 1 / 2.75 ? 7.5625 * t * t - : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 - : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 - : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; -} - -function d3_transition(groups, id) { - d3_subclass(groups, d3_transitionPrototype); - - groups.id = id; // Note: read-only! - - return groups; -} - -var d3_transitionPrototype = [], - d3_transitionId = 0, - d3_transitionInheritId, - d3_transitionInherit; - -d3_transitionPrototype.call = d3_selectionPrototype.call; -d3_transitionPrototype.empty = d3_selectionPrototype.empty; -d3_transitionPrototype.node = d3_selectionPrototype.node; -d3_transitionPrototype.size = d3_selectionPrototype.size; - -d3.transition = function(selection) { - return arguments.length - ? (d3_transitionInheritId ? selection.transition() : selection) - : d3_selectionRoot.transition(); -}; - -d3.transition.prototype = d3_transitionPrototype; - - -d3_transitionPrototype.select = function(selector) { - var id = this.id, - subgroups = [], - subgroup, - subnode, - node; - - selector = d3_selection_selector(selector); - - for (var j = -1, m = this.length; ++j < m;) { - subgroups.push(subgroup = []); - for (var group = this[j], i = -1, n = group.length; ++i < n;) { - if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) { - if ("__data__" in node) subnode.__data__ = node.__data__; - d3_transitionNode(subnode, i, id, node.__transition__[id]); - subgroup.push(subnode); - } else { - subgroup.push(null); - } - } - } - - return d3_transition(subgroups, id); -}; - -d3_transitionPrototype.selectAll = function(selector) { - var id = this.id, - subgroups = [], - subgroup, - subnodes, - node, - subnode, - transition; - - selector = d3_selection_selectorAll(selector); - - for (var j = -1, m = this.length; ++j < m;) { - for (var group = this[j], i = -1, n = group.length; ++i < n;) { - if (node = group[i]) { - transition = node.__transition__[id]; - subnodes = selector.call(node, node.__data__, i, j); - subgroups.push(subgroup = []); - for (var k = -1, o = subnodes.length; ++k < o;) { - if (subnode = subnodes[k]) d3_transitionNode(subnode, k, id, transition); - subgroup.push(subnode); - } - } - } - } - - return d3_transition(subgroups, id); -}; - -d3_transitionPrototype.filter = function(filter) { - var subgroups = [], - subgroup, - group, - node; - - if (typeof filter !== "function") filter = d3_selection_filter(filter); - - for (var j = 0, m = this.length; j < m; j++) { - subgroups.push(subgroup = []); - for (var group = this[j], i = 0, n = group.length; i < n; i++) { - if ((node = group[i]) && filter.call(node, node.__data__, i)) { - subgroup.push(node); - } - } - } - - return d3_transition(subgroups, this.id); -}; -function d3_Color() {} - -d3_Color.prototype.toString = function() { - return this.rgb() + ""; -}; - -d3.hsl = function(h, s, l) { - return arguments.length === 1 - ? (h instanceof d3_Hsl ? d3_hsl(h.h, h.s, h.l) - : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl)) - : d3_hsl(+h, +s, +l); -}; - -function d3_hsl(h, s, l) { - return new d3_Hsl(h, s, l); -} - -function d3_Hsl(h, s, l) { - this.h = h; - this.s = s; - this.l = l; -} - -var d3_hslPrototype = d3_Hsl.prototype = new d3_Color; - -d3_hslPrototype.brighter = function(k) { - k = Math.pow(0.7, arguments.length ? k : 1); - return d3_hsl(this.h, this.s, this.l / k); -}; - -d3_hslPrototype.darker = function(k) { - k = Math.pow(0.7, arguments.length ? k : 1); - return d3_hsl(this.h, this.s, k * this.l); -}; - -d3_hslPrototype.rgb = function() { - return d3_hsl_rgb(this.h, this.s, this.l); -}; - -function d3_hsl_rgb(h, s, l) { - var m1, - m2; - - /* Some simple corrections for h, s and l. */ - h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h; - s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s; - l = l < 0 ? 0 : l > 1 ? 1 : l; - - /* From FvD 13.37, CSS Color Module Level 3 */ - m2 = l <= .5 ? l * (1 + s) : l + s - l * s; - m1 = 2 * l - m2; - - function v(h) { - if (h > 360) h -= 360; - else if (h < 0) h += 360; - if (h < 60) return m1 + (m2 - m1) * h / 60; - if (h < 180) return m2; - if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; - return m1; - } - - function vv(h) { - return Math.round(v(h) * 255); - } - - return d3_rgb(vv(h + 120), vv(h), vv(h - 120)); -} - -d3.hcl = function(h, c, l) { - return arguments.length === 1 - ? (h instanceof d3_Hcl ? d3_hcl(h.h, h.c, h.l) - : (h instanceof d3_Lab ? d3_lab_hcl(h.l, h.a, h.b) - : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b))) - : d3_hcl(+h, +c, +l); -}; - -function d3_hcl(h, c, l) { - return new d3_Hcl(h, c, l); -} - -function d3_Hcl(h, c, l) { - this.h = h; - this.c = c; - this.l = l; -} - -var d3_hclPrototype = d3_Hcl.prototype = new d3_Color; - -d3_hclPrototype.brighter = function(k) { - return d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); -}; - -d3_hclPrototype.darker = function(k) { - return d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); -}; - -d3_hclPrototype.rgb = function() { - return d3_hcl_lab(this.h, this.c, this.l).rgb(); -}; - -function d3_hcl_lab(h, c, l) { - if (isNaN(h)) h = 0; - if (isNaN(c)) c = 0; - return d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); -} - -d3.lab = function(l, a, b) { - return arguments.length === 1 - ? (l instanceof d3_Lab ? d3_lab(l.l, l.a, l.b) - : (l instanceof d3_Hcl ? d3_hcl_lab(l.l, l.c, l.h) - : d3_rgb_lab((l = d3.rgb(l)).r, l.g, l.b))) - : d3_lab(+l, +a, +b); -}; - -function d3_lab(l, a, b) { - return new d3_Lab(l, a, b); -} - -function d3_Lab(l, a, b) { - this.l = l; - this.a = a; - this.b = b; -} - -// Corresponds roughly to RGB brighter/darker -var d3_lab_K = 18; - -// D65 standard referent -var d3_lab_X = 0.950470, - d3_lab_Y = 1, - d3_lab_Z = 1.088830; - -var d3_labPrototype = d3_Lab.prototype = new d3_Color; - -d3_labPrototype.brighter = function(k) { - return d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); -}; - -d3_labPrototype.darker = function(k) { - return d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); -}; - -d3_labPrototype.rgb = function() { - return d3_lab_rgb(this.l, this.a, this.b); -}; - -function d3_lab_rgb(l, a, b) { - var y = (l + 16) / 116, - x = y + a / 500, - z = y - b / 200; - x = d3_lab_xyz(x) * d3_lab_X; - y = d3_lab_xyz(y) * d3_lab_Y; - z = d3_lab_xyz(z) * d3_lab_Z; - return d3_rgb( - d3_xyz_rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z), - d3_xyz_rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z), - d3_xyz_rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z) - ); -} - -function d3_lab_hcl(l, a, b) { - return l > 0 - ? d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) - : d3_hcl(NaN, NaN, l); -} - -function d3_lab_xyz(x) { - return x > 0.206893034 ? x * x * x : (x - 4 / 29) / 7.787037; -} -function d3_xyz_lab(x) { - return x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; -} - -function d3_xyz_rgb(r) { - return Math.round(255 * (r <= 0.00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - 0.055)); -} - -d3.rgb = function(r, g, b) { - return arguments.length === 1 - ? (r instanceof d3_Rgb ? d3_rgb(r.r, r.g, r.b) - : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb)) - : d3_rgb(~~r, ~~g, ~~b); -}; - -function d3_rgbNumber(value) { - return d3_rgb(value >> 16, value >> 8 & 0xff, value & 0xff); -} - -function d3_rgbString(value) { - return d3_rgbNumber(value) + ""; -} - -function d3_rgb(r, g, b) { - return new d3_Rgb(r, g, b); -} - -function d3_Rgb(r, g, b) { - this.r = r; - this.g = g; - this.b = b; -} - -var d3_rgbPrototype = d3_Rgb.prototype = new d3_Color; - -d3_rgbPrototype.brighter = function(k) { - k = Math.pow(0.7, arguments.length ? k : 1); - var r = this.r, - g = this.g, - b = this.b, - i = 30; - if (!r && !g && !b) return d3_rgb(i, i, i); - if (r && r < i) r = i; - if (g && g < i) g = i; - if (b && b < i) b = i; - return d3_rgb(Math.min(255, ~~(r / k)), Math.min(255, ~~(g / k)), Math.min(255, ~~(b / k))); -}; - -d3_rgbPrototype.darker = function(k) { - k = Math.pow(0.7, arguments.length ? k : 1); - return d3_rgb(~~(k * this.r), ~~(k * this.g), ~~(k * this.b)); -}; - -d3_rgbPrototype.hsl = function() { - return d3_rgb_hsl(this.r, this.g, this.b); -}; - -d3_rgbPrototype.toString = function() { - return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b); -}; - -function d3_rgb_hex(v) { - return v < 0x10 - ? "0" + Math.max(0, v).toString(16) - : Math.min(255, v).toString(16); -} - -function d3_rgb_parse(format, rgb, hsl) { - var r = 0, // red channel; int in [0, 255] - g = 0, // green channel; int in [0, 255] - b = 0, // blue channel; int in [0, 255] - m1, // CSS color specification match - m2, // CSS color specification type (e.g., rgb) - name; - - /* Handle hsl, rgb. */ - m1 = /([a-z]+)\((.*)\)/i.exec(format); - if (m1) { - m2 = m1[2].split(","); - switch (m1[1]) { - case "hsl": { - return hsl( - parseFloat(m2[0]), // degrees - parseFloat(m2[1]) / 100, // percentage - parseFloat(m2[2]) / 100 // percentage - ); - } - case "rgb": { - return rgb( - d3_rgb_parseNumber(m2[0]), - d3_rgb_parseNumber(m2[1]), - d3_rgb_parseNumber(m2[2]) - ); - } - } - } - - /* Named colors. */ - if (name = d3_rgb_names.get(format)) return rgb(name.r, name.g, name.b); - - /* Hexadecimal colors: #rgb and #rrggbb. */ - if (format != null && format.charAt(0) === "#") { - if (format.length === 4) { - r = format.charAt(1); r += r; - g = format.charAt(2); g += g; - b = format.charAt(3); b += b; - } else if (format.length === 7) { - r = format.substring(1, 3); - g = format.substring(3, 5); - b = format.substring(5, 7); - } - r = parseInt(r, 16); - g = parseInt(g, 16); - b = parseInt(b, 16); - } - - return rgb(r, g, b); -} - -function d3_rgb_hsl(r, g, b) { - var min = Math.min(r /= 255, g /= 255, b /= 255), - max = Math.max(r, g, b), - d = max - min, - h, - s, - l = (max + min) / 2; - if (d) { - s = l < .5 ? d / (max + min) : d / (2 - max - min); - if (r == max) h = (g - b) / d + (g < b ? 6 : 0); - else if (g == max) h = (b - r) / d + 2; - else h = (r - g) / d + 4; - h *= 60; - } else { - h = NaN; - s = l > 0 && l < 1 ? 0 : h; - } - return d3_hsl(h, s, l); -} - -function d3_rgb_lab(r, g, b) { - r = d3_rgb_xyz(r); - g = d3_rgb_xyz(g); - b = d3_rgb_xyz(b); - var x = d3_xyz_lab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / d3_lab_X), - y = d3_xyz_lab((0.2126729 * r + 0.7151522 * g + 0.0721750 * b) / d3_lab_Y), - z = d3_xyz_lab((0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / d3_lab_Z); - return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); -} - -function d3_rgb_xyz(r) { - return (r /= 255) <= 0.04045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4); -} - -function d3_rgb_parseNumber(c) { // either integer or percentage - var f = parseFloat(c); - return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f; -} - -var d3_rgb_names = d3.map({ - aliceblue: 0xf0f8ff, - antiquewhite: 0xfaebd7, - aqua: 0x00ffff, - aquamarine: 0x7fffd4, - azure: 0xf0ffff, - beige: 0xf5f5dc, - bisque: 0xffe4c4, - black: 0x000000, - blanchedalmond: 0xffebcd, - blue: 0x0000ff, - blueviolet: 0x8a2be2, - brown: 0xa52a2a, - burlywood: 0xdeb887, - cadetblue: 0x5f9ea0, - chartreuse: 0x7fff00, - chocolate: 0xd2691e, - coral: 0xff7f50, - cornflowerblue: 0x6495ed, - cornsilk: 0xfff8dc, - crimson: 0xdc143c, - cyan: 0x00ffff, - darkblue: 0x00008b, - darkcyan: 0x008b8b, - darkgoldenrod: 0xb8860b, - darkgray: 0xa9a9a9, - darkgreen: 0x006400, - darkgrey: 0xa9a9a9, - darkkhaki: 0xbdb76b, - darkmagenta: 0x8b008b, - darkolivegreen: 0x556b2f, - darkorange: 0xff8c00, - darkorchid: 0x9932cc, - darkred: 0x8b0000, - darksalmon: 0xe9967a, - darkseagreen: 0x8fbc8f, - darkslateblue: 0x483d8b, - darkslategray: 0x2f4f4f, - darkslategrey: 0x2f4f4f, - darkturquoise: 0x00ced1, - darkviolet: 0x9400d3, - deeppink: 0xff1493, - deepskyblue: 0x00bfff, - dimgray: 0x696969, - dimgrey: 0x696969, - dodgerblue: 0x1e90ff, - firebrick: 0xb22222, - floralwhite: 0xfffaf0, - forestgreen: 0x228b22, - fuchsia: 0xff00ff, - gainsboro: 0xdcdcdc, - ghostwhite: 0xf8f8ff, - gold: 0xffd700, - goldenrod: 0xdaa520, - gray: 0x808080, - green: 0x008000, - greenyellow: 0xadff2f, - grey: 0x808080, - honeydew: 0xf0fff0, - hotpink: 0xff69b4, - indianred: 0xcd5c5c, - indigo: 0x4b0082, - ivory: 0xfffff0, - khaki: 0xf0e68c, - lavender: 0xe6e6fa, - lavenderblush: 0xfff0f5, - lawngreen: 0x7cfc00, - lemonchiffon: 0xfffacd, - lightblue: 0xadd8e6, - lightcoral: 0xf08080, - lightcyan: 0xe0ffff, - lightgoldenrodyellow: 0xfafad2, - lightgray: 0xd3d3d3, - lightgreen: 0x90ee90, - lightgrey: 0xd3d3d3, - lightpink: 0xffb6c1, - lightsalmon: 0xffa07a, - lightseagreen: 0x20b2aa, - lightskyblue: 0x87cefa, - lightslategray: 0x778899, - lightslategrey: 0x778899, - lightsteelblue: 0xb0c4de, - lightyellow: 0xffffe0, - lime: 0x00ff00, - limegreen: 0x32cd32, - linen: 0xfaf0e6, - magenta: 0xff00ff, - maroon: 0x800000, - mediumaquamarine: 0x66cdaa, - mediumblue: 0x0000cd, - mediumorchid: 0xba55d3, - mediumpurple: 0x9370db, - mediumseagreen: 0x3cb371, - mediumslateblue: 0x7b68ee, - mediumspringgreen: 0x00fa9a, - mediumturquoise: 0x48d1cc, - mediumvioletred: 0xc71585, - midnightblue: 0x191970, - mintcream: 0xf5fffa, - mistyrose: 0xffe4e1, - moccasin: 0xffe4b5, - navajowhite: 0xffdead, - navy: 0x000080, - oldlace: 0xfdf5e6, - olive: 0x808000, - olivedrab: 0x6b8e23, - orange: 0xffa500, - orangered: 0xff4500, - orchid: 0xda70d6, - palegoldenrod: 0xeee8aa, - palegreen: 0x98fb98, - paleturquoise: 0xafeeee, - palevioletred: 0xdb7093, - papayawhip: 0xffefd5, - peachpuff: 0xffdab9, - peru: 0xcd853f, - pink: 0xffc0cb, - plum: 0xdda0dd, - powderblue: 0xb0e0e6, - purple: 0x800080, - red: 0xff0000, - rosybrown: 0xbc8f8f, - royalblue: 0x4169e1, - saddlebrown: 0x8b4513, - salmon: 0xfa8072, - sandybrown: 0xf4a460, - seagreen: 0x2e8b57, - seashell: 0xfff5ee, - sienna: 0xa0522d, - silver: 0xc0c0c0, - skyblue: 0x87ceeb, - slateblue: 0x6a5acd, - slategray: 0x708090, - slategrey: 0x708090, - snow: 0xfffafa, - springgreen: 0x00ff7f, - steelblue: 0x4682b4, - tan: 0xd2b48c, - teal: 0x008080, - thistle: 0xd8bfd8, - tomato: 0xff6347, - turquoise: 0x40e0d0, - violet: 0xee82ee, - wheat: 0xf5deb3, - white: 0xffffff, - whitesmoke: 0xf5f5f5, - yellow: 0xffff00, - yellowgreen: 0x9acd32 -}); - -d3_rgb_names.forEach(function(key, value) { - d3_rgb_names.set(key, d3_rgbNumber(value)); -}); - -d3.interpolateRgb = d3_interpolateRgb; - -function d3_interpolateRgb(a, b) { - a = d3.rgb(a); - b = d3.rgb(b); - var ar = a.r, - ag = a.g, - ab = a.b, - br = b.r - ar, - bg = b.g - ag, - bb = b.b - ab; - return function(t) { - return "#" - + d3_rgb_hex(Math.round(ar + br * t)) - + d3_rgb_hex(Math.round(ag + bg * t)) - + d3_rgb_hex(Math.round(ab + bb * t)); - }; -} - -d3.interpolateObject = d3_interpolateObject; - -function d3_interpolateObject(a, b) { - var i = {}, - c = {}, - k; - for (k in a) { - if (k in b) { - i[k] = d3_interpolate(a[k], b[k]); - } else { - c[k] = a[k]; - } - } - for (k in b) { - if (!(k in a)) { - c[k] = b[k]; - } - } - return function(t) { - for (k in i) c[k] = i[k](t); - return c; - }; -} - -d3.interpolateArray = d3_interpolateArray; - -function d3_interpolateArray(a, b) { - var x = [], - c = [], - na = a.length, - nb = b.length, - n0 = Math.min(a.length, b.length), - i; - for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i])); - for (; i < na; ++i) c[i] = a[i]; - for (; i < nb; ++i) c[i] = b[i]; - return function(t) { - for (i = 0; i < n0; ++i) c[i] = x[i](t); - return c; - }; -} -d3.interpolateNumber = d3_interpolateNumber; - -function d3_interpolateNumber(a, b) { - b -= a = +a; - return function(t) { return a + b * t; }; -} - -d3.interpolateString = d3_interpolateString; - -function d3_interpolateString(a, b) { - var m, // current match - i, // current index - j, // current index (for coalescing) - s0 = 0, // start index of current string prefix - s1 = 0, // end index of current string prefix - s = [], // string constants and placeholders - q = [], // number interpolators - n, // q.length - o; - - // Coerce inputs to strings. - a = a + "", b = b + ""; - - // Reset our regular expression! - d3_interpolate_number.lastIndex = 0; - - // Find all numbers in b. - for (i = 0; m = d3_interpolate_number.exec(b); ++i) { - if (m.index) s.push(b.substring(s0, s1 = m.index)); - q.push({i: s.length, x: m[0]}); - s.push(null); - s0 = d3_interpolate_number.lastIndex; - } - if (s0 < b.length) s.push(b.substring(s0)); - - // Find all numbers in a. - for (i = 0, n = q.length; (m = d3_interpolate_number.exec(a)) && i < n; ++i) { - o = q[i]; - if (o.x == m[0]) { // The numbers match, so coalesce. - if (o.i) { - if (s[o.i + 1] == null) { // This match is followed by another number. - s[o.i - 1] += o.x; - s.splice(o.i, 1); - for (j = i + 1; j < n; ++j) q[j].i--; - } else { // This match is followed by a string, so coalesce twice. - s[o.i - 1] += o.x + s[o.i + 1]; - s.splice(o.i, 2); - for (j = i + 1; j < n; ++j) q[j].i -= 2; - } - } else { - if (s[o.i + 1] == null) { // This match is followed by another number. - s[o.i] = o.x; - } else { // This match is followed by a string, so coalesce twice. - s[o.i] = o.x + s[o.i + 1]; - s.splice(o.i + 1, 1); - for (j = i + 1; j < n; ++j) q[j].i--; - } - } - q.splice(i, 1); - n--; - i--; - } else { - o.x = d3_interpolateNumber(parseFloat(m[0]), parseFloat(o.x)); - } - } - - // Remove any numbers in b not found in a. - while (i < n) { - o = q.pop(); - if (s[o.i + 1] == null) { // This match is followed by another number. - s[o.i] = o.x; - } else { // This match is followed by a string, so coalesce twice. - s[o.i] = o.x + s[o.i + 1]; - s.splice(o.i + 1, 1); - } - n--; - } - - // Special optimization for only a single match. - if (s.length === 1) { - return s[0] == null - ? (o = q[0].x, function(t) { return o(t) + ""; }) - : function() { return b; }; - } - - // Otherwise, interpolate each of the numbers and rejoin the string. - return function(t) { - for (i = 0; i < n; ++i) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }; -} - -var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g; - -d3.interpolate = d3_interpolate; - -function d3_interpolate(a, b) { - var i = d3.interpolators.length, f; - while (--i >= 0 && !(f = d3.interpolators[i](a, b))); - return f; -} - -d3.interpolators = [ - function(a, b) { - var t = typeof b; - return (t === "string" ? (d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString) - : b instanceof d3_Color ? d3_interpolateRgb - : t === "object" ? (Array.isArray(b) ? d3_interpolateArray : d3_interpolateObject) - : d3_interpolateNumber)(a, b); - } -]; - -d3.transform = function(string) { - var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); - return (d3.transform = function(string) { - if (string != null) { - g.setAttribute("transform", string); - var t = g.transform.baseVal.consolidate(); - } - return new d3_transform(t ? t.matrix : d3_transformIdentity); - })(string); -}; - -// Compute x-scale and normalize the first row. -// Compute shear and make second row orthogonal to first. -// Compute y-scale and normalize the second row. -// Finally, compute the rotation. -function d3_transform(m) { - var r0 = [m.a, m.b], - r1 = [m.c, m.d], - kx = d3_transformNormalize(r0), - kz = d3_transformDot(r0, r1), - ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0; - if (r0[0] * r1[1] < r1[0] * r0[1]) { - r0[0] *= -1; - r0[1] *= -1; - kx *= -1; - kz *= -1; - } - this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees; - this.translate = [m.e, m.f]; - this.scale = [kx, ky]; - this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0; -}; - -d3_transform.prototype.toString = function() { - return "translate(" + this.translate - + ")rotate(" + this.rotate - + ")skewX(" + this.skew - + ")scale(" + this.scale - + ")"; -}; - -function d3_transformDot(a, b) { - return a[0] * b[0] + a[1] * b[1]; -} - -function d3_transformNormalize(a) { - var k = Math.sqrt(d3_transformDot(a, a)); - if (k) { - a[0] /= k; - a[1] /= k; - } - return k; -} - -function d3_transformCombine(a, b, k) { - a[0] += k * b[0]; - a[1] += k * b[1]; - return a; -} - -var d3_transformIdentity = {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0}; - -d3.interpolateTransform = d3_interpolateTransform; - -function d3_interpolateTransform(a, b) { - var s = [], // string constants and placeholders - q = [], // number interpolators - n, - A = d3.transform(a), - B = d3.transform(b), - ta = A.translate, - tb = B.translate, - ra = A.rotate, - rb = B.rotate, - wa = A.skew, - wb = B.skew, - ka = A.scale, - kb = B.scale; - - if (ta[0] != tb[0] || ta[1] != tb[1]) { - s.push("translate(", null, ",", null, ")"); - q.push({i: 1, x: d3_interpolateNumber(ta[0], tb[0])}, {i: 3, x: d3_interpolateNumber(ta[1], tb[1])}); - } else if (tb[0] || tb[1]) { - s.push("translate(" + tb + ")"); - } else { - s.push(""); - } - - if (ra != rb) { - if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; // shortest path - q.push({i: s.push(s.pop() + "rotate(", null, ")") - 2, x: d3_interpolateNumber(ra, rb)}); - } else if (rb) { - s.push(s.pop() + "rotate(" + rb + ")"); - } - - if (wa != wb) { - q.push({i: s.push(s.pop() + "skewX(", null, ")") - 2, x: d3_interpolateNumber(wa, wb)}); - } else if (wb) { - s.push(s.pop() + "skewX(" + wb + ")"); - } - - if (ka[0] != kb[0] || ka[1] != kb[1]) { - n = s.push(s.pop() + "scale(", null, ",", null, ")"); - q.push({i: n - 4, x: d3_interpolateNumber(ka[0], kb[0])}, {i: n - 2, x: d3_interpolateNumber(ka[1], kb[1])}); - } else if (kb[0] != 1 || kb[1] != 1) { - s.push(s.pop() + "scale(" + kb + ")"); - } - - n = q.length; - return function(t) { - var i = -1, o; - while (++i < n) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }; -} - -d3_transitionPrototype.tween = function(name, tween) { - var id = this.id; - if (arguments.length < 2) return this.node().__transition__[id].tween.get(name); - return d3_selection_each(this, tween == null - ? function(node) { node.__transition__[id].tween.remove(name); } - : function(node) { node.__transition__[id].tween.set(name, tween); }); -}; - -function d3_transition_tween(groups, name, value, tween) { - var id = groups.id; - return d3_selection_each(groups, typeof value === "function" - ? function(node, i, j) { node.__transition__[id].tween.set(name, tween(value.call(node, node.__data__, i, j))); } - : (value = tween(value), function(node) { node.__transition__[id].tween.set(name, value); })); -} - -d3_transitionPrototype.attr = function(nameNS, value) { - if (arguments.length < 2) { - - // For attr(object), the object specifies the names and values of the - // attributes to transition. The values may be functions that are - // evaluated for each element. - for (value in nameNS) this.attr(value, nameNS[value]); - return this; - } - - var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, - name = d3.ns.qualify(nameNS); - - // For attr(string, null), remove the attribute with the specified name. - function attrNull() { - this.removeAttribute(name); - } - function attrNullNS() { - this.removeAttributeNS(name.space, name.local); - } - - // For attr(string, string), set the attribute with the specified name. - function attrTween(b) { - return b == null ? attrNull : (b += "", function() { - var a = this.getAttribute(name), i; - return a !== b && (i = interpolate(a, b), function(t) { this.setAttribute(name, i(t)); }); - }); - } - function attrTweenNS(b) { - return b == null ? attrNullNS : (b += "", function() { - var a = this.getAttributeNS(name.space, name.local), i; - return a !== b && (i = interpolate(a, b), function(t) { this.setAttributeNS(name.space, name.local, i(t)); }); - }); - } - - return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween); -}; - -d3_transitionPrototype.attrTween = function(nameNS, tween) { - var name = d3.ns.qualify(nameNS); - - function attrTween(d, i) { - var f = tween.call(this, d, i, this.getAttribute(name)); - return f && function(t) { this.setAttribute(name, f(t)); }; - } - function attrTweenNS(d, i) { - var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); - return f && function(t) { this.setAttributeNS(name.space, name.local, f(t)); }; - } - - return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween); -}; - -d3_transitionPrototype.style = function(name, value, priority) { - var n = arguments.length; - if (n < 3) { - - // For style(object) or style(object, string), the object specifies the - // names and values of the attributes to set or remove. The values may be - // functions that are evaluated for each element. The optional string - // specifies the priority. - if (typeof name !== "string") { - if (n < 2) value = ""; - for (priority in name) this.style(priority, name[priority], value); - return this; - } - - // For style(string, string) or style(string, function), use the default - // priority. The priority is ignored for style(string, null). - priority = ""; - } - - // For style(name, null) or style(name, null, priority), remove the style - // property with the specified name. The priority is ignored. - function styleNull() { - this.style.removeProperty(name); - } - - // For style(name, string) or style(name, string, priority), set the style - // property with the specified name, using the specified priority. - // Otherwise, a name, value and priority are specified, and handled as below. - function styleString(b) { - return b == null ? styleNull : (b += "", function() { - var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i; - return a !== b && (i = d3_interpolate(a, b), function(t) { this.style.setProperty(name, i(t), priority); }); - }); - } - - return d3_transition_tween(this, "style." + name, value, styleString); -}; - -d3_transitionPrototype.styleTween = function(name, tween, priority) { - if (arguments.length < 3) priority = ""; - - function styleTween(d, i) { - var f = tween.call(this, d, i, d3_window.getComputedStyle(this, null).getPropertyValue(name)); - return f && function(t) { this.style.setProperty(name, f(t), priority); }; - } - - return this.tween("style." + name, styleTween); -}; - -d3_transitionPrototype.text = function(value) { - return d3_transition_tween(this, "text", value, d3_transition_text); -}; - -function d3_transition_text(b) { - if (b == null) b = ""; - return function() { this.textContent = b; }; -} - -d3_transitionPrototype.remove = function() { - return this.each("end.transition", function() { - var p; - if (!this.__transition__ && (p = this.parentNode)) p.removeChild(this); - }); -}; - -d3_transitionPrototype.ease = function(value) { - var id = this.id; - if (arguments.length < 1) return this.node().__transition__[id].ease; - if (typeof value !== "function") value = d3.ease.apply(d3, arguments); - return d3_selection_each(this, function(node) { node.__transition__[id].ease = value; }); -}; - -d3_transitionPrototype.delay = function(value) { - var id = this.id; - return d3_selection_each(this, typeof value === "function" - ? function(node, i, j) { node.__transition__[id].delay = value.call(node, node.__data__, i, j) | 0; } - : (value |= 0, function(node) { node.__transition__[id].delay = value; })); -}; - -d3_transitionPrototype.duration = function(value) { - var id = this.id; - return d3_selection_each(this, typeof value === "function" - ? function(node, i, j) { node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j) | 0); } - : (value = Math.max(1, value | 0), function(node) { node.__transition__[id].duration = value; })); -}; - -d3_transitionPrototype.each = function(type, listener) { - var id = this.id; - if (arguments.length < 2) { - var inherit = d3_transitionInherit, - inheritId = d3_transitionInheritId; - d3_transitionInheritId = id; - d3_selection_each(this, function(node, i, j) { - d3_transitionInherit = node.__transition__[id]; - type.call(node, node.__data__, i, j); - }); - d3_transitionInherit = inherit; - d3_transitionInheritId = inheritId; - } else { - d3_selection_each(this, function(node) { - var transition = node.__transition__[id]; - (transition.event || (transition.event = d3.dispatch("start", "end"))).on(type, listener); - }); - } - return this; -}; - -d3_transitionPrototype.transition = function() { - var id0 = this.id, - id1 = ++d3_transitionId, - subgroups = [], - subgroup, - group, - node, - transition; - - for (var j = 0, m = this.length; j < m; j++) { - subgroups.push(subgroup = []); - for (var group = this[j], i = 0, n = group.length; i < n; i++) { - if (node = group[i]) { - transition = Object.create(node.__transition__[id0]); - transition.delay += transition.duration; - d3_transitionNode(node, i, id1, transition); - } - subgroup.push(node); - } - } - - return d3_transition(subgroups, id1); -}; - -function d3_transitionNode(node, i, id, inherit) { - var lock = node.__transition__ || (node.__transition__ = {active: 0, count: 0}), - transition = lock[id]; - - if (!transition) { - var time = inherit.time; - - transition = lock[id] = { - tween: new d3_Map, - time: time, - ease: inherit.ease, - delay: inherit.delay, - duration: inherit.duration - }; - - ++lock.count; - - d3.timer(function(elapsed) { - var d = node.__data__, - ease = transition.ease, - delay = transition.delay, - duration = transition.duration, - tweened = []; - - if (delay <= elapsed) return start(elapsed); - d3_timer_replace(start, delay, time); - - function start(elapsed) { - if (lock.active > id) return stop(); - lock.active = id; - transition.event && transition.event.start.call(node, d, i); - - transition.tween.forEach(function(key, value) { - if (value = value.call(node, d, i)) { - tweened.push(value); - } - }); - - if (tick(elapsed)) return 1; - d3_timer_replace(tick, 0, time); - } - - function tick(elapsed) { - if (lock.active !== id) return stop(); - - var t = (elapsed - delay) / duration, - e = ease(t), - n = tweened.length; - - while (n > 0) { - tweened[--n].call(node, e); - } - - if (t >= 1) { - stop(); - transition.event && transition.event.end.call(node, d, i); - return 1; - } - } - - function stop() { - if (--lock.count) delete lock[id]; - else delete node.__transition__; - return 1; - } - }, 0, time); - } -} - -d3.xhr = d3_xhrType(d3_identity); - -function d3_xhrType(response) { - return function(url, mimeType, callback) { - if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, mimeType = null; - return d3_xhr(url, mimeType, response, callback); - }; -} - -function d3_xhr(url, mimeType, response, callback) { - var xhr = {}, - dispatch = d3.dispatch("progress", "load", "error"), - headers = {}, - request = new XMLHttpRequest, - responseType = null; - - // If IE does not support CORS, use XDomainRequest. - if (d3_window.XDomainRequest - && !("withCredentials" in request) - && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest; - - "onload" in request - ? request.onload = request.onerror = respond - : request.onreadystatechange = function() { request.readyState > 3 && respond(); }; - - function respond() { - var status = request.status, result; - if (!status && request.responseText || status >= 200 && status < 300 || status === 304) { - try { - result = response.call(xhr, request); - } catch (e) { - dispatch.error.call(xhr, e); - return; - } - dispatch.load.call(xhr, result); - } else { - dispatch.error.call(xhr, request); - } - } - - request.onprogress = function(event) { - var o = d3.event; - d3.event = event; - try { dispatch.progress.call(xhr, request); } - finally { d3.event = o; } - }; - - xhr.header = function(name, value) { - name = (name + "").toLowerCase(); - if (arguments.length < 2) return headers[name]; - if (value == null) delete headers[name]; - else headers[name] = value + ""; - return xhr; - }; - - // If mimeType is non-null and no Accept header is set, a default is used. - xhr.mimeType = function(value) { - if (!arguments.length) return mimeType; - mimeType = value == null ? null : value + ""; - return xhr; - }; - - // Specifies what type the response value should take; - // for instance, arraybuffer, blob, document, or text. - xhr.responseType = function(value) { - if (!arguments.length) return responseType; - responseType = value; - return xhr; - }; - - // Specify how to convert the response content to a specific type; - // changes the callback value on "load" events. - xhr.response = function(value) { - response = value; - return xhr; - }; - - // Convenience methods. - ["get", "post"].forEach(function(method) { - xhr[method] = function() { - return xhr.send.apply(xhr, [method].concat(d3_array(arguments))); - }; - }); - - // If callback is non-null, it will be used for error and load events. - xhr.send = function(method, data, callback) { - if (arguments.length === 2 && typeof data === "function") callback = data, data = null; - request.open(method, url, true); - if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; - if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); - if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); - if (responseType != null) request.responseType = responseType; - if (callback != null) xhr.on("error", callback).on("load", function(request) { callback(null, request); }); - request.send(data == null ? null : data); - return xhr; - }; - - xhr.abort = function() { - request.abort(); - return xhr; - }; - - d3.rebind(xhr, dispatch, "on"); - - return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); -}; - -function d3_xhr_fixCallback(callback) { - return callback.length === 1 - ? function(error, request) { callback(error == null ? request : null); } - : callback; -} - -d3.text = d3_xhrType(function(request) { - return request.responseText; -}); - -d3.json = function(url, callback) { - return d3_xhr(url, "application/json", d3_json, callback); -}; - -function d3_json(request) { - return JSON.parse(request.responseText); -} - -d3.html = function(url, callback) { - return d3_xhr(url, "text/html", d3_html, callback); -}; - -function d3_html(request) { - var range = d3_document.createRange(); - range.selectNode(d3_document.body); - return range.createContextualFragment(request.responseText); -} - -d3.xml = d3_xhrType(function(request) { - return request.responseXML; -}); - return d3; -})(); -d3.combobox = function() { - var event = d3.dispatch('accept'), - data = [], - suggestions = []; - - var fetcher = function(val, cb) { - cb(data.filter(function(d) { - return d.title - .toString() - .toLowerCase() - .indexOf(val.toLowerCase()) !== -1; - })); - }; - - var combobox = function(input) { - var idx = -1, - container = d3.select(document.body) - .selectAll('div.combobox') - .filter(function(d) { return d === input.node(); }), - shown = !container.empty(); - - input - .classed('combobox-input', true) - .on('focus.typeahead', focus) - .on('blur.typeahead', blur) - .on('keydown.typeahead', keydown) - .on('keyup.typeahead', keyup) - .on('input.typeahead', change) - .each(function() { - var parent = this.parentNode, - sibling = this.nextSibling; - - var carat = d3.select(parent).selectAll('.combobox-carat') - .filter(function(d) { return d === input.node(); }) - .data([input.node()]); - - carat.enter().insert('div', function() { return sibling; }) - .attr('class', 'combobox-carat'); - - carat - .on('mousedown', function () { - // prevent the form element from blurring. it blurs - // on mousedown - d3.event.stopPropagation(); - d3.event.preventDefault(); - input.node().focus(); - }); - }); - - function focus() { - fetch(render); - } - - function blur() { - window.setTimeout(hide, 150); - } - - function show() { - if (!shown) { - container = d3.select(document.body) - .insert('div', ':first-child') - .datum(input.node()) - .attr('class', 'combobox') - .style({ - position: 'absolute', - display: 'block', - left: '0px' - }); - - d3.select(document.body) - .on('scroll.combobox', render, true); - - shown = true; - } - } - - function hide() { - if (shown) { - idx = -1; - container.remove(); - - d3.select(document.body) - .on('scroll.combobox', null); - - shown = false; - } - } - - function keydown() { - switch (d3.event.keyCode) { - // backspace, delete - case 8: - case 46: - input.on('input.typeahead', function() { - idx = -1; - render(); - input.on('input.typeahead', change); - }); - break; - // tab - case 9: - container.selectAll('a.selected').each(event.accept); - break; - // return - case 13: - d3.event.preventDefault(); - break; - // up arrow - case 38: - nav(-1); - d3.event.preventDefault(); - break; - // down arrow - case 40: - nav(+1); - d3.event.preventDefault(); - break; - } - d3.event.stopPropagation(); - } - - function keyup() { - switch (d3.event.keyCode) { - // escape - case 27: - hide(); - break; - // return - case 13: - container.selectAll('a.selected').each(event.accept); - hide(); - break; - } - } - - function change() { - fetch(function() { - autocomplete(); - render(); - }); - } - - function nav(dir) { - idx = Math.max(Math.min(idx + dir, suggestions.length - 1), 0); - input.property('value', suggestions[idx].value); - render(); - ensureVisible(); - } - - function value() { - var value = input.property('value'), - start = input.property('selectionStart'), - end = input.property('selectionEnd'); - - if (start && end) { - value = value.substring(0, start); - } - - return value; - } - - function fetch(cb) { - fetcher.call(input, value(), function(_) { - suggestions = _; - cb(); - }); - } - - function autocomplete() { - var v = value(); - - idx = -1; - - if (!v) return; - - for (var i = 0; i < suggestions.length; i++) { - if (suggestions[i].value.toLowerCase().indexOf(v.toLowerCase()) === 0) { - var completion = v + suggestions[i].value.substr(v.length); - idx = i; - input.property('value', completion); - input.node().setSelectionRange(v.length, completion.length); - return; - } - } - } - - function render() { - if (suggestions.length && document.activeElement === input.node()) { - show(); - } else { - hide(); - return; - } - - var options = container - .selectAll('a.combobox-option') - .data(suggestions, function(d) { return d.value; }); - - options.enter().append('a') - .attr('class', 'combobox-option') - .text(function(d) { return d.value; }); - - options - .attr('title', function(d) { return d.title; }) - .classed('selected', function(d, i) { return i == idx; }) - .on('mouseover', select) - .on('click', accept) - .order(); - - options.exit() - .remove(); - - var rect = input.node().getBoundingClientRect(); - - container.style({ - 'left': rect.left + 'px', - 'width': rect.width + 'px', - 'top': rect.height + rect.top + 'px' - }); - } - - function select(d, i) { - idx = i; - render(); - } - - function ensureVisible() { - var node = container.selectAll('a.selected').node(); - if (node) node.scrollIntoView(); - } - - function accept(d) { - if (!shown) return; - input - .property('value', d.value) - .trigger('change'); - event.accept(d); - hide(); - } - }; - - combobox.fetcher = function(_) { - if (!arguments.length) return fetcher; - fetcher = _; - return combobox; - }; - - combobox.data = function(_) { - if (!arguments.length) return data; - data = _; - return combobox; - }; - - return d3.rebind(combobox, event, 'on'); -}; -d3.geo.tile = function() { - var size = [960, 500], - scale = 256, - scaleExtent = [0, 20], - translate = [size[0] / 2, size[1] / 2], - zoomDelta = 0; - - function bound(_) { - return Math.min(scaleExtent[1], Math.max(scaleExtent[0], _)); - } - - function tile() { - var z = Math.max(Math.log(scale) / Math.LN2 - 8, 0), - z0 = bound(Math.round(z + zoomDelta)), - k = Math.pow(2, z - z0 + 8), - origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k], - tiles = [], - cols = d3.range(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))), - rows = d3.range(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1]))); - - rows.forEach(function(y) { - cols.forEach(function(x) { - tiles.push([x, y, z0]); - }); - }); - - tiles.translate = origin; - tiles.scale = k; - - return tiles; - } - - tile.scaleExtent = function(_) { - if (!arguments.length) return scaleExtent; - scaleExtent = _; - return tile; - }; - - tile.size = function(_) { - if (!arguments.length) return size; - size = _; - return tile; - }; - - tile.scale = function(_) { - if (!arguments.length) return scale; - scale = _; - return tile; - }; - - tile.translate = function(_) { - if (!arguments.length) return translate; - translate = _; - return tile; - }; - - tile.zoomDelta = function(_) { - if (!arguments.length) return zoomDelta; - zoomDelta = +_; - return tile; - }; - - return tile; -}; -d3.jsonp = function (url, callback) { - function rand() { - var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', - c = '', i = -1; - while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52)); - return c; - } - - function create(url) { - var e = url.match(/callback=d3.jsonp.(\w+)/), - c = e ? e[1] : rand(); - d3.jsonp[c] = function(data) { - callback(data); - delete d3.jsonp[c]; - script.remove(); - }; - return 'd3.jsonp.' + c; - } - - var cb = create(url), - script = d3.select('head') - .append('script') - .attr('type', 'text/javascript') - .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb)); -}; -/* - * This code is licensed under the MIT license. - * - * Copyright © 2013, iD authors. - * - * Portions copyright © 2011, Keith Cirkel - * See https://github.com/keithamus/jwerty - * - */ -d3.keybinding = function(namespace) { - var bindings = []; - - function matches(binding, event) { - for (var p in binding.event) { - if (event[p] != binding.event[p]) - return false; - } - - return (!binding.capture) === (event.eventPhase !== Event.CAPTURING_PHASE); - } - - function capture() { - for (var i = 0; i < bindings.length; i++) { - var binding = bindings[i]; - if (matches(binding, d3.event)) { - binding.callback(); - } - } - } - - function bubble() { - var tagName = d3.select(d3.event.target).node().tagName; - if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') { - return; - } - capture(); - } - - function keybinding(selection) { - selection = selection || d3.select(document); - selection.on('keydown.capture' + namespace, capture, true); - selection.on('keydown.bubble' + namespace, bubble, false); - return keybinding; - } - - keybinding.off = function(selection) { - selection = selection || d3.select(document); - selection.on('keydown.capture' + namespace, null); - selection.on('keydown.bubble' + namespace, null); - return keybinding; - }; - - keybinding.on = function(code, callback, capture) { - var binding = { - event: { - keyCode: 0, - shiftKey: false, - ctrlKey: false, - altKey: false, - metaKey: false - }, - capture: capture, - callback: callback - }; - - code = code.toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g); - - for (var i = 0; i < code.length; i++) { - // Normalise matching errors - if (code[i] === '++') code[i] = '+'; - - if (code[i] in d3.keybinding.modifierCodes) { - binding.event[d3.keybinding.modifierProperties[d3.keybinding.modifierCodes[code[i]]]] = true; - } else if (code[i] in d3.keybinding.keyCodes) { - binding.event.keyCode = d3.keybinding.keyCodes[code[i]]; - } - } - - bindings.push(binding); - - return keybinding; - }; - - return keybinding; -}; +(function () { + + var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + var check = function (it) { + return it && it.Math == Math && it; + }; + + // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 + var global$F = + // eslint-disable-next-line es/no-global-this -- safe + check(typeof globalThis == 'object' && globalThis) || + check(typeof window == 'object' && window) || + // eslint-disable-next-line no-restricted-globals -- safe + check(typeof self == 'object' && self) || + check(typeof commonjsGlobal == 'object' && commonjsGlobal) || + // eslint-disable-next-line no-new-func -- fallback + (function () { return this; })() || Function('return this')(); + + var objectGetOwnPropertyDescriptor = {}; + + var fails$N = function (exec) { + try { + return !!exec(); + } catch (error) { + return true; + } + }; + + var fails$M = fails$N; + + // Detect IE8's incomplete defineProperty implementation + var descriptors = !fails$M(function () { + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] != 7; + }); + + var objectPropertyIsEnumerable = {}; + + var $propertyIsEnumerable$1 = {}.propertyIsEnumerable; + // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe + var getOwnPropertyDescriptor$5 = Object.getOwnPropertyDescriptor; + + // Nashorn ~ JDK8 bug + var NASHORN_BUG = getOwnPropertyDescriptor$5 && !$propertyIsEnumerable$1.call({ 1: 2 }, 1); + + // `Object.prototype.propertyIsEnumerable` method implementation + // https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable + objectPropertyIsEnumerable.f = NASHORN_BUG ? function propertyIsEnumerable(V) { + var descriptor = getOwnPropertyDescriptor$5(this, V); + return !!descriptor && descriptor.enumerable; + } : $propertyIsEnumerable$1; + + var createPropertyDescriptor$7 = function (bitmap, value) { + return { + enumerable: !(bitmap & 1), + configurable: !(bitmap & 2), + writable: !(bitmap & 4), + value: value + }; + }; + + var toString$2 = {}.toString; + + var classofRaw$1 = function (it) { + return toString$2.call(it).slice(8, -1); + }; + + var fails$L = fails$N; + var classof$c = classofRaw$1; + + var split$1 = ''.split; + + // fallback for non-array-like ES3 and non-enumerable old V8 strings + var indexedObject = fails$L(function () { + // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346 + // eslint-disable-next-line no-prototype-builtins -- safe + return !Object('z').propertyIsEnumerable(0); + }) ? function (it) { + return classof$c(it) == 'String' ? split$1.call(it, '') : Object(it); + } : Object; + + // `RequireObjectCoercible` abstract operation + // https://tc39.es/ecma262/#sec-requireobjectcoercible + var requireObjectCoercible$e = function (it) { + if (it == undefined) throw TypeError("Can't call method on " + it); + return it; + }; + + // toObject with fallback for non-array-like ES3 strings + var IndexedObject$4 = indexedObject; + var requireObjectCoercible$d = requireObjectCoercible$e; + + var toIndexedObject$b = function (it) { + return IndexedObject$4(requireObjectCoercible$d(it)); + }; + + var isObject$r = function (it) { + return typeof it === 'object' ? it !== null : typeof it === 'function'; + }; + + var isObject$q = isObject$r; + + // `ToPrimitive` abstract operation + // https://tc39.es/ecma262/#sec-toprimitive + // instead of the ES6 spec version, we didn't implement @@toPrimitive case + // and the second argument - flag - preferred type is a string + var toPrimitive$7 = function (input, PREFERRED_STRING) { + if (!isObject$q(input)) return input; + var fn, val; + if (PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject$q(val = fn.call(input))) return val; + if (typeof (fn = input.valueOf) == 'function' && !isObject$q(val = fn.call(input))) return val; + if (!PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject$q(val = fn.call(input))) return val; + throw TypeError("Can't convert object to primitive value"); + }; + + var requireObjectCoercible$c = requireObjectCoercible$e; + + // `ToObject` abstract operation + // https://tc39.es/ecma262/#sec-toobject + var toObject$i = function (argument) { + return Object(requireObjectCoercible$c(argument)); + }; + + var toObject$h = toObject$i; + + var hasOwnProperty$3 = {}.hasOwnProperty; + + var has$j = Object.hasOwn || function hasOwn(it, key) { + return hasOwnProperty$3.call(toObject$h(it), key); + }; + + var global$E = global$F; + var isObject$p = isObject$r; + + var document$3 = global$E.document; + // typeof document.createElement is 'object' in old IE + var EXISTS = isObject$p(document$3) && isObject$p(document$3.createElement); + + var documentCreateElement$1 = function (it) { + return EXISTS ? document$3.createElement(it) : {}; + }; + + var DESCRIPTORS$m = descriptors; + var fails$K = fails$N; + var createElement$1 = documentCreateElement$1; + + // Thank's IE8 for his funny defineProperty + var ie8DomDefine = !DESCRIPTORS$m && !fails$K(function () { + // eslint-disable-next-line es/no-object-defineproperty -- requied for testing + return Object.defineProperty(createElement$1('div'), 'a', { + get: function () { return 7; } + }).a != 7; + }); + + var DESCRIPTORS$l = descriptors; + var propertyIsEnumerableModule$2 = objectPropertyIsEnumerable; + var createPropertyDescriptor$6 = createPropertyDescriptor$7; + var toIndexedObject$a = toIndexedObject$b; + var toPrimitive$6 = toPrimitive$7; + var has$i = has$j; + var IE8_DOM_DEFINE$1 = ie8DomDefine; + + // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe + var $getOwnPropertyDescriptor$1 = Object.getOwnPropertyDescriptor; + + // `Object.getOwnPropertyDescriptor` method + // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor + objectGetOwnPropertyDescriptor.f = DESCRIPTORS$l ? $getOwnPropertyDescriptor$1 : function getOwnPropertyDescriptor(O, P) { + O = toIndexedObject$a(O); + P = toPrimitive$6(P, true); + if (IE8_DOM_DEFINE$1) try { + return $getOwnPropertyDescriptor$1(O, P); + } catch (error) { /* empty */ } + if (has$i(O, P)) return createPropertyDescriptor$6(!propertyIsEnumerableModule$2.f.call(O, P), O[P]); + }; + + var objectDefineProperty = {}; + + var isObject$o = isObject$r; + + var anObject$m = function (it) { + if (!isObject$o(it)) { + throw TypeError(String(it) + ' is not an object'); + } return it; + }; + + var DESCRIPTORS$k = descriptors; + var IE8_DOM_DEFINE = ie8DomDefine; + var anObject$l = anObject$m; + var toPrimitive$5 = toPrimitive$7; + + // eslint-disable-next-line es/no-object-defineproperty -- safe + var $defineProperty$1 = Object.defineProperty; + + // `Object.defineProperty` method + // https://tc39.es/ecma262/#sec-object.defineproperty + objectDefineProperty.f = DESCRIPTORS$k ? $defineProperty$1 : function defineProperty(O, P, Attributes) { + anObject$l(O); + P = toPrimitive$5(P, true); + anObject$l(Attributes); + if (IE8_DOM_DEFINE) try { + return $defineProperty$1(O, P, Attributes); + } catch (error) { /* empty */ } + if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported'); + if ('value' in Attributes) O[P] = Attributes.value; + return O; + }; + + var DESCRIPTORS$j = descriptors; + var definePropertyModule$7 = objectDefineProperty; + var createPropertyDescriptor$5 = createPropertyDescriptor$7; + + var createNonEnumerableProperty$e = DESCRIPTORS$j ? function (object, key, value) { + return definePropertyModule$7.f(object, key, createPropertyDescriptor$5(1, value)); + } : function (object, key, value) { + object[key] = value; + return object; + }; + + var redefine$g = {exports: {}}; + + var global$D = global$F; + var createNonEnumerableProperty$d = createNonEnumerableProperty$e; + + var setGlobal$3 = function (key, value) { + try { + createNonEnumerableProperty$d(global$D, key, value); + } catch (error) { + global$D[key] = value; + } return value; + }; + + var global$C = global$F; + var setGlobal$2 = setGlobal$3; + + var SHARED = '__core-js_shared__'; + var store$4 = global$C[SHARED] || setGlobal$2(SHARED, {}); + + var sharedStore = store$4; + + var store$3 = sharedStore; + + var functionToString = Function.toString; + + // this helper broken in `core-js@3.4.1-3.4.4`, so we can't use `shared` helper + if (typeof store$3.inspectSource != 'function') { + store$3.inspectSource = function (it) { + return functionToString.call(it); + }; + } + + var inspectSource$3 = store$3.inspectSource; + + var global$B = global$F; + var inspectSource$2 = inspectSource$3; + + var WeakMap$1 = global$B.WeakMap; + + var nativeWeakMap = typeof WeakMap$1 === 'function' && /native code/.test(inspectSource$2(WeakMap$1)); + + var shared$5 = {exports: {}}; + + var isPure = false; + + var store$2 = sharedStore; + + (shared$5.exports = function (key, value) { + return store$2[key] || (store$2[key] = value !== undefined ? value : {}); + })('versions', []).push({ + version: '3.15.0', + mode: 'global', + copyright: '© 2021 Denis Pushkarev (zloirock.ru)' + }); + + var id$2 = 0; + var postfix = Math.random(); + + var uid$5 = function (key) { + return 'Symbol(' + String(key === undefined ? '' : key) + ')_' + (++id$2 + postfix).toString(36); + }; + + var shared$4 = shared$5.exports; + var uid$4 = uid$5; + + var keys$3 = shared$4('keys'); + + var sharedKey$4 = function (key) { + return keys$3[key] || (keys$3[key] = uid$4(key)); + }; + + var hiddenKeys$6 = {}; + + var NATIVE_WEAK_MAP = nativeWeakMap; + var global$A = global$F; + var isObject$n = isObject$r; + var createNonEnumerableProperty$c = createNonEnumerableProperty$e; + var objectHas = has$j; + var shared$3 = sharedStore; + var sharedKey$3 = sharedKey$4; + var hiddenKeys$5 = hiddenKeys$6; + + var OBJECT_ALREADY_INITIALIZED = 'Object already initialized'; + var WeakMap = global$A.WeakMap; + var set$4, get$5, has$h; + + var enforce = function (it) { + return has$h(it) ? get$5(it) : set$4(it, {}); + }; + + var getterFor = function (TYPE) { + return function (it) { + var state; + if (!isObject$n(it) || (state = get$5(it)).type !== TYPE) { + throw TypeError('Incompatible receiver, ' + TYPE + ' required'); + } return state; + }; + }; + + if (NATIVE_WEAK_MAP || shared$3.state) { + var store$1 = shared$3.state || (shared$3.state = new WeakMap()); + var wmget = store$1.get; + var wmhas = store$1.has; + var wmset = store$1.set; + set$4 = function (it, metadata) { + if (wmhas.call(store$1, it)) throw new TypeError(OBJECT_ALREADY_INITIALIZED); + metadata.facade = it; + wmset.call(store$1, it, metadata); + return metadata; + }; + get$5 = function (it) { + return wmget.call(store$1, it) || {}; + }; + has$h = function (it) { + return wmhas.call(store$1, it); + }; + } else { + var STATE = sharedKey$3('state'); + hiddenKeys$5[STATE] = true; + set$4 = function (it, metadata) { + if (objectHas(it, STATE)) throw new TypeError(OBJECT_ALREADY_INITIALIZED); + metadata.facade = it; + createNonEnumerableProperty$c(it, STATE, metadata); + return metadata; + }; + get$5 = function (it) { + return objectHas(it, STATE) ? it[STATE] : {}; + }; + has$h = function (it) { + return objectHas(it, STATE); + }; + } + + var internalState = { + set: set$4, + get: get$5, + has: has$h, + enforce: enforce, + getterFor: getterFor + }; + + var global$z = global$F; + var createNonEnumerableProperty$b = createNonEnumerableProperty$e; + var has$g = has$j; + var setGlobal$1 = setGlobal$3; + var inspectSource$1 = inspectSource$3; + var InternalStateModule$9 = internalState; + + var getInternalState$7 = InternalStateModule$9.get; + var enforceInternalState$1 = InternalStateModule$9.enforce; + var TEMPLATE = String(String).split('String'); + + (redefine$g.exports = function (O, key, value, options) { + var unsafe = options ? !!options.unsafe : false; + var simple = options ? !!options.enumerable : false; + var noTargetGet = options ? !!options.noTargetGet : false; + var state; + if (typeof value == 'function') { + if (typeof key == 'string' && !has$g(value, 'name')) { + createNonEnumerableProperty$b(value, 'name', key); + } + state = enforceInternalState$1(value); + if (!state.source) { + state.source = TEMPLATE.join(typeof key == 'string' ? key : ''); + } + } + if (O === global$z) { + if (simple) O[key] = value; + else setGlobal$1(key, value); + return; + } else if (!unsafe) { + delete O[key]; + } else if (!noTargetGet && O[key]) { + simple = true; + } + if (simple) O[key] = value; + else createNonEnumerableProperty$b(O, key, value); + // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative + })(Function.prototype, 'toString', function toString() { + return typeof this == 'function' && getInternalState$7(this).source || inspectSource$1(this); + }); + + var global$y = global$F; + + var path$2 = global$y; + + var path$1 = path$2; + var global$x = global$F; + + var aFunction$a = function (variable) { + return typeof variable == 'function' ? variable : undefined; + }; + + var getBuiltIn$9 = function (namespace, method) { + return arguments.length < 2 ? aFunction$a(path$1[namespace]) || aFunction$a(global$x[namespace]) + : path$1[namespace] && path$1[namespace][method] || global$x[namespace] && global$x[namespace][method]; + }; + + var objectGetOwnPropertyNames = {}; + + var ceil$1 = Math.ceil; + var floor$7 = Math.floor; + + // `ToInteger` abstract operation + // https://tc39.es/ecma262/#sec-tointeger + var toInteger$b = function (argument) { + return isNaN(argument = +argument) ? 0 : (argument > 0 ? floor$7 : ceil$1)(argument); + }; + + var toInteger$a = toInteger$b; + + var min$9 = Math.min; + + // `ToLength` abstract operation + // https://tc39.es/ecma262/#sec-tolength + var toLength$q = function (argument) { + return argument > 0 ? min$9(toInteger$a(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991 + }; + + var toInteger$9 = toInteger$b; + + var max$4 = Math.max; + var min$8 = Math.min; + + // Helper for a popular repeating case of the spec: + // Let integer be ? ToInteger(index). + // If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length). + var toAbsoluteIndex$8 = function (index, length) { + var integer = toInteger$9(index); + return integer < 0 ? max$4(integer + length, 0) : min$8(integer, length); + }; + + var toIndexedObject$9 = toIndexedObject$b; + var toLength$p = toLength$q; + var toAbsoluteIndex$7 = toAbsoluteIndex$8; + + // `Array.prototype.{ indexOf, includes }` methods implementation + var createMethod$6 = function (IS_INCLUDES) { + return function ($this, el, fromIndex) { + var O = toIndexedObject$9($this); + var length = toLength$p(O.length); + var index = toAbsoluteIndex$7(fromIndex, length); + var value; + // Array#includes uses SameValueZero equality algorithm + // eslint-disable-next-line no-self-compare -- NaN check + if (IS_INCLUDES && el != el) while (length > index) { + value = O[index++]; + // eslint-disable-next-line no-self-compare -- NaN check + if (value != value) return true; + // Array#indexOf ignores holes, Array#includes - not + } else for (;length > index; index++) { + if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0; + } return !IS_INCLUDES && -1; + }; + }; + + var arrayIncludes = { + // `Array.prototype.includes` method + // https://tc39.es/ecma262/#sec-array.prototype.includes + includes: createMethod$6(true), + // `Array.prototype.indexOf` method + // https://tc39.es/ecma262/#sec-array.prototype.indexof + indexOf: createMethod$6(false) + }; + + var has$f = has$j; + var toIndexedObject$8 = toIndexedObject$b; + var indexOf = arrayIncludes.indexOf; + var hiddenKeys$4 = hiddenKeys$6; + + var objectKeysInternal = function (object, names) { + var O = toIndexedObject$8(object); + var i = 0; + var result = []; + var key; + for (key in O) !has$f(hiddenKeys$4, key) && has$f(O, key) && result.push(key); + // Don't enum bug & hidden keys + while (names.length > i) if (has$f(O, key = names[i++])) { + ~indexOf(result, key) || result.push(key); + } + return result; + }; + + // IE8- don't enum bug keys + var enumBugKeys$3 = [ + 'constructor', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'toLocaleString', + 'toString', + 'valueOf' + ]; + + var internalObjectKeys$1 = objectKeysInternal; + var enumBugKeys$2 = enumBugKeys$3; + + var hiddenKeys$3 = enumBugKeys$2.concat('length', 'prototype'); + + // `Object.getOwnPropertyNames` method + // https://tc39.es/ecma262/#sec-object.getownpropertynames + // eslint-disable-next-line es/no-object-getownpropertynames -- safe + objectGetOwnPropertyNames.f = Object.getOwnPropertyNames || function getOwnPropertyNames(O) { + return internalObjectKeys$1(O, hiddenKeys$3); + }; + + var objectGetOwnPropertySymbols = {}; + + // eslint-disable-next-line es/no-object-getownpropertysymbols -- safe + objectGetOwnPropertySymbols.f = Object.getOwnPropertySymbols; + + var getBuiltIn$8 = getBuiltIn$9; + var getOwnPropertyNamesModule$1 = objectGetOwnPropertyNames; + var getOwnPropertySymbolsModule$2 = objectGetOwnPropertySymbols; + var anObject$k = anObject$m; + + // all object keys, includes non-enumerable and symbols + var ownKeys$1 = getBuiltIn$8('Reflect', 'ownKeys') || function ownKeys(it) { + var keys = getOwnPropertyNamesModule$1.f(anObject$k(it)); + var getOwnPropertySymbols = getOwnPropertySymbolsModule$2.f; + return getOwnPropertySymbols ? keys.concat(getOwnPropertySymbols(it)) : keys; + }; + + var has$e = has$j; + var ownKeys = ownKeys$1; + var getOwnPropertyDescriptorModule$3 = objectGetOwnPropertyDescriptor; + var definePropertyModule$6 = objectDefineProperty; + + var copyConstructorProperties$2 = function (target, source) { + var keys = ownKeys(source); + var defineProperty = definePropertyModule$6.f; + var getOwnPropertyDescriptor = getOwnPropertyDescriptorModule$3.f; + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (!has$e(target, key)) defineProperty(target, key, getOwnPropertyDescriptor(source, key)); + } + }; + + var fails$J = fails$N; + + var replacement = /#|\.prototype\./; + + var isForced$5 = function (feature, detection) { + var value = data[normalize$1(feature)]; + return value == POLYFILL ? true + : value == NATIVE ? false + : typeof detection == 'function' ? fails$J(detection) + : !!detection; + }; + + var normalize$1 = isForced$5.normalize = function (string) { + return String(string).replace(replacement, '.').toLowerCase(); + }; + + var data = isForced$5.data = {}; + var NATIVE = isForced$5.NATIVE = 'N'; + var POLYFILL = isForced$5.POLYFILL = 'P'; + + var isForced_1 = isForced$5; + + var global$w = global$F; + var getOwnPropertyDescriptor$4 = objectGetOwnPropertyDescriptor.f; + var createNonEnumerableProperty$a = createNonEnumerableProperty$e; + var redefine$f = redefine$g.exports; + var setGlobal = setGlobal$3; + var copyConstructorProperties$1 = copyConstructorProperties$2; + var isForced$4 = isForced_1; + + /* + options.target - name of the target object + options.global - target is the global object + options.stat - export as static methods of target + options.proto - export as prototype methods of target + options.real - real prototype method for the `pure` version + options.forced - export even if the native feature is available + options.bind - bind methods to the target, required for the `pure` version + options.wrap - wrap constructors to preventing global pollution, required for the `pure` version + options.unsafe - use the simple assignment of property instead of delete + defineProperty + options.sham - add a flag to not completely full polyfills + options.enumerable - export as enumerable property + options.noTargetGet - prevent calling a getter on target + */ + var _export = function (options, source) { + var TARGET = options.target; + var GLOBAL = options.global; + var STATIC = options.stat; + var FORCED, target, key, targetProperty, sourceProperty, descriptor; + if (GLOBAL) { + target = global$w; + } else if (STATIC) { + target = global$w[TARGET] || setGlobal(TARGET, {}); + } else { + target = (global$w[TARGET] || {}).prototype; + } + if (target) for (key in source) { + sourceProperty = source[key]; + if (options.noTargetGet) { + descriptor = getOwnPropertyDescriptor$4(target, key); + targetProperty = descriptor && descriptor.value; + } else targetProperty = target[key]; + FORCED = isForced$4(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced); + // contained in target + if (!FORCED && targetProperty !== undefined) { + if (typeof sourceProperty === typeof targetProperty) continue; + copyConstructorProperties$1(sourceProperty, targetProperty); + } + // add a flag to not completely full polyfills + if (options.sham || (targetProperty && targetProperty.sham)) { + createNonEnumerableProperty$a(sourceProperty, 'sham', true); + } + // extend global + redefine$f(target, key, sourceProperty, options); + } + }; + + var $$16 = _export; + + // `Date.now` method + // https://tc39.es/ecma262/#sec-date.now + $$16({ target: 'Date', stat: true }, { + now: function now() { + return new Date().getTime(); + } + }); + + var redefine$e = redefine$g.exports; + + var DatePrototype$1 = Date.prototype; + var INVALID_DATE = 'Invalid Date'; + var TO_STRING$1 = 'toString'; + var nativeDateToString = DatePrototype$1[TO_STRING$1]; + var getTime$1 = DatePrototype$1.getTime; + + // `Date.prototype.toString` method + // https://tc39.es/ecma262/#sec-date.prototype.tostring + if (new Date(NaN) + '' != INVALID_DATE) { + redefine$e(DatePrototype$1, TO_STRING$1, function toString() { + var value = getTime$1.call(this); + // eslint-disable-next-line no-self-compare -- NaN check + return value === value ? nativeDateToString.call(this) : INVALID_DATE; + }); + } + + function _typeof(obj) { + "@babel/helpers - typeof"; + + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); + } + + function _classCallCheck$1(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _defineProperties$1(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass$1(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties$1(Constructor.prototype, protoProps); + if (staticProps) _defineProperties$1(Constructor, staticProps); + return Constructor; + } + + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + function _slicedToArray(arr, i) { + return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); + } + + function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); + } + + function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) return _arrayLikeToArray(arr); + } + + function _arrayWithHoles(arr) { + if (Array.isArray(arr)) return arr; + } + + function _iterableToArray(iter) { + if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); + } + + function _iterableToArrayLimit(arr, i) { + var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; + + if (_i == null) return; + var _arr = []; + var _n = true; + var _d = false; + + var _s, _e; + + try { + for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { + _arr.push(_s.value); + + if (i && _arr.length === i) break; + } + } catch (err) { + _d = true; + _e = err; + } finally { + try { + if (!_n && _i["return"] != null) _i["return"](); + } finally { + if (_d) throw _e; + } + } + + return _arr; + } + + function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); + } + + function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + + return arr2; + } + + function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } + + function _nonIterableRest() { + throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } + + function _createForOfIteratorHelper(o, allowArrayLike) { + var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; + + if (!it) { + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + + var F = function () {}; + + return { + s: F, + n: function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; + }, + e: function (e) { + throw e; + }, + f: F + }; + } + + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } + + var normalCompletion = true, + didErr = false, + err; + return { + s: function () { + it = it.call(o); + }, + n: function () { + var step = it.next(); + normalCompletion = step.done; + return step; + }, + e: function (e) { + didErr = true; + err = e; + }, + f: function () { + try { + if (!normalCompletion && it.return != null) it.return(); + } finally { + if (didErr) throw err; + } + } + }; + } + + var wellKnownSymbolWrapped = {}; + + var getBuiltIn$7 = getBuiltIn$9; + + var engineUserAgent = getBuiltIn$7('navigator', 'userAgent') || ''; + + var global$v = global$F; + var userAgent$5 = engineUserAgent; + + var process$4 = global$v.process; + var versions = process$4 && process$4.versions; + var v8 = versions && versions.v8; + var match, version$1; + + if (v8) { + match = v8.split('.'); + version$1 = match[0] < 4 ? 1 : match[0] + match[1]; + } else if (userAgent$5) { + match = userAgent$5.match(/Edge\/(\d+)/); + if (!match || match[1] >= 74) { + match = userAgent$5.match(/Chrome\/(\d+)/); + if (match) version$1 = match[1]; + } + } + + var engineV8Version = version$1 && +version$1; + + /* eslint-disable es/no-symbol -- required for testing */ + + var V8_VERSION$3 = engineV8Version; + var fails$I = fails$N; + + // eslint-disable-next-line es/no-object-getownpropertysymbols -- required for testing + var nativeSymbol = !!Object.getOwnPropertySymbols && !fails$I(function () { + var symbol = Symbol(); + // Chrome 38 Symbol has incorrect toString conversion + // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances + return !String(symbol) || !(Object(symbol) instanceof Symbol) || + // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances + !Symbol.sham && V8_VERSION$3 && V8_VERSION$3 < 41; + }); + + /* eslint-disable es/no-symbol -- required for testing */ + + var NATIVE_SYMBOL$2 = nativeSymbol; + + var useSymbolAsUid = NATIVE_SYMBOL$2 + && !Symbol.sham + && typeof Symbol.iterator == 'symbol'; + + var global$u = global$F; + var shared$2 = shared$5.exports; + var has$d = has$j; + var uid$3 = uid$5; + var NATIVE_SYMBOL$1 = nativeSymbol; + var USE_SYMBOL_AS_UID$1 = useSymbolAsUid; + + var WellKnownSymbolsStore$1 = shared$2('wks'); + var Symbol$1 = global$u.Symbol; + var createWellKnownSymbol = USE_SYMBOL_AS_UID$1 ? Symbol$1 : Symbol$1 && Symbol$1.withoutSetter || uid$3; + + var wellKnownSymbol$s = function (name) { + if (!has$d(WellKnownSymbolsStore$1, name) || !(NATIVE_SYMBOL$1 || typeof WellKnownSymbolsStore$1[name] == 'string')) { + if (NATIVE_SYMBOL$1 && has$d(Symbol$1, name)) { + WellKnownSymbolsStore$1[name] = Symbol$1[name]; + } else { + WellKnownSymbolsStore$1[name] = createWellKnownSymbol('Symbol.' + name); + } + } return WellKnownSymbolsStore$1[name]; + }; + + var wellKnownSymbol$r = wellKnownSymbol$s; + + wellKnownSymbolWrapped.f = wellKnownSymbol$r; + + var path = path$2; + var has$c = has$j; + var wrappedWellKnownSymbolModule$1 = wellKnownSymbolWrapped; + var defineProperty$a = objectDefineProperty.f; + + var defineWellKnownSymbol$4 = function (NAME) { + var Symbol = path.Symbol || (path.Symbol = {}); + if (!has$c(Symbol, NAME)) defineProperty$a(Symbol, NAME, { + value: wrappedWellKnownSymbolModule$1.f(NAME) + }); + }; + + var defineWellKnownSymbol$3 = defineWellKnownSymbol$4; + + // `Symbol.iterator` well-known symbol + // https://tc39.es/ecma262/#sec-symbol.iterator + defineWellKnownSymbol$3('iterator'); + + var internalObjectKeys = objectKeysInternal; + var enumBugKeys$1 = enumBugKeys$3; + + // `Object.keys` method + // https://tc39.es/ecma262/#sec-object.keys + // eslint-disable-next-line es/no-object-keys -- safe + var objectKeys$4 = Object.keys || function keys(O) { + return internalObjectKeys(O, enumBugKeys$1); + }; + + var DESCRIPTORS$i = descriptors; + var definePropertyModule$5 = objectDefineProperty; + var anObject$j = anObject$m; + var objectKeys$3 = objectKeys$4; + + // `Object.defineProperties` method + // https://tc39.es/ecma262/#sec-object.defineproperties + // eslint-disable-next-line es/no-object-defineproperties -- safe + var objectDefineProperties = DESCRIPTORS$i ? Object.defineProperties : function defineProperties(O, Properties) { + anObject$j(O); + var keys = objectKeys$3(Properties); + var length = keys.length; + var index = 0; + var key; + while (length > index) definePropertyModule$5.f(O, key = keys[index++], Properties[key]); + return O; + }; + + var getBuiltIn$6 = getBuiltIn$9; + + var html$2 = getBuiltIn$6('document', 'documentElement'); + + var anObject$i = anObject$m; + var defineProperties$2 = objectDefineProperties; + var enumBugKeys = enumBugKeys$3; + var hiddenKeys$2 = hiddenKeys$6; + var html$1 = html$2; + var documentCreateElement = documentCreateElement$1; + var sharedKey$2 = sharedKey$4; + + var GT = '>'; + var LT = '<'; + var PROTOTYPE$2 = 'prototype'; + var SCRIPT = 'script'; + var IE_PROTO$1 = sharedKey$2('IE_PROTO'); + + var EmptyConstructor = function () { /* empty */ }; + + var scriptTag = function (content) { + return LT + SCRIPT + GT + content + LT + '/' + SCRIPT + GT; + }; + + // Create object with fake `null` prototype: use ActiveX Object with cleared prototype + var NullProtoObjectViaActiveX = function (activeXDocument) { + activeXDocument.write(scriptTag('')); + activeXDocument.close(); + var temp = activeXDocument.parentWindow.Object; + activeXDocument = null; // avoid memory leak + return temp; + }; + + // Create object with fake `null` prototype: use iframe Object with cleared prototype + var NullProtoObjectViaIFrame = function () { + // Thrash, waste and sodomy: IE GC bug + var iframe = documentCreateElement('iframe'); + var JS = 'java' + SCRIPT + ':'; + var iframeDocument; + iframe.style.display = 'none'; + html$1.appendChild(iframe); + // https://github.com/zloirock/core-js/issues/475 + iframe.src = String(JS); + iframeDocument = iframe.contentWindow.document; + iframeDocument.open(); + iframeDocument.write(scriptTag('document.F=Object')); + iframeDocument.close(); + return iframeDocument.F; + }; + + // Check for document.domain and active x support + // No need to use active x approach when document.domain is not set + // see https://github.com/es-shims/es5-shim/issues/150 + // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346 + // avoid IE GC bug + var activeXDocument; + var NullProtoObject = function () { + try { + /* global ActiveXObject -- old IE */ + activeXDocument = document.domain && new ActiveXObject('htmlfile'); + } catch (error) { /* ignore */ } + NullProtoObject = activeXDocument ? NullProtoObjectViaActiveX(activeXDocument) : NullProtoObjectViaIFrame(); + var length = enumBugKeys.length; + while (length--) delete NullProtoObject[PROTOTYPE$2][enumBugKeys[length]]; + return NullProtoObject(); + }; + + hiddenKeys$2[IE_PROTO$1] = true; + + // `Object.create` method + // https://tc39.es/ecma262/#sec-object.create + var objectCreate = Object.create || function create(O, Properties) { + var result; + if (O !== null) { + EmptyConstructor[PROTOTYPE$2] = anObject$i(O); + result = new EmptyConstructor(); + EmptyConstructor[PROTOTYPE$2] = null; + // add "__proto__" for Object.getPrototypeOf polyfill + result[IE_PROTO$1] = O; + } else result = NullProtoObject(); + return Properties === undefined ? result : defineProperties$2(result, Properties); + }; + + var wellKnownSymbol$q = wellKnownSymbol$s; + var create$b = objectCreate; + var definePropertyModule$4 = objectDefineProperty; + + var UNSCOPABLES = wellKnownSymbol$q('unscopables'); + var ArrayPrototype$1 = Array.prototype; + + // Array.prototype[@@unscopables] + // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables + if (ArrayPrototype$1[UNSCOPABLES] == undefined) { + definePropertyModule$4.f(ArrayPrototype$1, UNSCOPABLES, { + configurable: true, + value: create$b(null) + }); + } + + // add a key to Array.prototype[@@unscopables] + var addToUnscopables$5 = function (key) { + ArrayPrototype$1[UNSCOPABLES][key] = true; + }; + + var iterators = {}; + + var fails$H = fails$N; + + var correctPrototypeGetter = !fails$H(function () { + function F() { /* empty */ } + F.prototype.constructor = null; + // eslint-disable-next-line es/no-object-getprototypeof -- required for testing + return Object.getPrototypeOf(new F()) !== F.prototype; + }); + + var has$b = has$j; + var toObject$g = toObject$i; + var sharedKey$1 = sharedKey$4; + var CORRECT_PROTOTYPE_GETTER$1 = correctPrototypeGetter; + + var IE_PROTO = sharedKey$1('IE_PROTO'); + var ObjectPrototype$3 = Object.prototype; + + // `Object.getPrototypeOf` method + // https://tc39.es/ecma262/#sec-object.getprototypeof + // eslint-disable-next-line es/no-object-getprototypeof -- safe + var objectGetPrototypeOf = CORRECT_PROTOTYPE_GETTER$1 ? Object.getPrototypeOf : function (O) { + O = toObject$g(O); + if (has$b(O, IE_PROTO)) return O[IE_PROTO]; + if (typeof O.constructor == 'function' && O instanceof O.constructor) { + return O.constructor.prototype; + } return O instanceof Object ? ObjectPrototype$3 : null; + }; + + var fails$G = fails$N; + var getPrototypeOf$4 = objectGetPrototypeOf; + var createNonEnumerableProperty$9 = createNonEnumerableProperty$e; + var has$a = has$j; + var wellKnownSymbol$p = wellKnownSymbol$s; + + var ITERATOR$8 = wellKnownSymbol$p('iterator'); + var BUGGY_SAFARI_ITERATORS$1 = false; + + var returnThis$2 = function () { return this; }; + + // `%IteratorPrototype%` object + // https://tc39.es/ecma262/#sec-%iteratorprototype%-object + var IteratorPrototype$2, PrototypeOfArrayIteratorPrototype, arrayIterator; + + /* eslint-disable es/no-array-prototype-keys -- safe */ + if ([].keys) { + arrayIterator = [].keys(); + // Safari 8 has buggy iterators w/o `next` + if (!('next' in arrayIterator)) BUGGY_SAFARI_ITERATORS$1 = true; + else { + PrototypeOfArrayIteratorPrototype = getPrototypeOf$4(getPrototypeOf$4(arrayIterator)); + if (PrototypeOfArrayIteratorPrototype !== Object.prototype) IteratorPrototype$2 = PrototypeOfArrayIteratorPrototype; + } + } + + var NEW_ITERATOR_PROTOTYPE = IteratorPrototype$2 == undefined || fails$G(function () { + var test = {}; + // FF44- legacy iterators case + return IteratorPrototype$2[ITERATOR$8].call(test) !== test; + }); + + if (NEW_ITERATOR_PROTOTYPE) IteratorPrototype$2 = {}; + + // `%IteratorPrototype%[@@iterator]()` method + // https://tc39.es/ecma262/#sec-%iteratorprototype%-@@iterator + if (!has$a(IteratorPrototype$2, ITERATOR$8)) { + createNonEnumerableProperty$9(IteratorPrototype$2, ITERATOR$8, returnThis$2); + } + + var iteratorsCore = { + IteratorPrototype: IteratorPrototype$2, + BUGGY_SAFARI_ITERATORS: BUGGY_SAFARI_ITERATORS$1 + }; + + var defineProperty$9 = objectDefineProperty.f; + var has$9 = has$j; + var wellKnownSymbol$o = wellKnownSymbol$s; + + var TO_STRING_TAG$4 = wellKnownSymbol$o('toStringTag'); + + var setToStringTag$a = function (it, TAG, STATIC) { + if (it && !has$9(it = STATIC ? it : it.prototype, TO_STRING_TAG$4)) { + defineProperty$9(it, TO_STRING_TAG$4, { configurable: true, value: TAG }); + } + }; + + var IteratorPrototype$1 = iteratorsCore.IteratorPrototype; + var create$a = objectCreate; + var createPropertyDescriptor$4 = createPropertyDescriptor$7; + var setToStringTag$9 = setToStringTag$a; + var Iterators$4 = iterators; + + var returnThis$1 = function () { return this; }; + + var createIteratorConstructor$2 = function (IteratorConstructor, NAME, next) { + var TO_STRING_TAG = NAME + ' Iterator'; + IteratorConstructor.prototype = create$a(IteratorPrototype$1, { next: createPropertyDescriptor$4(1, next) }); + setToStringTag$9(IteratorConstructor, TO_STRING_TAG, false); + Iterators$4[TO_STRING_TAG] = returnThis$1; + return IteratorConstructor; + }; + + var isObject$m = isObject$r; + + var aPossiblePrototype$1 = function (it) { + if (!isObject$m(it) && it !== null) { + throw TypeError("Can't set " + String(it) + ' as a prototype'); + } return it; + }; + + /* eslint-disable no-proto -- safe */ + + var anObject$h = anObject$m; + var aPossiblePrototype = aPossiblePrototype$1; + + // `Object.setPrototypeOf` method + // https://tc39.es/ecma262/#sec-object.setprototypeof + // Works with __proto__ only. Old v8 can't work with null proto objects. + // eslint-disable-next-line es/no-object-setprototypeof -- safe + var objectSetPrototypeOf = Object.setPrototypeOf || ('__proto__' in {} ? function () { + var CORRECT_SETTER = false; + var test = {}; + var setter; + try { + // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe + setter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set; + setter.call(test, []); + CORRECT_SETTER = test instanceof Array; + } catch (error) { /* empty */ } + return function setPrototypeOf(O, proto) { + anObject$h(O); + aPossiblePrototype(proto); + if (CORRECT_SETTER) setter.call(O, proto); + else O.__proto__ = proto; + return O; + }; + }() : undefined); + + var $$15 = _export; + var createIteratorConstructor$1 = createIteratorConstructor$2; + var getPrototypeOf$3 = objectGetPrototypeOf; + var setPrototypeOf$6 = objectSetPrototypeOf; + var setToStringTag$8 = setToStringTag$a; + var createNonEnumerableProperty$8 = createNonEnumerableProperty$e; + var redefine$d = redefine$g.exports; + var wellKnownSymbol$n = wellKnownSymbol$s; + var Iterators$3 = iterators; + var IteratorsCore = iteratorsCore; + + var IteratorPrototype = IteratorsCore.IteratorPrototype; + var BUGGY_SAFARI_ITERATORS = IteratorsCore.BUGGY_SAFARI_ITERATORS; + var ITERATOR$7 = wellKnownSymbol$n('iterator'); + var KEYS = 'keys'; + var VALUES = 'values'; + var ENTRIES = 'entries'; + + var returnThis = function () { return this; }; + + var defineIterator$3 = function (Iterable, NAME, IteratorConstructor, next, DEFAULT, IS_SET, FORCED) { + createIteratorConstructor$1(IteratorConstructor, NAME, next); + + var getIterationMethod = function (KIND) { + if (KIND === DEFAULT && defaultIterator) return defaultIterator; + if (!BUGGY_SAFARI_ITERATORS && KIND in IterablePrototype) return IterablePrototype[KIND]; + switch (KIND) { + case KEYS: return function keys() { return new IteratorConstructor(this, KIND); }; + case VALUES: return function values() { return new IteratorConstructor(this, KIND); }; + case ENTRIES: return function entries() { return new IteratorConstructor(this, KIND); }; + } return function () { return new IteratorConstructor(this); }; + }; + + var TO_STRING_TAG = NAME + ' Iterator'; + var INCORRECT_VALUES_NAME = false; + var IterablePrototype = Iterable.prototype; + var nativeIterator = IterablePrototype[ITERATOR$7] + || IterablePrototype['@@iterator'] + || DEFAULT && IterablePrototype[DEFAULT]; + var defaultIterator = !BUGGY_SAFARI_ITERATORS && nativeIterator || getIterationMethod(DEFAULT); + var anyNativeIterator = NAME == 'Array' ? IterablePrototype.entries || nativeIterator : nativeIterator; + var CurrentIteratorPrototype, methods, KEY; + + // fix native + if (anyNativeIterator) { + CurrentIteratorPrototype = getPrototypeOf$3(anyNativeIterator.call(new Iterable())); + if (IteratorPrototype !== Object.prototype && CurrentIteratorPrototype.next) { + if (getPrototypeOf$3(CurrentIteratorPrototype) !== IteratorPrototype) { + if (setPrototypeOf$6) { + setPrototypeOf$6(CurrentIteratorPrototype, IteratorPrototype); + } else if (typeof CurrentIteratorPrototype[ITERATOR$7] != 'function') { + createNonEnumerableProperty$8(CurrentIteratorPrototype, ITERATOR$7, returnThis); + } + } + // Set @@toStringTag to native iterators + setToStringTag$8(CurrentIteratorPrototype, TO_STRING_TAG, true); + } + } + + // fix Array.prototype.{ values, @@iterator }.name in V8 / FF + if (DEFAULT == VALUES && nativeIterator && nativeIterator.name !== VALUES) { + INCORRECT_VALUES_NAME = true; + defaultIterator = function values() { return nativeIterator.call(this); }; + } + + // define iterator + if (IterablePrototype[ITERATOR$7] !== defaultIterator) { + createNonEnumerableProperty$8(IterablePrototype, ITERATOR$7, defaultIterator); + } + Iterators$3[NAME] = defaultIterator; + + // export additional methods + if (DEFAULT) { + methods = { + values: getIterationMethod(VALUES), + keys: IS_SET ? defaultIterator : getIterationMethod(KEYS), + entries: getIterationMethod(ENTRIES) + }; + if (FORCED) for (KEY in methods) { + if (BUGGY_SAFARI_ITERATORS || INCORRECT_VALUES_NAME || !(KEY in IterablePrototype)) { + redefine$d(IterablePrototype, KEY, methods[KEY]); + } + } else $$15({ target: NAME, proto: true, forced: BUGGY_SAFARI_ITERATORS || INCORRECT_VALUES_NAME }, methods); + } + + return methods; + }; + + var toIndexedObject$7 = toIndexedObject$b; + var addToUnscopables$4 = addToUnscopables$5; + var Iterators$2 = iterators; + var InternalStateModule$8 = internalState; + var defineIterator$2 = defineIterator$3; + + var ARRAY_ITERATOR = 'Array Iterator'; + var setInternalState$8 = InternalStateModule$8.set; + var getInternalState$6 = InternalStateModule$8.getterFor(ARRAY_ITERATOR); + + // `Array.prototype.entries` method + // https://tc39.es/ecma262/#sec-array.prototype.entries + // `Array.prototype.keys` method + // https://tc39.es/ecma262/#sec-array.prototype.keys + // `Array.prototype.values` method + // https://tc39.es/ecma262/#sec-array.prototype.values + // `Array.prototype[@@iterator]` method + // https://tc39.es/ecma262/#sec-array.prototype-@@iterator + // `CreateArrayIterator` internal method + // https://tc39.es/ecma262/#sec-createarrayiterator + var es_array_iterator = defineIterator$2(Array, 'Array', function (iterated, kind) { + setInternalState$8(this, { + type: ARRAY_ITERATOR, + target: toIndexedObject$7(iterated), // target + index: 0, // next index + kind: kind // kind + }); + // `%ArrayIteratorPrototype%.next` method + // https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next + }, function () { + var state = getInternalState$6(this); + var target = state.target; + var kind = state.kind; + var index = state.index++; + if (!target || index >= target.length) { + state.target = undefined; + return { value: undefined, done: true }; + } + if (kind == 'keys') return { value: index, done: false }; + if (kind == 'values') return { value: target[index], done: false }; + return { value: [index, target[index]], done: false }; + }, 'values'); + + // argumentsList[@@iterator] is %ArrayProto_values% + // https://tc39.es/ecma262/#sec-createunmappedargumentsobject + // https://tc39.es/ecma262/#sec-createmappedargumentsobject + Iterators$2.Arguments = Iterators$2.Array; + + // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables + addToUnscopables$4('keys'); + addToUnscopables$4('values'); + addToUnscopables$4('entries'); + + var wellKnownSymbol$m = wellKnownSymbol$s; + + var TO_STRING_TAG$3 = wellKnownSymbol$m('toStringTag'); + var test$2 = {}; + + test$2[TO_STRING_TAG$3] = 'z'; + + var toStringTagSupport = String(test$2) === '[object z]'; + + var TO_STRING_TAG_SUPPORT$2 = toStringTagSupport; + var classofRaw = classofRaw$1; + var wellKnownSymbol$l = wellKnownSymbol$s; + + var TO_STRING_TAG$2 = wellKnownSymbol$l('toStringTag'); + // ES3 wrong here + var CORRECT_ARGUMENTS = classofRaw(function () { return arguments; }()) == 'Arguments'; + + // fallback for IE11 Script Access Denied error + var tryGet = function (it, key) { + try { + return it[key]; + } catch (error) { /* empty */ } + }; + + // getting tag from ES6+ `Object.prototype.toString` + var classof$b = TO_STRING_TAG_SUPPORT$2 ? classofRaw : function (it) { + var O, tag, result; + return it === undefined ? 'Undefined' : it === null ? 'Null' + // @@toStringTag case + : typeof (tag = tryGet(O = Object(it), TO_STRING_TAG$2)) == 'string' ? tag + // builtinTag case + : CORRECT_ARGUMENTS ? classofRaw(O) + // ES3 arguments fallback + : (result = classofRaw(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : result; + }; + + var TO_STRING_TAG_SUPPORT$1 = toStringTagSupport; + var classof$a = classof$b; + + // `Object.prototype.toString` method implementation + // https://tc39.es/ecma262/#sec-object.prototype.tostring + var objectToString$1 = TO_STRING_TAG_SUPPORT$1 ? {}.toString : function toString() { + return '[object ' + classof$a(this) + ']'; + }; + + var TO_STRING_TAG_SUPPORT = toStringTagSupport; + var redefine$c = redefine$g.exports; + var toString$1 = objectToString$1; + + // `Object.prototype.toString` method + // https://tc39.es/ecma262/#sec-object.prototype.tostring + if (!TO_STRING_TAG_SUPPORT) { + redefine$c(Object.prototype, 'toString', toString$1, { unsafe: true }); + } + + var toInteger$8 = toInteger$b; + var requireObjectCoercible$b = requireObjectCoercible$e; + + // `String.prototype.{ codePointAt, at }` methods implementation + var createMethod$5 = function (CONVERT_TO_STRING) { + return function ($this, pos) { + var S = String(requireObjectCoercible$b($this)); + var position = toInteger$8(pos); + var size = S.length; + var first, second; + if (position < 0 || position >= size) return CONVERT_TO_STRING ? '' : undefined; + first = S.charCodeAt(position); + return first < 0xD800 || first > 0xDBFF || position + 1 === size + || (second = S.charCodeAt(position + 1)) < 0xDC00 || second > 0xDFFF + ? CONVERT_TO_STRING ? S.charAt(position) : first + : CONVERT_TO_STRING ? S.slice(position, position + 2) : (first - 0xD800 << 10) + (second - 0xDC00) + 0x10000; + }; + }; + + var stringMultibyte = { + // `String.prototype.codePointAt` method + // https://tc39.es/ecma262/#sec-string.prototype.codepointat + codeAt: createMethod$5(false), + // `String.prototype.at` method + // https://github.com/mathiasbynens/String.prototype.at + charAt: createMethod$5(true) + }; + + var charAt$1 = stringMultibyte.charAt; + var InternalStateModule$7 = internalState; + var defineIterator$1 = defineIterator$3; + + var STRING_ITERATOR = 'String Iterator'; + var setInternalState$7 = InternalStateModule$7.set; + var getInternalState$5 = InternalStateModule$7.getterFor(STRING_ITERATOR); + + // `String.prototype[@@iterator]` method + // https://tc39.es/ecma262/#sec-string.prototype-@@iterator + defineIterator$1(String, 'String', function (iterated) { + setInternalState$7(this, { + type: STRING_ITERATOR, + string: String(iterated), + index: 0 + }); + // `%StringIteratorPrototype%.next` method + // https://tc39.es/ecma262/#sec-%stringiteratorprototype%.next + }, function next() { + var state = getInternalState$5(this); + var string = state.string; + var index = state.index; + var point; + if (index >= string.length) return { value: undefined, done: true }; + point = charAt$1(string, index); + state.index += point.length; + return { value: point, done: false }; + }); + + // iterable DOM collections + // flag - `iterable` interface - 'entries', 'keys', 'values', 'forEach' methods + var domIterables = { + CSSRuleList: 0, + CSSStyleDeclaration: 0, + CSSValueList: 0, + ClientRectList: 0, + DOMRectList: 0, + DOMStringList: 0, + DOMTokenList: 1, + DataTransferItemList: 0, + FileList: 0, + HTMLAllCollection: 0, + HTMLCollection: 0, + HTMLFormElement: 0, + HTMLSelectElement: 0, + MediaList: 0, + MimeTypeArray: 0, + NamedNodeMap: 0, + NodeList: 1, + PaintRequestList: 0, + Plugin: 0, + PluginArray: 0, + SVGLengthList: 0, + SVGNumberList: 0, + SVGPathSegList: 0, + SVGPointList: 0, + SVGStringList: 0, + SVGTransformList: 0, + SourceBufferList: 0, + StyleSheetList: 0, + TextTrackCueList: 0, + TextTrackList: 0, + TouchList: 0 + }; + + var global$t = global$F; + var DOMIterables$1 = domIterables; + var ArrayIteratorMethods = es_array_iterator; + var createNonEnumerableProperty$7 = createNonEnumerableProperty$e; + var wellKnownSymbol$k = wellKnownSymbol$s; + + var ITERATOR$6 = wellKnownSymbol$k('iterator'); + var TO_STRING_TAG$1 = wellKnownSymbol$k('toStringTag'); + var ArrayValues = ArrayIteratorMethods.values; + + for (var COLLECTION_NAME$1 in DOMIterables$1) { + var Collection$1 = global$t[COLLECTION_NAME$1]; + var CollectionPrototype$1 = Collection$1 && Collection$1.prototype; + if (CollectionPrototype$1) { + // some Chrome versions have non-configurable methods on DOMTokenList + if (CollectionPrototype$1[ITERATOR$6] !== ArrayValues) try { + createNonEnumerableProperty$7(CollectionPrototype$1, ITERATOR$6, ArrayValues); + } catch (error) { + CollectionPrototype$1[ITERATOR$6] = ArrayValues; + } + if (!CollectionPrototype$1[TO_STRING_TAG$1]) { + createNonEnumerableProperty$7(CollectionPrototype$1, TO_STRING_TAG$1, COLLECTION_NAME$1); + } + if (DOMIterables$1[COLLECTION_NAME$1]) for (var METHOD_NAME in ArrayIteratorMethods) { + // some Chrome versions have non-configurable methods on DOMTokenList + if (CollectionPrototype$1[METHOD_NAME] !== ArrayIteratorMethods[METHOD_NAME]) try { + createNonEnumerableProperty$7(CollectionPrototype$1, METHOD_NAME, ArrayIteratorMethods[METHOD_NAME]); + } catch (error) { + CollectionPrototype$1[METHOD_NAME] = ArrayIteratorMethods[METHOD_NAME]; + } + } + } + } + + var classof$9 = classofRaw$1; + + // `IsArray` abstract operation + // https://tc39.es/ecma262/#sec-isarray + // eslint-disable-next-line es/no-array-isarray -- safe + var isArray$6 = Array.isArray || function isArray(arg) { + return classof$9(arg) == 'Array'; + }; + + var objectGetOwnPropertyNamesExternal = {}; + + /* eslint-disable es/no-object-getownpropertynames -- safe */ + + var toIndexedObject$6 = toIndexedObject$b; + var $getOwnPropertyNames$1 = objectGetOwnPropertyNames.f; + + var toString = {}.toString; + + var windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames + ? Object.getOwnPropertyNames(window) : []; + + var getWindowNames = function (it) { + try { + return $getOwnPropertyNames$1(it); + } catch (error) { + return windowNames.slice(); + } + }; + + // fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window + objectGetOwnPropertyNamesExternal.f = function getOwnPropertyNames(it) { + return windowNames && toString.call(it) == '[object Window]' + ? getWindowNames(it) + : $getOwnPropertyNames$1(toIndexedObject$6(it)); + }; + + var aFunction$9 = function (it) { + if (typeof it != 'function') { + throw TypeError(String(it) + ' is not a function'); + } return it; + }; + + var aFunction$8 = aFunction$9; + + // optional / simple context binding + var functionBindContext = function (fn, that, length) { + aFunction$8(fn); + if (that === undefined) return fn; + switch (length) { + case 0: return function () { + return fn.call(that); + }; + case 1: return function (a) { + return fn.call(that, a); + }; + case 2: return function (a, b) { + return fn.call(that, a, b); + }; + case 3: return function (a, b, c) { + return fn.call(that, a, b, c); + }; + } + return function (/* ...args */) { + return fn.apply(that, arguments); + }; + }; + + var isObject$l = isObject$r; + var isArray$5 = isArray$6; + var wellKnownSymbol$j = wellKnownSymbol$s; + + var SPECIES$6 = wellKnownSymbol$j('species'); + + // `ArraySpeciesCreate` abstract operation + // https://tc39.es/ecma262/#sec-arrayspeciescreate + var arraySpeciesCreate$3 = function (originalArray, length) { + var C; + if (isArray$5(originalArray)) { + C = originalArray.constructor; + // cross-realm fallback + if (typeof C == 'function' && (C === Array || isArray$5(C.prototype))) C = undefined; + else if (isObject$l(C)) { + C = C[SPECIES$6]; + if (C === null) C = undefined; + } + } return new (C === undefined ? Array : C)(length === 0 ? 0 : length); + }; + + var bind$b = functionBindContext; + var IndexedObject$3 = indexedObject; + var toObject$f = toObject$i; + var toLength$o = toLength$q; + var arraySpeciesCreate$2 = arraySpeciesCreate$3; + + var push = [].push; + + // `Array.prototype.{ forEach, map, filter, some, every, find, findIndex, filterOut }` methods implementation + var createMethod$4 = function (TYPE) { + var IS_MAP = TYPE == 1; + var IS_FILTER = TYPE == 2; + var IS_SOME = TYPE == 3; + var IS_EVERY = TYPE == 4; + var IS_FIND_INDEX = TYPE == 6; + var IS_FILTER_OUT = TYPE == 7; + var NO_HOLES = TYPE == 5 || IS_FIND_INDEX; + return function ($this, callbackfn, that, specificCreate) { + var O = toObject$f($this); + var self = IndexedObject$3(O); + var boundFunction = bind$b(callbackfn, that, 3); + var length = toLength$o(self.length); + var index = 0; + var create = specificCreate || arraySpeciesCreate$2; + var target = IS_MAP ? create($this, length) : IS_FILTER || IS_FILTER_OUT ? create($this, 0) : undefined; + var value, result; + for (;length > index; index++) if (NO_HOLES || index in self) { + value = self[index]; + result = boundFunction(value, index, O); + if (TYPE) { + if (IS_MAP) target[index] = result; // map + else if (result) switch (TYPE) { + case 3: return true; // some + case 5: return value; // find + case 6: return index; // findIndex + case 2: push.call(target, value); // filter + } else switch (TYPE) { + case 4: return false; // every + case 7: push.call(target, value); // filterOut + } + } + } + return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : target; + }; + }; + + var arrayIteration = { + // `Array.prototype.forEach` method + // https://tc39.es/ecma262/#sec-array.prototype.foreach + forEach: createMethod$4(0), + // `Array.prototype.map` method + // https://tc39.es/ecma262/#sec-array.prototype.map + map: createMethod$4(1), + // `Array.prototype.filter` method + // https://tc39.es/ecma262/#sec-array.prototype.filter + filter: createMethod$4(2), + // `Array.prototype.some` method + // https://tc39.es/ecma262/#sec-array.prototype.some + some: createMethod$4(3), + // `Array.prototype.every` method + // https://tc39.es/ecma262/#sec-array.prototype.every + every: createMethod$4(4), + // `Array.prototype.find` method + // https://tc39.es/ecma262/#sec-array.prototype.find + find: createMethod$4(5), + // `Array.prototype.findIndex` method + // https://tc39.es/ecma262/#sec-array.prototype.findIndex + findIndex: createMethod$4(6), + // `Array.prototype.filterOut` method + // https://github.com/tc39/proposal-array-filtering + filterOut: createMethod$4(7) + }; + + var $$14 = _export; + var global$s = global$F; + var getBuiltIn$5 = getBuiltIn$9; + var DESCRIPTORS$h = descriptors; + var NATIVE_SYMBOL = nativeSymbol; + var USE_SYMBOL_AS_UID = useSymbolAsUid; + var fails$F = fails$N; + var has$8 = has$j; + var isArray$4 = isArray$6; + var isObject$k = isObject$r; + var anObject$g = anObject$m; + var toObject$e = toObject$i; + var toIndexedObject$5 = toIndexedObject$b; + var toPrimitive$4 = toPrimitive$7; + var createPropertyDescriptor$3 = createPropertyDescriptor$7; + var nativeObjectCreate = objectCreate; + var objectKeys$2 = objectKeys$4; + var getOwnPropertyNamesModule = objectGetOwnPropertyNames; + var getOwnPropertyNamesExternal = objectGetOwnPropertyNamesExternal; + var getOwnPropertySymbolsModule$1 = objectGetOwnPropertySymbols; + var getOwnPropertyDescriptorModule$2 = objectGetOwnPropertyDescriptor; + var definePropertyModule$3 = objectDefineProperty; + var propertyIsEnumerableModule$1 = objectPropertyIsEnumerable; + var createNonEnumerableProperty$6 = createNonEnumerableProperty$e; + var redefine$b = redefine$g.exports; + var shared$1 = shared$5.exports; + var sharedKey = sharedKey$4; + var hiddenKeys$1 = hiddenKeys$6; + var uid$2 = uid$5; + var wellKnownSymbol$i = wellKnownSymbol$s; + var wrappedWellKnownSymbolModule = wellKnownSymbolWrapped; + var defineWellKnownSymbol$2 = defineWellKnownSymbol$4; + var setToStringTag$7 = setToStringTag$a; + var InternalStateModule$6 = internalState; + var $forEach$2 = arrayIteration.forEach; + + var HIDDEN = sharedKey('hidden'); + var SYMBOL = 'Symbol'; + var PROTOTYPE$1 = 'prototype'; + var TO_PRIMITIVE = wellKnownSymbol$i('toPrimitive'); + var setInternalState$6 = InternalStateModule$6.set; + var getInternalState$4 = InternalStateModule$6.getterFor(SYMBOL); + var ObjectPrototype$2 = Object[PROTOTYPE$1]; + var $Symbol = global$s.Symbol; + var $stringify = getBuiltIn$5('JSON', 'stringify'); + var nativeGetOwnPropertyDescriptor$2 = getOwnPropertyDescriptorModule$2.f; + var nativeDefineProperty$1 = definePropertyModule$3.f; + var nativeGetOwnPropertyNames = getOwnPropertyNamesExternal.f; + var nativePropertyIsEnumerable = propertyIsEnumerableModule$1.f; + var AllSymbols = shared$1('symbols'); + var ObjectPrototypeSymbols = shared$1('op-symbols'); + var StringToSymbolRegistry = shared$1('string-to-symbol-registry'); + var SymbolToStringRegistry = shared$1('symbol-to-string-registry'); + var WellKnownSymbolsStore = shared$1('wks'); + var QObject = global$s.QObject; + // Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173 + var USE_SETTER = !QObject || !QObject[PROTOTYPE$1] || !QObject[PROTOTYPE$1].findChild; + + // fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687 + var setSymbolDescriptor = DESCRIPTORS$h && fails$F(function () { + return nativeObjectCreate(nativeDefineProperty$1({}, 'a', { + get: function () { return nativeDefineProperty$1(this, 'a', { value: 7 }).a; } + })).a != 7; + }) ? function (O, P, Attributes) { + var ObjectPrototypeDescriptor = nativeGetOwnPropertyDescriptor$2(ObjectPrototype$2, P); + if (ObjectPrototypeDescriptor) delete ObjectPrototype$2[P]; + nativeDefineProperty$1(O, P, Attributes); + if (ObjectPrototypeDescriptor && O !== ObjectPrototype$2) { + nativeDefineProperty$1(ObjectPrototype$2, P, ObjectPrototypeDescriptor); + } + } : nativeDefineProperty$1; + + var wrap$2 = function (tag, description) { + var symbol = AllSymbols[tag] = nativeObjectCreate($Symbol[PROTOTYPE$1]); + setInternalState$6(symbol, { + type: SYMBOL, + tag: tag, + description: description + }); + if (!DESCRIPTORS$h) symbol.description = description; + return symbol; + }; + + var isSymbol$1 = USE_SYMBOL_AS_UID ? function (it) { + return typeof it == 'symbol'; + } : function (it) { + return Object(it) instanceof $Symbol; + }; + + var $defineProperty = function defineProperty(O, P, Attributes) { + if (O === ObjectPrototype$2) $defineProperty(ObjectPrototypeSymbols, P, Attributes); + anObject$g(O); + var key = toPrimitive$4(P, true); + anObject$g(Attributes); + if (has$8(AllSymbols, key)) { + if (!Attributes.enumerable) { + if (!has$8(O, HIDDEN)) nativeDefineProperty$1(O, HIDDEN, createPropertyDescriptor$3(1, {})); + O[HIDDEN][key] = true; + } else { + if (has$8(O, HIDDEN) && O[HIDDEN][key]) O[HIDDEN][key] = false; + Attributes = nativeObjectCreate(Attributes, { enumerable: createPropertyDescriptor$3(0, false) }); + } return setSymbolDescriptor(O, key, Attributes); + } return nativeDefineProperty$1(O, key, Attributes); + }; + + var $defineProperties = function defineProperties(O, Properties) { + anObject$g(O); + var properties = toIndexedObject$5(Properties); + var keys = objectKeys$2(properties).concat($getOwnPropertySymbols(properties)); + $forEach$2(keys, function (key) { + if (!DESCRIPTORS$h || $propertyIsEnumerable.call(properties, key)) $defineProperty(O, key, properties[key]); + }); + return O; + }; + + var $create = function create(O, Properties) { + return Properties === undefined ? nativeObjectCreate(O) : $defineProperties(nativeObjectCreate(O), Properties); + }; + + var $propertyIsEnumerable = function propertyIsEnumerable(V) { + var P = toPrimitive$4(V, true); + var enumerable = nativePropertyIsEnumerable.call(this, P); + if (this === ObjectPrototype$2 && has$8(AllSymbols, P) && !has$8(ObjectPrototypeSymbols, P)) return false; + return enumerable || !has$8(this, P) || !has$8(AllSymbols, P) || has$8(this, HIDDEN) && this[HIDDEN][P] ? enumerable : true; + }; + + var $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(O, P) { + var it = toIndexedObject$5(O); + var key = toPrimitive$4(P, true); + if (it === ObjectPrototype$2 && has$8(AllSymbols, key) && !has$8(ObjectPrototypeSymbols, key)) return; + var descriptor = nativeGetOwnPropertyDescriptor$2(it, key); + if (descriptor && has$8(AllSymbols, key) && !(has$8(it, HIDDEN) && it[HIDDEN][key])) { + descriptor.enumerable = true; + } + return descriptor; + }; + + var $getOwnPropertyNames = function getOwnPropertyNames(O) { + var names = nativeGetOwnPropertyNames(toIndexedObject$5(O)); + var result = []; + $forEach$2(names, function (key) { + if (!has$8(AllSymbols, key) && !has$8(hiddenKeys$1, key)) result.push(key); + }); + return result; + }; + + var $getOwnPropertySymbols = function getOwnPropertySymbols(O) { + var IS_OBJECT_PROTOTYPE = O === ObjectPrototype$2; + var names = nativeGetOwnPropertyNames(IS_OBJECT_PROTOTYPE ? ObjectPrototypeSymbols : toIndexedObject$5(O)); + var result = []; + $forEach$2(names, function (key) { + if (has$8(AllSymbols, key) && (!IS_OBJECT_PROTOTYPE || has$8(ObjectPrototype$2, key))) { + result.push(AllSymbols[key]); + } + }); + return result; + }; + + // `Symbol` constructor + // https://tc39.es/ecma262/#sec-symbol-constructor + if (!NATIVE_SYMBOL) { + $Symbol = function Symbol() { + if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor'); + var description = !arguments.length || arguments[0] === undefined ? undefined : String(arguments[0]); + var tag = uid$2(description); + var setter = function (value) { + if (this === ObjectPrototype$2) setter.call(ObjectPrototypeSymbols, value); + if (has$8(this, HIDDEN) && has$8(this[HIDDEN], tag)) this[HIDDEN][tag] = false; + setSymbolDescriptor(this, tag, createPropertyDescriptor$3(1, value)); + }; + if (DESCRIPTORS$h && USE_SETTER) setSymbolDescriptor(ObjectPrototype$2, tag, { configurable: true, set: setter }); + return wrap$2(tag, description); + }; + + redefine$b($Symbol[PROTOTYPE$1], 'toString', function toString() { + return getInternalState$4(this).tag; + }); + + redefine$b($Symbol, 'withoutSetter', function (description) { + return wrap$2(uid$2(description), description); + }); + + propertyIsEnumerableModule$1.f = $propertyIsEnumerable; + definePropertyModule$3.f = $defineProperty; + getOwnPropertyDescriptorModule$2.f = $getOwnPropertyDescriptor; + getOwnPropertyNamesModule.f = getOwnPropertyNamesExternal.f = $getOwnPropertyNames; + getOwnPropertySymbolsModule$1.f = $getOwnPropertySymbols; + + wrappedWellKnownSymbolModule.f = function (name) { + return wrap$2(wellKnownSymbol$i(name), name); + }; + + if (DESCRIPTORS$h) { + // https://github.com/tc39/proposal-Symbol-description + nativeDefineProperty$1($Symbol[PROTOTYPE$1], 'description', { + configurable: true, + get: function description() { + return getInternalState$4(this).description; + } + }); + { + redefine$b(ObjectPrototype$2, 'propertyIsEnumerable', $propertyIsEnumerable, { unsafe: true }); + } + } + } + + $$14({ global: true, wrap: true, forced: !NATIVE_SYMBOL, sham: !NATIVE_SYMBOL }, { + Symbol: $Symbol + }); + + $forEach$2(objectKeys$2(WellKnownSymbolsStore), function (name) { + defineWellKnownSymbol$2(name); + }); + + $$14({ target: SYMBOL, stat: true, forced: !NATIVE_SYMBOL }, { + // `Symbol.for` method + // https://tc39.es/ecma262/#sec-symbol.for + 'for': function (key) { + var string = String(key); + if (has$8(StringToSymbolRegistry, string)) return StringToSymbolRegistry[string]; + var symbol = $Symbol(string); + StringToSymbolRegistry[string] = symbol; + SymbolToStringRegistry[symbol] = string; + return symbol; + }, + // `Symbol.keyFor` method + // https://tc39.es/ecma262/#sec-symbol.keyfor + keyFor: function keyFor(sym) { + if (!isSymbol$1(sym)) throw TypeError(sym + ' is not a symbol'); + if (has$8(SymbolToStringRegistry, sym)) return SymbolToStringRegistry[sym]; + }, + useSetter: function () { USE_SETTER = true; }, + useSimple: function () { USE_SETTER = false; } + }); + + $$14({ target: 'Object', stat: true, forced: !NATIVE_SYMBOL, sham: !DESCRIPTORS$h }, { + // `Object.create` method + // https://tc39.es/ecma262/#sec-object.create + create: $create, + // `Object.defineProperty` method + // https://tc39.es/ecma262/#sec-object.defineproperty + defineProperty: $defineProperty, + // `Object.defineProperties` method + // https://tc39.es/ecma262/#sec-object.defineproperties + defineProperties: $defineProperties, + // `Object.getOwnPropertyDescriptor` method + // https://tc39.es/ecma262/#sec-object.getownpropertydescriptors + getOwnPropertyDescriptor: $getOwnPropertyDescriptor + }); + + $$14({ target: 'Object', stat: true, forced: !NATIVE_SYMBOL }, { + // `Object.getOwnPropertyNames` method + // https://tc39.es/ecma262/#sec-object.getownpropertynames + getOwnPropertyNames: $getOwnPropertyNames, + // `Object.getOwnPropertySymbols` method + // https://tc39.es/ecma262/#sec-object.getownpropertysymbols + getOwnPropertySymbols: $getOwnPropertySymbols + }); + + // Chrome 38 and 39 `Object.getOwnPropertySymbols` fails on primitives + // https://bugs.chromium.org/p/v8/issues/detail?id=3443 + $$14({ target: 'Object', stat: true, forced: fails$F(function () { getOwnPropertySymbolsModule$1.f(1); }) }, { + getOwnPropertySymbols: function getOwnPropertySymbols(it) { + return getOwnPropertySymbolsModule$1.f(toObject$e(it)); + } + }); + + // `JSON.stringify` method behavior with symbols + // https://tc39.es/ecma262/#sec-json.stringify + if ($stringify) { + var FORCED_JSON_STRINGIFY = !NATIVE_SYMBOL || fails$F(function () { + var symbol = $Symbol(); + // MS Edge converts symbol values to JSON as {} + return $stringify([symbol]) != '[null]' + // WebKit converts symbol values to JSON as null + || $stringify({ a: symbol }) != '{}' + // V8 throws on boxed symbols + || $stringify(Object(symbol)) != '{}'; + }); + + $$14({ target: 'JSON', stat: true, forced: FORCED_JSON_STRINGIFY }, { + // eslint-disable-next-line no-unused-vars -- required for `.length` + stringify: function stringify(it, replacer, space) { + var args = [it]; + var index = 1; + var $replacer; + while (arguments.length > index) args.push(arguments[index++]); + $replacer = replacer; + if (!isObject$k(replacer) && it === undefined || isSymbol$1(it)) return; // IE8 returns string on undefined + if (!isArray$4(replacer)) replacer = function (key, value) { + if (typeof $replacer == 'function') value = $replacer.call(this, key, value); + if (!isSymbol$1(value)) return value; + }; + args[1] = replacer; + return $stringify.apply(null, args); + } + }); + } + + // `Symbol.prototype[@@toPrimitive]` method + // https://tc39.es/ecma262/#sec-symbol.prototype-@@toprimitive + if (!$Symbol[PROTOTYPE$1][TO_PRIMITIVE]) { + createNonEnumerableProperty$6($Symbol[PROTOTYPE$1], TO_PRIMITIVE, $Symbol[PROTOTYPE$1].valueOf); + } + // `Symbol.prototype[@@toStringTag]` property + // https://tc39.es/ecma262/#sec-symbol.prototype-@@tostringtag + setToStringTag$7($Symbol, SYMBOL); + + hiddenKeys$1[HIDDEN] = true; + + var $$13 = _export; + var DESCRIPTORS$g = descriptors; + var global$r = global$F; + var has$7 = has$j; + var isObject$j = isObject$r; + var defineProperty$8 = objectDefineProperty.f; + var copyConstructorProperties = copyConstructorProperties$2; + + var NativeSymbol = global$r.Symbol; + + if (DESCRIPTORS$g && typeof NativeSymbol == 'function' && (!('description' in NativeSymbol.prototype) || + // Safari 12 bug + NativeSymbol().description !== undefined + )) { + var EmptyStringDescriptionStore = {}; + // wrap Symbol constructor for correct work with undefined description + var SymbolWrapper = function Symbol() { + var description = arguments.length < 1 || arguments[0] === undefined ? undefined : String(arguments[0]); + var result = this instanceof SymbolWrapper + ? new NativeSymbol(description) + // in Edge 13, String(Symbol(undefined)) === 'Symbol(undefined)' + : description === undefined ? NativeSymbol() : NativeSymbol(description); + if (description === '') EmptyStringDescriptionStore[result] = true; + return result; + }; + copyConstructorProperties(SymbolWrapper, NativeSymbol); + var symbolPrototype = SymbolWrapper.prototype = NativeSymbol.prototype; + symbolPrototype.constructor = SymbolWrapper; + + var symbolToString = symbolPrototype.toString; + var native = String(NativeSymbol('test')) == 'Symbol(test)'; + var regexp = /^Symbol\((.*)\)[^)]+$/; + defineProperty$8(symbolPrototype, 'description', { + configurable: true, + get: function description() { + var symbol = isObject$j(this) ? this.valueOf() : this; + var string = symbolToString.call(symbol); + if (has$7(EmptyStringDescriptionStore, symbol)) return ''; + var desc = native ? string.slice(7, -1) : string.replace(regexp, '$1'); + return desc === '' ? undefined : desc; + } + }); + + $$13({ global: true, forced: true }, { + Symbol: SymbolWrapper + }); + } + + // eslint-disable-next-line es/no-typed-arrays -- safe + var arrayBufferNative = typeof ArrayBuffer !== 'undefined' && typeof DataView !== 'undefined'; + + var redefine$a = redefine$g.exports; + + var redefineAll$4 = function (target, src, options) { + for (var key in src) redefine$a(target, key, src[key], options); + return target; + }; + + var anInstance$7 = function (it, Constructor, name) { + if (!(it instanceof Constructor)) { + throw TypeError('Incorrect ' + (name ? name + ' ' : '') + 'invocation'); + } return it; + }; + + var toInteger$7 = toInteger$b; + var toLength$n = toLength$q; + + // `ToIndex` abstract operation + // https://tc39.es/ecma262/#sec-toindex + var toIndex$2 = function (it) { + if (it === undefined) return 0; + var number = toInteger$7(it); + var length = toLength$n(number); + if (number !== length) throw RangeError('Wrong length or index'); + return length; + }; + + // IEEE754 conversions based on https://github.com/feross/ieee754 + var abs$4 = Math.abs; + var pow$2 = Math.pow; + var floor$6 = Math.floor; + var log$2 = Math.log; + var LN2 = Math.LN2; + + var pack = function (number, mantissaLength, bytes) { + var buffer = new Array(bytes); + var exponentLength = bytes * 8 - mantissaLength - 1; + var eMax = (1 << exponentLength) - 1; + var eBias = eMax >> 1; + var rt = mantissaLength === 23 ? pow$2(2, -24) - pow$2(2, -77) : 0; + var sign = number < 0 || number === 0 && 1 / number < 0 ? 1 : 0; + var index = 0; + var exponent, mantissa, c; + number = abs$4(number); + // eslint-disable-next-line no-self-compare -- NaN check + if (number != number || number === Infinity) { + // eslint-disable-next-line no-self-compare -- NaN check + mantissa = number != number ? 1 : 0; + exponent = eMax; + } else { + exponent = floor$6(log$2(number) / LN2); + if (number * (c = pow$2(2, -exponent)) < 1) { + exponent--; + c *= 2; + } + if (exponent + eBias >= 1) { + number += rt / c; + } else { + number += rt * pow$2(2, 1 - eBias); + } + if (number * c >= 2) { + exponent++; + c /= 2; + } + if (exponent + eBias >= eMax) { + mantissa = 0; + exponent = eMax; + } else if (exponent + eBias >= 1) { + mantissa = (number * c - 1) * pow$2(2, mantissaLength); + exponent = exponent + eBias; + } else { + mantissa = number * pow$2(2, eBias - 1) * pow$2(2, mantissaLength); + exponent = 0; + } + } + for (; mantissaLength >= 8; buffer[index++] = mantissa & 255, mantissa /= 256, mantissaLength -= 8); + exponent = exponent << mantissaLength | mantissa; + exponentLength += mantissaLength; + for (; exponentLength > 0; buffer[index++] = exponent & 255, exponent /= 256, exponentLength -= 8); + buffer[--index] |= sign * 128; + return buffer; + }; + + var unpack = function (buffer, mantissaLength) { + var bytes = buffer.length; + var exponentLength = bytes * 8 - mantissaLength - 1; + var eMax = (1 << exponentLength) - 1; + var eBias = eMax >> 1; + var nBits = exponentLength - 7; + var index = bytes - 1; + var sign = buffer[index--]; + var exponent = sign & 127; + var mantissa; + sign >>= 7; + for (; nBits > 0; exponent = exponent * 256 + buffer[index], index--, nBits -= 8); + mantissa = exponent & (1 << -nBits) - 1; + exponent >>= -nBits; + nBits += mantissaLength; + for (; nBits > 0; mantissa = mantissa * 256 + buffer[index], index--, nBits -= 8); + if (exponent === 0) { + exponent = 1 - eBias; + } else if (exponent === eMax) { + return mantissa ? NaN : sign ? -Infinity : Infinity; + } else { + mantissa = mantissa + pow$2(2, mantissaLength); + exponent = exponent - eBias; + } return (sign ? -1 : 1) * mantissa * pow$2(2, exponent - mantissaLength); + }; + + var ieee754$2 = { + pack: pack, + unpack: unpack + }; + + var toObject$d = toObject$i; + var toAbsoluteIndex$6 = toAbsoluteIndex$8; + var toLength$m = toLength$q; + + // `Array.prototype.fill` method implementation + // https://tc39.es/ecma262/#sec-array.prototype.fill + var arrayFill$1 = function fill(value /* , start = 0, end = @length */) { + var O = toObject$d(this); + var length = toLength$m(O.length); + var argumentsLength = arguments.length; + var index = toAbsoluteIndex$6(argumentsLength > 1 ? arguments[1] : undefined, length); + var end = argumentsLength > 2 ? arguments[2] : undefined; + var endPos = end === undefined ? length : toAbsoluteIndex$6(end, length); + while (endPos > index) O[index++] = value; + return O; + }; + + var global$q = global$F; + var DESCRIPTORS$f = descriptors; + var NATIVE_ARRAY_BUFFER$2 = arrayBufferNative; + var createNonEnumerableProperty$5 = createNonEnumerableProperty$e; + var redefineAll$3 = redefineAll$4; + var fails$E = fails$N; + var anInstance$6 = anInstance$7; + var toInteger$6 = toInteger$b; + var toLength$l = toLength$q; + var toIndex$1 = toIndex$2; + var IEEE754 = ieee754$2; + var getPrototypeOf$2 = objectGetPrototypeOf; + var setPrototypeOf$5 = objectSetPrototypeOf; + var getOwnPropertyNames$4 = objectGetOwnPropertyNames.f; + var defineProperty$7 = objectDefineProperty.f; + var arrayFill = arrayFill$1; + var setToStringTag$6 = setToStringTag$a; + var InternalStateModule$5 = internalState; + + var getInternalState$3 = InternalStateModule$5.get; + var setInternalState$5 = InternalStateModule$5.set; + var ARRAY_BUFFER$1 = 'ArrayBuffer'; + var DATA_VIEW = 'DataView'; + var PROTOTYPE = 'prototype'; + var WRONG_LENGTH$1 = 'Wrong length'; + var WRONG_INDEX = 'Wrong index'; + var NativeArrayBuffer$1 = global$q[ARRAY_BUFFER$1]; + var $ArrayBuffer = NativeArrayBuffer$1; + var $DataView = global$q[DATA_VIEW]; + var $DataViewPrototype = $DataView && $DataView[PROTOTYPE]; + var ObjectPrototype$1 = Object.prototype; + var RangeError$2 = global$q.RangeError; + + var packIEEE754 = IEEE754.pack; + var unpackIEEE754 = IEEE754.unpack; + + var packInt8 = function (number) { + return [number & 0xFF]; + }; + + var packInt16 = function (number) { + return [number & 0xFF, number >> 8 & 0xFF]; + }; + + var packInt32 = function (number) { + return [number & 0xFF, number >> 8 & 0xFF, number >> 16 & 0xFF, number >> 24 & 0xFF]; + }; + + var unpackInt32 = function (buffer) { + return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0]; + }; + + var packFloat32 = function (number) { + return packIEEE754(number, 23, 4); + }; + + var packFloat64 = function (number) { + return packIEEE754(number, 52, 8); + }; + + var addGetter$1 = function (Constructor, key) { + defineProperty$7(Constructor[PROTOTYPE], key, { get: function () { return getInternalState$3(this)[key]; } }); + }; + + var get$4 = function (view, count, index, isLittleEndian) { + var intIndex = toIndex$1(index); + var store = getInternalState$3(view); + if (intIndex + count > store.byteLength) throw RangeError$2(WRONG_INDEX); + var bytes = getInternalState$3(store.buffer).bytes; + var start = intIndex + store.byteOffset; + var pack = bytes.slice(start, start + count); + return isLittleEndian ? pack : pack.reverse(); + }; + + var set$3 = function (view, count, index, conversion, value, isLittleEndian) { + var intIndex = toIndex$1(index); + var store = getInternalState$3(view); + if (intIndex + count > store.byteLength) throw RangeError$2(WRONG_INDEX); + var bytes = getInternalState$3(store.buffer).bytes; + var start = intIndex + store.byteOffset; + var pack = conversion(+value); + for (var i = 0; i < count; i++) bytes[start + i] = pack[isLittleEndian ? i : count - i - 1]; + }; + + if (!NATIVE_ARRAY_BUFFER$2) { + $ArrayBuffer = function ArrayBuffer(length) { + anInstance$6(this, $ArrayBuffer, ARRAY_BUFFER$1); + var byteLength = toIndex$1(length); + setInternalState$5(this, { + bytes: arrayFill.call(new Array(byteLength), 0), + byteLength: byteLength + }); + if (!DESCRIPTORS$f) this.byteLength = byteLength; + }; + + $DataView = function DataView(buffer, byteOffset, byteLength) { + anInstance$6(this, $DataView, DATA_VIEW); + anInstance$6(buffer, $ArrayBuffer, DATA_VIEW); + var bufferLength = getInternalState$3(buffer).byteLength; + var offset = toInteger$6(byteOffset); + if (offset < 0 || offset > bufferLength) throw RangeError$2('Wrong offset'); + byteLength = byteLength === undefined ? bufferLength - offset : toLength$l(byteLength); + if (offset + byteLength > bufferLength) throw RangeError$2(WRONG_LENGTH$1); + setInternalState$5(this, { + buffer: buffer, + byteLength: byteLength, + byteOffset: offset + }); + if (!DESCRIPTORS$f) { + this.buffer = buffer; + this.byteLength = byteLength; + this.byteOffset = offset; + } + }; + + if (DESCRIPTORS$f) { + addGetter$1($ArrayBuffer, 'byteLength'); + addGetter$1($DataView, 'buffer'); + addGetter$1($DataView, 'byteLength'); + addGetter$1($DataView, 'byteOffset'); + } + + redefineAll$3($DataView[PROTOTYPE], { + getInt8: function getInt8(byteOffset) { + return get$4(this, 1, byteOffset)[0] << 24 >> 24; + }, + getUint8: function getUint8(byteOffset) { + return get$4(this, 1, byteOffset)[0]; + }, + getInt16: function getInt16(byteOffset /* , littleEndian */) { + var bytes = get$4(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined); + return (bytes[1] << 8 | bytes[0]) << 16 >> 16; + }, + getUint16: function getUint16(byteOffset /* , littleEndian */) { + var bytes = get$4(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined); + return bytes[1] << 8 | bytes[0]; + }, + getInt32: function getInt32(byteOffset /* , littleEndian */) { + return unpackInt32(get$4(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined)); + }, + getUint32: function getUint32(byteOffset /* , littleEndian */) { + return unpackInt32(get$4(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined)) >>> 0; + }, + getFloat32: function getFloat32(byteOffset /* , littleEndian */) { + return unpackIEEE754(get$4(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 23); + }, + getFloat64: function getFloat64(byteOffset /* , littleEndian */) { + return unpackIEEE754(get$4(this, 8, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 52); + }, + setInt8: function setInt8(byteOffset, value) { + set$3(this, 1, byteOffset, packInt8, value); + }, + setUint8: function setUint8(byteOffset, value) { + set$3(this, 1, byteOffset, packInt8, value); + }, + setInt16: function setInt16(byteOffset, value /* , littleEndian */) { + set$3(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined); + }, + setUint16: function setUint16(byteOffset, value /* , littleEndian */) { + set$3(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined); + }, + setInt32: function setInt32(byteOffset, value /* , littleEndian */) { + set$3(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined); + }, + setUint32: function setUint32(byteOffset, value /* , littleEndian */) { + set$3(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined); + }, + setFloat32: function setFloat32(byteOffset, value /* , littleEndian */) { + set$3(this, 4, byteOffset, packFloat32, value, arguments.length > 2 ? arguments[2] : undefined); + }, + setFloat64: function setFloat64(byteOffset, value /* , littleEndian */) { + set$3(this, 8, byteOffset, packFloat64, value, arguments.length > 2 ? arguments[2] : undefined); + } + }); + } else { + /* eslint-disable no-new -- required for testing */ + if (!fails$E(function () { + NativeArrayBuffer$1(1); + }) || !fails$E(function () { + new NativeArrayBuffer$1(-1); + }) || fails$E(function () { + new NativeArrayBuffer$1(); + new NativeArrayBuffer$1(1.5); + new NativeArrayBuffer$1(NaN); + return NativeArrayBuffer$1.name != ARRAY_BUFFER$1; + })) { + /* eslint-enable no-new -- required for testing */ + $ArrayBuffer = function ArrayBuffer(length) { + anInstance$6(this, $ArrayBuffer); + return new NativeArrayBuffer$1(toIndex$1(length)); + }; + var ArrayBufferPrototype = $ArrayBuffer[PROTOTYPE] = NativeArrayBuffer$1[PROTOTYPE]; + for (var keys$2 = getOwnPropertyNames$4(NativeArrayBuffer$1), j$2 = 0, key$1; keys$2.length > j$2;) { + if (!((key$1 = keys$2[j$2++]) in $ArrayBuffer)) { + createNonEnumerableProperty$5($ArrayBuffer, key$1, NativeArrayBuffer$1[key$1]); + } + } + ArrayBufferPrototype.constructor = $ArrayBuffer; + } + + // WebKit bug - the same parent prototype for typed arrays and data view + if (setPrototypeOf$5 && getPrototypeOf$2($DataViewPrototype) !== ObjectPrototype$1) { + setPrototypeOf$5($DataViewPrototype, ObjectPrototype$1); + } + + // iOS Safari 7.x bug + var testView = new $DataView(new $ArrayBuffer(2)); + var $setInt8 = $DataViewPrototype.setInt8; + testView.setInt8(0, 2147483648); + testView.setInt8(1, 2147483649); + if (testView.getInt8(0) || !testView.getInt8(1)) redefineAll$3($DataViewPrototype, { + setInt8: function setInt8(byteOffset, value) { + $setInt8.call(this, byteOffset, value << 24 >> 24); + }, + setUint8: function setUint8(byteOffset, value) { + $setInt8.call(this, byteOffset, value << 24 >> 24); + } + }, { unsafe: true }); + } + + setToStringTag$6($ArrayBuffer, ARRAY_BUFFER$1); + setToStringTag$6($DataView, DATA_VIEW); + + var arrayBuffer = { + ArrayBuffer: $ArrayBuffer, + DataView: $DataView + }; + + var anObject$f = anObject$m; + var aFunction$7 = aFunction$9; + var wellKnownSymbol$h = wellKnownSymbol$s; + + var SPECIES$5 = wellKnownSymbol$h('species'); + + // `SpeciesConstructor` abstract operation + // https://tc39.es/ecma262/#sec-speciesconstructor + var speciesConstructor$8 = function (O, defaultConstructor) { + var C = anObject$f(O).constructor; + var S; + return C === undefined || (S = anObject$f(C)[SPECIES$5]) == undefined ? defaultConstructor : aFunction$7(S); + }; + + var $$12 = _export; + var fails$D = fails$N; + var ArrayBufferModule$2 = arrayBuffer; + var anObject$e = anObject$m; + var toAbsoluteIndex$5 = toAbsoluteIndex$8; + var toLength$k = toLength$q; + var speciesConstructor$7 = speciesConstructor$8; + + var ArrayBuffer$4 = ArrayBufferModule$2.ArrayBuffer; + var DataView$2 = ArrayBufferModule$2.DataView; + var nativeArrayBufferSlice = ArrayBuffer$4.prototype.slice; + + var INCORRECT_SLICE = fails$D(function () { + return !new ArrayBuffer$4(2).slice(1, undefined).byteLength; + }); + + // `ArrayBuffer.prototype.slice` method + // https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice + $$12({ target: 'ArrayBuffer', proto: true, unsafe: true, forced: INCORRECT_SLICE }, { + slice: function slice(start, end) { + if (nativeArrayBufferSlice !== undefined && end === undefined) { + return nativeArrayBufferSlice.call(anObject$e(this), start); // FF fix + } + var length = anObject$e(this).byteLength; + var first = toAbsoluteIndex$5(start, length); + var fin = toAbsoluteIndex$5(end === undefined ? length : end, length); + var result = new (speciesConstructor$7(this, ArrayBuffer$4))(toLength$k(fin - first)); + var viewSource = new DataView$2(this); + var viewTarget = new DataView$2(result); + var index = 0; + while (first < fin) { + viewTarget.setUint8(index++, viewSource.getUint8(first++)); + } return result; + } + }); + + var $$11 = _export; + var ArrayBufferModule$1 = arrayBuffer; + var NATIVE_ARRAY_BUFFER$1 = arrayBufferNative; + + // `DataView` constructor + // https://tc39.es/ecma262/#sec-dataview-constructor + $$11({ global: true, forced: !NATIVE_ARRAY_BUFFER$1 }, { + DataView: ArrayBufferModule$1.DataView + }); + + var NATIVE_ARRAY_BUFFER = arrayBufferNative; + var DESCRIPTORS$e = descriptors; + var global$p = global$F; + var isObject$i = isObject$r; + var has$6 = has$j; + var classof$8 = classof$b; + var createNonEnumerableProperty$4 = createNonEnumerableProperty$e; + var redefine$9 = redefine$g.exports; + var defineProperty$6 = objectDefineProperty.f; + var getPrototypeOf$1 = objectGetPrototypeOf; + var setPrototypeOf$4 = objectSetPrototypeOf; + var wellKnownSymbol$g = wellKnownSymbol$s; + var uid$1 = uid$5; + + var Int8Array$3 = global$p.Int8Array; + var Int8ArrayPrototype = Int8Array$3 && Int8Array$3.prototype; + var Uint8ClampedArray = global$p.Uint8ClampedArray; + var Uint8ClampedArrayPrototype = Uint8ClampedArray && Uint8ClampedArray.prototype; + var TypedArray$1 = Int8Array$3 && getPrototypeOf$1(Int8Array$3); + var TypedArrayPrototype$1 = Int8ArrayPrototype && getPrototypeOf$1(Int8ArrayPrototype); + var ObjectPrototype = Object.prototype; + var isPrototypeOf = ObjectPrototype.isPrototypeOf; + + var TO_STRING_TAG = wellKnownSymbol$g('toStringTag'); + var TYPED_ARRAY_TAG$1 = uid$1('TYPED_ARRAY_TAG'); + // Fixing native typed arrays in Opera Presto crashes the browser, see #595 + var NATIVE_ARRAY_BUFFER_VIEWS$3 = NATIVE_ARRAY_BUFFER && !!setPrototypeOf$4 && classof$8(global$p.opera) !== 'Opera'; + var TYPED_ARRAY_TAG_REQIRED = false; + var NAME$1; + + var TypedArrayConstructorsList = { + Int8Array: 1, + Uint8Array: 1, + Uint8ClampedArray: 1, + Int16Array: 2, + Uint16Array: 2, + Int32Array: 4, + Uint32Array: 4, + Float32Array: 4, + Float64Array: 8 + }; + + var BigIntArrayConstructorsList = { + BigInt64Array: 8, + BigUint64Array: 8 + }; + + var isView = function isView(it) { + if (!isObject$i(it)) return false; + var klass = classof$8(it); + return klass === 'DataView' + || has$6(TypedArrayConstructorsList, klass) + || has$6(BigIntArrayConstructorsList, klass); + }; + + var isTypedArray$1 = function (it) { + if (!isObject$i(it)) return false; + var klass = classof$8(it); + return has$6(TypedArrayConstructorsList, klass) + || has$6(BigIntArrayConstructorsList, klass); + }; + + var aTypedArray$m = function (it) { + if (isTypedArray$1(it)) return it; + throw TypeError('Target is not a typed array'); + }; + + var aTypedArrayConstructor$5 = function (C) { + if (setPrototypeOf$4) { + if (isPrototypeOf.call(TypedArray$1, C)) return C; + } else for (var ARRAY in TypedArrayConstructorsList) if (has$6(TypedArrayConstructorsList, NAME$1)) { + var TypedArrayConstructor = global$p[ARRAY]; + if (TypedArrayConstructor && (C === TypedArrayConstructor || isPrototypeOf.call(TypedArrayConstructor, C))) { + return C; + } + } throw TypeError('Target is not a typed array constructor'); + }; + + var exportTypedArrayMethod$n = function (KEY, property, forced) { + if (!DESCRIPTORS$e) return; + if (forced) for (var ARRAY in TypedArrayConstructorsList) { + var TypedArrayConstructor = global$p[ARRAY]; + if (TypedArrayConstructor && has$6(TypedArrayConstructor.prototype, KEY)) try { + delete TypedArrayConstructor.prototype[KEY]; + } catch (error) { /* empty */ } + } + if (!TypedArrayPrototype$1[KEY] || forced) { + redefine$9(TypedArrayPrototype$1, KEY, forced ? property + : NATIVE_ARRAY_BUFFER_VIEWS$3 && Int8ArrayPrototype[KEY] || property); + } + }; + + var exportTypedArrayStaticMethod$1 = function (KEY, property, forced) { + var ARRAY, TypedArrayConstructor; + if (!DESCRIPTORS$e) return; + if (setPrototypeOf$4) { + if (forced) for (ARRAY in TypedArrayConstructorsList) { + TypedArrayConstructor = global$p[ARRAY]; + if (TypedArrayConstructor && has$6(TypedArrayConstructor, KEY)) try { + delete TypedArrayConstructor[KEY]; + } catch (error) { /* empty */ } + } + if (!TypedArray$1[KEY] || forced) { + // V8 ~ Chrome 49-50 `%TypedArray%` methods are non-writable non-configurable + try { + return redefine$9(TypedArray$1, KEY, forced ? property : NATIVE_ARRAY_BUFFER_VIEWS$3 && TypedArray$1[KEY] || property); + } catch (error) { /* empty */ } + } else return; + } + for (ARRAY in TypedArrayConstructorsList) { + TypedArrayConstructor = global$p[ARRAY]; + if (TypedArrayConstructor && (!TypedArrayConstructor[KEY] || forced)) { + redefine$9(TypedArrayConstructor, KEY, property); + } + } + }; + + for (NAME$1 in TypedArrayConstructorsList) { + if (!global$p[NAME$1]) NATIVE_ARRAY_BUFFER_VIEWS$3 = false; + } + + // WebKit bug - typed arrays constructors prototype is Object.prototype + if (!NATIVE_ARRAY_BUFFER_VIEWS$3 || typeof TypedArray$1 != 'function' || TypedArray$1 === Function.prototype) { + // eslint-disable-next-line no-shadow -- safe + TypedArray$1 = function TypedArray() { + throw TypeError('Incorrect invocation'); + }; + if (NATIVE_ARRAY_BUFFER_VIEWS$3) for (NAME$1 in TypedArrayConstructorsList) { + if (global$p[NAME$1]) setPrototypeOf$4(global$p[NAME$1], TypedArray$1); + } + } + + if (!NATIVE_ARRAY_BUFFER_VIEWS$3 || !TypedArrayPrototype$1 || TypedArrayPrototype$1 === ObjectPrototype) { + TypedArrayPrototype$1 = TypedArray$1.prototype; + if (NATIVE_ARRAY_BUFFER_VIEWS$3) for (NAME$1 in TypedArrayConstructorsList) { + if (global$p[NAME$1]) setPrototypeOf$4(global$p[NAME$1].prototype, TypedArrayPrototype$1); + } + } + + // WebKit bug - one more object in Uint8ClampedArray prototype chain + if (NATIVE_ARRAY_BUFFER_VIEWS$3 && getPrototypeOf$1(Uint8ClampedArrayPrototype) !== TypedArrayPrototype$1) { + setPrototypeOf$4(Uint8ClampedArrayPrototype, TypedArrayPrototype$1); + } + + if (DESCRIPTORS$e && !has$6(TypedArrayPrototype$1, TO_STRING_TAG)) { + TYPED_ARRAY_TAG_REQIRED = true; + defineProperty$6(TypedArrayPrototype$1, TO_STRING_TAG, { get: function () { + return isObject$i(this) ? this[TYPED_ARRAY_TAG$1] : undefined; + } }); + for (NAME$1 in TypedArrayConstructorsList) if (global$p[NAME$1]) { + createNonEnumerableProperty$4(global$p[NAME$1], TYPED_ARRAY_TAG$1, NAME$1); + } + } + + var arrayBufferViewCore = { + NATIVE_ARRAY_BUFFER_VIEWS: NATIVE_ARRAY_BUFFER_VIEWS$3, + TYPED_ARRAY_TAG: TYPED_ARRAY_TAG_REQIRED && TYPED_ARRAY_TAG$1, + aTypedArray: aTypedArray$m, + aTypedArrayConstructor: aTypedArrayConstructor$5, + exportTypedArrayMethod: exportTypedArrayMethod$n, + exportTypedArrayStaticMethod: exportTypedArrayStaticMethod$1, + isView: isView, + isTypedArray: isTypedArray$1, + TypedArray: TypedArray$1, + TypedArrayPrototype: TypedArrayPrototype$1 + }; + + var $$10 = _export; + var ArrayBufferViewCore$n = arrayBufferViewCore; + + var NATIVE_ARRAY_BUFFER_VIEWS$2 = ArrayBufferViewCore$n.NATIVE_ARRAY_BUFFER_VIEWS; + + // `ArrayBuffer.isView` method + // https://tc39.es/ecma262/#sec-arraybuffer.isview + $$10({ target: 'ArrayBuffer', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS$2 }, { + isView: ArrayBufferViewCore$n.isView + }); + + var getBuiltIn$4 = getBuiltIn$9; + var definePropertyModule$2 = objectDefineProperty; + var wellKnownSymbol$f = wellKnownSymbol$s; + var DESCRIPTORS$d = descriptors; + + var SPECIES$4 = wellKnownSymbol$f('species'); + + var setSpecies$5 = function (CONSTRUCTOR_NAME) { + var Constructor = getBuiltIn$4(CONSTRUCTOR_NAME); + var defineProperty = definePropertyModule$2.f; + + if (DESCRIPTORS$d && Constructor && !Constructor[SPECIES$4]) { + defineProperty(Constructor, SPECIES$4, { + configurable: true, + get: function () { return this; } + }); + } + }; + + var $$$ = _export; + var global$o = global$F; + var arrayBufferModule = arrayBuffer; + var setSpecies$4 = setSpecies$5; + + var ARRAY_BUFFER = 'ArrayBuffer'; + var ArrayBuffer$3 = arrayBufferModule[ARRAY_BUFFER]; + var NativeArrayBuffer = global$o[ARRAY_BUFFER]; + + // `ArrayBuffer` constructor + // https://tc39.es/ecma262/#sec-arraybuffer-constructor + $$$({ global: true, forced: NativeArrayBuffer !== ArrayBuffer$3 }, { + ArrayBuffer: ArrayBuffer$3 + }); + + setSpecies$4(ARRAY_BUFFER); + + var fails$C = fails$N; + + var arrayMethodIsStrict$8 = function (METHOD_NAME, argument) { + var method = [][METHOD_NAME]; + return !!method && fails$C(function () { + // eslint-disable-next-line no-useless-call,no-throw-literal -- required for testing + method.call(null, argument || function () { throw 1; }, 1); + }); + }; + + /* eslint-disable es/no-array-prototype-indexof -- required for testing */ + var $$_ = _export; + var $indexOf$1 = arrayIncludes.indexOf; + var arrayMethodIsStrict$7 = arrayMethodIsStrict$8; + + var nativeIndexOf = [].indexOf; + + var NEGATIVE_ZERO$1 = !!nativeIndexOf && 1 / [1].indexOf(1, -0) < 0; + var STRICT_METHOD$7 = arrayMethodIsStrict$7('indexOf'); + + // `Array.prototype.indexOf` method + // https://tc39.es/ecma262/#sec-array.prototype.indexof + $$_({ target: 'Array', proto: true, forced: NEGATIVE_ZERO$1 || !STRICT_METHOD$7 }, { + indexOf: function indexOf(searchElement /* , fromIndex = 0 */) { + return NEGATIVE_ZERO$1 + // convert -0 to +0 + ? nativeIndexOf.apply(this, arguments) || 0 + : $indexOf$1(this, searchElement, arguments.length > 1 ? arguments[1] : undefined); + } + }); + + var fails$B = fails$N; + var wellKnownSymbol$e = wellKnownSymbol$s; + var V8_VERSION$2 = engineV8Version; + + var SPECIES$3 = wellKnownSymbol$e('species'); + + var arrayMethodHasSpeciesSupport$5 = function (METHOD_NAME) { + // We can't use this feature detection in V8 since it causes + // deoptimization and serious performance degradation + // https://github.com/zloirock/core-js/issues/677 + return V8_VERSION$2 >= 51 || !fails$B(function () { + var array = []; + var constructor = array.constructor = {}; + constructor[SPECIES$3] = function () { + return { foo: 1 }; + }; + return array[METHOD_NAME](Boolean).foo !== 1; + }); + }; + + var $$Z = _export; + var $map$1 = arrayIteration.map; + var arrayMethodHasSpeciesSupport$4 = arrayMethodHasSpeciesSupport$5; + + var HAS_SPECIES_SUPPORT$3 = arrayMethodHasSpeciesSupport$4('map'); + + // `Array.prototype.map` method + // https://tc39.es/ecma262/#sec-array.prototype.map + // with adding support of @@species + $$Z({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$3 }, { + map: function map(callbackfn /* , thisArg */) { + return $map$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); + } + }); + + var $forEach$1 = arrayIteration.forEach; + var arrayMethodIsStrict$6 = arrayMethodIsStrict$8; + + var STRICT_METHOD$6 = arrayMethodIsStrict$6('forEach'); + + // `Array.prototype.forEach` method implementation + // https://tc39.es/ecma262/#sec-array.prototype.foreach + var arrayForEach = !STRICT_METHOD$6 ? function forEach(callbackfn /* , thisArg */) { + return $forEach$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); + // eslint-disable-next-line es/no-array-prototype-foreach -- safe + } : [].forEach; + + var $$Y = _export; + var forEach$3 = arrayForEach; + + // `Array.prototype.forEach` method + // https://tc39.es/ecma262/#sec-array.prototype.foreach + // eslint-disable-next-line es/no-array-prototype-foreach -- safe + $$Y({ target: 'Array', proto: true, forced: [].forEach != forEach$3 }, { + forEach: forEach$3 + }); + + var global$n = global$F; + var DOMIterables = domIterables; + var forEach$2 = arrayForEach; + var createNonEnumerableProperty$3 = createNonEnumerableProperty$e; + + for (var COLLECTION_NAME in DOMIterables) { + var Collection = global$n[COLLECTION_NAME]; + var CollectionPrototype = Collection && Collection.prototype; + // some Chrome versions have non-configurable methods on DOMTokenList + if (CollectionPrototype && CollectionPrototype.forEach !== forEach$2) try { + createNonEnumerableProperty$3(CollectionPrototype, 'forEach', forEach$2); + } catch (error) { + CollectionPrototype.forEach = forEach$2; + } + } + + var $$X = _export; + var isArray$3 = isArray$6; + + // `Array.isArray` method + // https://tc39.es/ecma262/#sec-array.isarray + $$X({ target: 'Array', stat: true }, { + isArray: isArray$3 + }); + + var $$W = _export; + var fails$A = fails$N; + var getOwnPropertyNames$3 = objectGetOwnPropertyNamesExternal.f; + + // eslint-disable-next-line es/no-object-getownpropertynames -- required for testing + var FAILS_ON_PRIMITIVES$4 = fails$A(function () { return !Object.getOwnPropertyNames(1); }); + + // `Object.getOwnPropertyNames` method + // https://tc39.es/ecma262/#sec-object.getownpropertynames + $$W({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$4 }, { + getOwnPropertyNames: getOwnPropertyNames$3 + }); + + var global$m = global$F; + + var nativePromiseConstructor = global$m.Promise; + + var wellKnownSymbol$d = wellKnownSymbol$s; + var Iterators$1 = iterators; + + var ITERATOR$5 = wellKnownSymbol$d('iterator'); + var ArrayPrototype = Array.prototype; + + // check on default Array iterator + var isArrayIteratorMethod$3 = function (it) { + return it !== undefined && (Iterators$1.Array === it || ArrayPrototype[ITERATOR$5] === it); + }; + + var classof$7 = classof$b; + var Iterators = iterators; + var wellKnownSymbol$c = wellKnownSymbol$s; + + var ITERATOR$4 = wellKnownSymbol$c('iterator'); + + var getIteratorMethod$5 = function (it) { + if (it != undefined) return it[ITERATOR$4] + || it['@@iterator'] + || Iterators[classof$7(it)]; + }; + + var anObject$d = anObject$m; + + var iteratorClose$2 = function (iterator) { + var returnMethod = iterator['return']; + if (returnMethod !== undefined) { + return anObject$d(returnMethod.call(iterator)).value; + } + }; + + var anObject$c = anObject$m; + var isArrayIteratorMethod$2 = isArrayIteratorMethod$3; + var toLength$j = toLength$q; + var bind$a = functionBindContext; + var getIteratorMethod$4 = getIteratorMethod$5; + var iteratorClose$1 = iteratorClose$2; + + var Result = function (stopped, result) { + this.stopped = stopped; + this.result = result; + }; + + var iterate$3 = function (iterable, unboundFunction, options) { + var that = options && options.that; + var AS_ENTRIES = !!(options && options.AS_ENTRIES); + var IS_ITERATOR = !!(options && options.IS_ITERATOR); + var INTERRUPTED = !!(options && options.INTERRUPTED); + var fn = bind$a(unboundFunction, that, 1 + AS_ENTRIES + INTERRUPTED); + var iterator, iterFn, index, length, result, next, step; + + var stop = function (condition) { + if (iterator) iteratorClose$1(iterator); + return new Result(true, condition); + }; + + var callFn = function (value) { + if (AS_ENTRIES) { + anObject$c(value); + return INTERRUPTED ? fn(value[0], value[1], stop) : fn(value[0], value[1]); + } return INTERRUPTED ? fn(value, stop) : fn(value); + }; + + if (IS_ITERATOR) { + iterator = iterable; + } else { + iterFn = getIteratorMethod$4(iterable); + if (typeof iterFn != 'function') throw TypeError('Target is not iterable'); + // optimisation for array iterators + if (isArrayIteratorMethod$2(iterFn)) { + for (index = 0, length = toLength$j(iterable.length); length > index; index++) { + result = callFn(iterable[index]); + if (result && result instanceof Result) return result; + } return new Result(false); + } + iterator = iterFn.call(iterable); + } + + next = iterator.next; + while (!(step = next.call(iterator)).done) { + try { + result = callFn(step.value); + } catch (error) { + iteratorClose$1(iterator); + throw error; + } + if (typeof result == 'object' && result && result instanceof Result) return result; + } return new Result(false); + }; + + var wellKnownSymbol$b = wellKnownSymbol$s; + + var ITERATOR$3 = wellKnownSymbol$b('iterator'); + var SAFE_CLOSING = false; + + try { + var called = 0; + var iteratorWithReturn = { + next: function () { + return { done: !!called++ }; + }, + 'return': function () { + SAFE_CLOSING = true; + } + }; + iteratorWithReturn[ITERATOR$3] = function () { + return this; + }; + // eslint-disable-next-line es/no-array-from, no-throw-literal -- required for testing + Array.from(iteratorWithReturn, function () { throw 2; }); + } catch (error) { /* empty */ } + + var checkCorrectnessOfIteration$4 = function (exec, SKIP_CLOSING) { + if (!SKIP_CLOSING && !SAFE_CLOSING) return false; + var ITERATION_SUPPORT = false; + try { + var object = {}; + object[ITERATOR$3] = function () { + return { + next: function () { + return { done: ITERATION_SUPPORT = true }; + } + }; + }; + exec(object); + } catch (error) { /* empty */ } + return ITERATION_SUPPORT; + }; + + var userAgent$4 = engineUserAgent; + + var engineIsIos = /(?:iphone|ipod|ipad).*applewebkit/i.test(userAgent$4); + + var classof$6 = classofRaw$1; + var global$l = global$F; + + var engineIsNode = classof$6(global$l.process) == 'process'; + + var global$k = global$F; + var fails$z = fails$N; + var bind$9 = functionBindContext; + var html = html$2; + var createElement = documentCreateElement$1; + var IS_IOS$1 = engineIsIos; + var IS_NODE$3 = engineIsNode; + + var location$1 = global$k.location; + var set$2 = global$k.setImmediate; + var clear = global$k.clearImmediate; + var process$3 = global$k.process; + var MessageChannel = global$k.MessageChannel; + var Dispatch$1 = global$k.Dispatch; + var counter = 0; + var queue = {}; + var ONREADYSTATECHANGE = 'onreadystatechange'; + var defer, channel, port; + + var run = function (id) { + // eslint-disable-next-line no-prototype-builtins -- safe + if (queue.hasOwnProperty(id)) { + var fn = queue[id]; + delete queue[id]; + fn(); + } + }; + + var runner = function (id) { + return function () { + run(id); + }; + }; + + var listener = function (event) { + run(event.data); + }; + + var post = function (id) { + // old engines have not location.origin + global$k.postMessage(id + '', location$1.protocol + '//' + location$1.host); + }; + + // Node.js 0.9+ & IE10+ has setImmediate, otherwise: + if (!set$2 || !clear) { + set$2 = function setImmediate(fn) { + var args = []; + var i = 1; + while (arguments.length > i) args.push(arguments[i++]); + queue[++counter] = function () { + // eslint-disable-next-line no-new-func -- spec requirement + (typeof fn == 'function' ? fn : Function(fn)).apply(undefined, args); + }; + defer(counter); + return counter; + }; + clear = function clearImmediate(id) { + delete queue[id]; + }; + // Node.js 0.8- + if (IS_NODE$3) { + defer = function (id) { + process$3.nextTick(runner(id)); + }; + // Sphere (JS game engine) Dispatch API + } else if (Dispatch$1 && Dispatch$1.now) { + defer = function (id) { + Dispatch$1.now(runner(id)); + }; + // Browsers with MessageChannel, includes WebWorkers + // except iOS - https://github.com/zloirock/core-js/issues/624 + } else if (MessageChannel && !IS_IOS$1) { + channel = new MessageChannel(); + port = channel.port2; + channel.port1.onmessage = listener; + defer = bind$9(port.postMessage, port, 1); + // Browsers with postMessage, skip WebWorkers + // IE8 has postMessage, but it's sync & typeof its postMessage is 'object' + } else if ( + global$k.addEventListener && + typeof postMessage == 'function' && + !global$k.importScripts && + location$1 && location$1.protocol !== 'file:' && + !fails$z(post) + ) { + defer = post; + global$k.addEventListener('message', listener, false); + // IE8- + } else if (ONREADYSTATECHANGE in createElement('script')) { + defer = function (id) { + html.appendChild(createElement('script'))[ONREADYSTATECHANGE] = function () { + html.removeChild(this); + run(id); + }; + }; + // Rest old browsers + } else { + defer = function (id) { + setTimeout(runner(id), 0); + }; + } + } + + var task$1 = { + set: set$2, + clear: clear + }; + + var userAgent$3 = engineUserAgent; + + var engineIsWebosWebkit = /web0s(?!.*chrome)/i.test(userAgent$3); + + var global$j = global$F; + var getOwnPropertyDescriptor$3 = objectGetOwnPropertyDescriptor.f; + var macrotask = task$1.set; + var IS_IOS = engineIsIos; + var IS_WEBOS_WEBKIT = engineIsWebosWebkit; + var IS_NODE$2 = engineIsNode; + + var MutationObserver = global$j.MutationObserver || global$j.WebKitMutationObserver; + var document$2 = global$j.document; + var process$2 = global$j.process; + var Promise$1 = global$j.Promise; + // Node.js 11 shows ExperimentalWarning on getting `queueMicrotask` + var queueMicrotaskDescriptor = getOwnPropertyDescriptor$3(global$j, 'queueMicrotask'); + var queueMicrotask = queueMicrotaskDescriptor && queueMicrotaskDescriptor.value; + + var flush, head, last, notify$1, toggle, node, promise, then; + + // modern engines have queueMicrotask method + if (!queueMicrotask) { + flush = function () { + var parent, fn; + if (IS_NODE$2 && (parent = process$2.domain)) parent.exit(); + while (head) { + fn = head.fn; + head = head.next; + try { + fn(); + } catch (error) { + if (head) notify$1(); + else last = undefined; + throw error; + } + } last = undefined; + if (parent) parent.enter(); + }; + + // browsers with MutationObserver, except iOS - https://github.com/zloirock/core-js/issues/339 + // also except WebOS Webkit https://github.com/zloirock/core-js/issues/898 + if (!IS_IOS && !IS_NODE$2 && !IS_WEBOS_WEBKIT && MutationObserver && document$2) { + toggle = true; + node = document$2.createTextNode(''); + new MutationObserver(flush).observe(node, { characterData: true }); + notify$1 = function () { + node.data = toggle = !toggle; + }; + // environments with maybe non-completely correct, but existent Promise + } else if (Promise$1 && Promise$1.resolve) { + // Promise.resolve without an argument throws an error in LG WebOS 2 + promise = Promise$1.resolve(undefined); + // workaround of WebKit ~ iOS Safari 10.1 bug + promise.constructor = Promise$1; + then = promise.then; + notify$1 = function () { + then.call(promise, flush); + }; + // Node.js without promises + } else if (IS_NODE$2) { + notify$1 = function () { + process$2.nextTick(flush); + }; + // for other environments - macrotask based on: + // - setImmediate + // - MessageChannel + // - window.postMessag + // - onreadystatechange + // - setTimeout + } else { + notify$1 = function () { + // strange IE + webpack dev server bug - use .call(global) + macrotask.call(global$j, flush); + }; + } + } + + var microtask$1 = queueMicrotask || function (fn) { + var task = { fn: fn, next: undefined }; + if (last) last.next = task; + if (!head) { + head = task; + notify$1(); + } last = task; + }; + + var newPromiseCapability$2 = {}; + + var aFunction$6 = aFunction$9; + + var PromiseCapability = function (C) { + var resolve, reject; + this.promise = new C(function ($$resolve, $$reject) { + if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor'); + resolve = $$resolve; + reject = $$reject; + }); + this.resolve = aFunction$6(resolve); + this.reject = aFunction$6(reject); + }; + + // `NewPromiseCapability` abstract operation + // https://tc39.es/ecma262/#sec-newpromisecapability + newPromiseCapability$2.f = function (C) { + return new PromiseCapability(C); + }; + + var anObject$b = anObject$m; + var isObject$h = isObject$r; + var newPromiseCapability$1 = newPromiseCapability$2; + + var promiseResolve$2 = function (C, x) { + anObject$b(C); + if (isObject$h(x) && x.constructor === C) return x; + var promiseCapability = newPromiseCapability$1.f(C); + var resolve = promiseCapability.resolve; + resolve(x); + return promiseCapability.promise; + }; + + var global$i = global$F; + + var hostReportErrors$1 = function (a, b) { + var console = global$i.console; + if (console && console.error) { + arguments.length === 1 ? console.error(a) : console.error(a, b); + } + }; + + var perform$1 = function (exec) { + try { + return { error: false, value: exec() }; + } catch (error) { + return { error: true, value: error }; + } + }; + + var engineIsBrowser = typeof window == 'object'; + + var $$V = _export; + var global$h = global$F; + var getBuiltIn$3 = getBuiltIn$9; + var NativePromise$1 = nativePromiseConstructor; + var redefine$8 = redefine$g.exports; + var redefineAll$2 = redefineAll$4; + var setPrototypeOf$3 = objectSetPrototypeOf; + var setToStringTag$5 = setToStringTag$a; + var setSpecies$3 = setSpecies$5; + var isObject$g = isObject$r; + var aFunction$5 = aFunction$9; + var anInstance$5 = anInstance$7; + var inspectSource = inspectSource$3; + var iterate$2 = iterate$3; + var checkCorrectnessOfIteration$3 = checkCorrectnessOfIteration$4; + var speciesConstructor$6 = speciesConstructor$8; + var task = task$1.set; + var microtask = microtask$1; + var promiseResolve$1 = promiseResolve$2; + var hostReportErrors = hostReportErrors$1; + var newPromiseCapabilityModule = newPromiseCapability$2; + var perform = perform$1; + var InternalStateModule$4 = internalState; + var isForced$3 = isForced_1; + var wellKnownSymbol$a = wellKnownSymbol$s; + var IS_BROWSER = engineIsBrowser; + var IS_NODE$1 = engineIsNode; + var V8_VERSION$1 = engineV8Version; + + var SPECIES$2 = wellKnownSymbol$a('species'); + var PROMISE = 'Promise'; + var getInternalState$2 = InternalStateModule$4.get; + var setInternalState$4 = InternalStateModule$4.set; + var getInternalPromiseState = InternalStateModule$4.getterFor(PROMISE); + var NativePromisePrototype = NativePromise$1 && NativePromise$1.prototype; + var PromiseConstructor = NativePromise$1; + var PromiseConstructorPrototype = NativePromisePrototype; + var TypeError$1 = global$h.TypeError; + var document$1 = global$h.document; + var process$1 = global$h.process; + var newPromiseCapability = newPromiseCapabilityModule.f; + var newGenericPromiseCapability = newPromiseCapability; + var DISPATCH_EVENT = !!(document$1 && document$1.createEvent && global$h.dispatchEvent); + var NATIVE_REJECTION_EVENT = typeof PromiseRejectionEvent == 'function'; + var UNHANDLED_REJECTION = 'unhandledrejection'; + var REJECTION_HANDLED = 'rejectionhandled'; + var PENDING = 0; + var FULFILLED = 1; + var REJECTED = 2; + var HANDLED = 1; + var UNHANDLED = 2; + var SUBCLASSING = false; + var Internal, OwnPromiseCapability, PromiseWrapper, nativeThen; + + var FORCED$f = isForced$3(PROMISE, function () { + var GLOBAL_CORE_JS_PROMISE = inspectSource(PromiseConstructor) !== String(PromiseConstructor); + // V8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables + // https://bugs.chromium.org/p/chromium/issues/detail?id=830565 + // We can't detect it synchronously, so just check versions + if (!GLOBAL_CORE_JS_PROMISE && V8_VERSION$1 === 66) return true; + // We can't use @@species feature detection in V8 since it causes + // deoptimization and performance degradation + // https://github.com/zloirock/core-js/issues/679 + if (V8_VERSION$1 >= 51 && /native code/.test(PromiseConstructor)) return false; + // Detect correctness of subclassing with @@species support + var promise = new PromiseConstructor(function (resolve) { resolve(1); }); + var FakePromise = function (exec) { + exec(function () { /* empty */ }, function () { /* empty */ }); + }; + var constructor = promise.constructor = {}; + constructor[SPECIES$2] = FakePromise; + SUBCLASSING = promise.then(function () { /* empty */ }) instanceof FakePromise; + if (!SUBCLASSING) return true; + // Unhandled rejections tracking support, NodeJS Promise without it fails @@species test + return !GLOBAL_CORE_JS_PROMISE && IS_BROWSER && !NATIVE_REJECTION_EVENT; + }); + + var INCORRECT_ITERATION$1 = FORCED$f || !checkCorrectnessOfIteration$3(function (iterable) { + PromiseConstructor.all(iterable)['catch'](function () { /* empty */ }); + }); + + // helpers + var isThenable = function (it) { + var then; + return isObject$g(it) && typeof (then = it.then) == 'function' ? then : false; + }; + + var notify = function (state, isReject) { + if (state.notified) return; + state.notified = true; + var chain = state.reactions; + microtask(function () { + var value = state.value; + var ok = state.state == FULFILLED; + var index = 0; + // variable length - can't use forEach + while (chain.length > index) { + var reaction = chain[index++]; + var handler = ok ? reaction.ok : reaction.fail; + var resolve = reaction.resolve; + var reject = reaction.reject; + var domain = reaction.domain; + var result, then, exited; + try { + if (handler) { + if (!ok) { + if (state.rejection === UNHANDLED) onHandleUnhandled(state); + state.rejection = HANDLED; + } + if (handler === true) result = value; + else { + if (domain) domain.enter(); + result = handler(value); // can throw + if (domain) { + domain.exit(); + exited = true; + } + } + if (result === reaction.promise) { + reject(TypeError$1('Promise-chain cycle')); + } else if (then = isThenable(result)) { + then.call(result, resolve, reject); + } else resolve(result); + } else reject(value); + } catch (error) { + if (domain && !exited) domain.exit(); + reject(error); + } + } + state.reactions = []; + state.notified = false; + if (isReject && !state.rejection) onUnhandled(state); + }); + }; + + var dispatchEvent$1 = function (name, promise, reason) { + var event, handler; + if (DISPATCH_EVENT) { + event = document$1.createEvent('Event'); + event.promise = promise; + event.reason = reason; + event.initEvent(name, false, true); + global$h.dispatchEvent(event); + } else event = { promise: promise, reason: reason }; + if (!NATIVE_REJECTION_EVENT && (handler = global$h['on' + name])) handler(event); + else if (name === UNHANDLED_REJECTION) hostReportErrors('Unhandled promise rejection', reason); + }; + + var onUnhandled = function (state) { + task.call(global$h, function () { + var promise = state.facade; + var value = state.value; + var IS_UNHANDLED = isUnhandled(state); + var result; + if (IS_UNHANDLED) { + result = perform(function () { + if (IS_NODE$1) { + process$1.emit('unhandledRejection', value, promise); + } else dispatchEvent$1(UNHANDLED_REJECTION, promise, value); + }); + // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should + state.rejection = IS_NODE$1 || isUnhandled(state) ? UNHANDLED : HANDLED; + if (result.error) throw result.value; + } + }); + }; + + var isUnhandled = function (state) { + return state.rejection !== HANDLED && !state.parent; + }; + + var onHandleUnhandled = function (state) { + task.call(global$h, function () { + var promise = state.facade; + if (IS_NODE$1) { + process$1.emit('rejectionHandled', promise); + } else dispatchEvent$1(REJECTION_HANDLED, promise, state.value); + }); + }; + + var bind$8 = function (fn, state, unwrap) { + return function (value) { + fn(state, value, unwrap); + }; + }; + + var internalReject = function (state, value, unwrap) { + if (state.done) return; + state.done = true; + if (unwrap) state = unwrap; + state.value = value; + state.state = REJECTED; + notify(state, true); + }; + + var internalResolve = function (state, value, unwrap) { + if (state.done) return; + state.done = true; + if (unwrap) state = unwrap; + try { + if (state.facade === value) throw TypeError$1("Promise can't be resolved itself"); + var then = isThenable(value); + if (then) { + microtask(function () { + var wrapper = { done: false }; + try { + then.call(value, + bind$8(internalResolve, wrapper, state), + bind$8(internalReject, wrapper, state) + ); + } catch (error) { + internalReject(wrapper, error, state); + } + }); + } else { + state.value = value; + state.state = FULFILLED; + notify(state, false); + } + } catch (error) { + internalReject({ done: false }, error, state); + } + }; + + // constructor polyfill + if (FORCED$f) { + // 25.4.3.1 Promise(executor) + PromiseConstructor = function Promise(executor) { + anInstance$5(this, PromiseConstructor, PROMISE); + aFunction$5(executor); + Internal.call(this); + var state = getInternalState$2(this); + try { + executor(bind$8(internalResolve, state), bind$8(internalReject, state)); + } catch (error) { + internalReject(state, error); + } + }; + PromiseConstructorPrototype = PromiseConstructor.prototype; + // eslint-disable-next-line no-unused-vars -- required for `.length` + Internal = function Promise(executor) { + setInternalState$4(this, { + type: PROMISE, + done: false, + notified: false, + parent: false, + reactions: [], + rejection: false, + state: PENDING, + value: undefined + }); + }; + Internal.prototype = redefineAll$2(PromiseConstructorPrototype, { + // `Promise.prototype.then` method + // https://tc39.es/ecma262/#sec-promise.prototype.then + then: function then(onFulfilled, onRejected) { + var state = getInternalPromiseState(this); + var reaction = newPromiseCapability(speciesConstructor$6(this, PromiseConstructor)); + reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true; + reaction.fail = typeof onRejected == 'function' && onRejected; + reaction.domain = IS_NODE$1 ? process$1.domain : undefined; + state.parent = true; + state.reactions.push(reaction); + if (state.state != PENDING) notify(state, false); + return reaction.promise; + }, + // `Promise.prototype.catch` method + // https://tc39.es/ecma262/#sec-promise.prototype.catch + 'catch': function (onRejected) { + return this.then(undefined, onRejected); + } + }); + OwnPromiseCapability = function () { + var promise = new Internal(); + var state = getInternalState$2(promise); + this.promise = promise; + this.resolve = bind$8(internalResolve, state); + this.reject = bind$8(internalReject, state); + }; + newPromiseCapabilityModule.f = newPromiseCapability = function (C) { + return C === PromiseConstructor || C === PromiseWrapper + ? new OwnPromiseCapability(C) + : newGenericPromiseCapability(C); + }; + + if (typeof NativePromise$1 == 'function' && NativePromisePrototype !== Object.prototype) { + nativeThen = NativePromisePrototype.then; + + if (!SUBCLASSING) { + // make `Promise#then` return a polyfilled `Promise` for native promise-based APIs + redefine$8(NativePromisePrototype, 'then', function then(onFulfilled, onRejected) { + var that = this; + return new PromiseConstructor(function (resolve, reject) { + nativeThen.call(that, resolve, reject); + }).then(onFulfilled, onRejected); + // https://github.com/zloirock/core-js/issues/640 + }, { unsafe: true }); + + // makes sure that native promise-based APIs `Promise#catch` properly works with patched `Promise#then` + redefine$8(NativePromisePrototype, 'catch', PromiseConstructorPrototype['catch'], { unsafe: true }); + } + + // make `.constructor === Promise` work for native promise-based APIs + try { + delete NativePromisePrototype.constructor; + } catch (error) { /* empty */ } + + // make `instanceof Promise` work for native promise-based APIs + if (setPrototypeOf$3) { + setPrototypeOf$3(NativePromisePrototype, PromiseConstructorPrototype); + } + } + } + + $$V({ global: true, wrap: true, forced: FORCED$f }, { + Promise: PromiseConstructor + }); + + setToStringTag$5(PromiseConstructor, PROMISE, false); + setSpecies$3(PROMISE); + + PromiseWrapper = getBuiltIn$3(PROMISE); + + // statics + $$V({ target: PROMISE, stat: true, forced: FORCED$f }, { + // `Promise.reject` method + // https://tc39.es/ecma262/#sec-promise.reject + reject: function reject(r) { + var capability = newPromiseCapability(this); + capability.reject.call(undefined, r); + return capability.promise; + } + }); + + $$V({ target: PROMISE, stat: true, forced: FORCED$f }, { + // `Promise.resolve` method + // https://tc39.es/ecma262/#sec-promise.resolve + resolve: function resolve(x) { + return promiseResolve$1(this, x); + } + }); + + $$V({ target: PROMISE, stat: true, forced: INCORRECT_ITERATION$1 }, { + // `Promise.all` method + // https://tc39.es/ecma262/#sec-promise.all + all: function all(iterable) { + var C = this; + var capability = newPromiseCapability(C); + var resolve = capability.resolve; + var reject = capability.reject; + var result = perform(function () { + var $promiseResolve = aFunction$5(C.resolve); + var values = []; + var counter = 0; + var remaining = 1; + iterate$2(iterable, function (promise) { + var index = counter++; + var alreadyCalled = false; + values.push(undefined); + remaining++; + $promiseResolve.call(C, promise).then(function (value) { + if (alreadyCalled) return; + alreadyCalled = true; + values[index] = value; + --remaining || resolve(values); + }, reject); + }); + --remaining || resolve(values); + }); + if (result.error) reject(result.value); + return capability.promise; + }, + // `Promise.race` method + // https://tc39.es/ecma262/#sec-promise.race + race: function race(iterable) { + var C = this; + var capability = newPromiseCapability(C); + var reject = capability.reject; + var result = perform(function () { + var $promiseResolve = aFunction$5(C.resolve); + iterate$2(iterable, function (promise) { + $promiseResolve.call(C, promise).then(capability.resolve, reject); + }); + }); + if (result.error) reject(result.value); + return capability.promise; + } + }); + + var typedArrayConstructor = {exports: {}}; + + /* eslint-disable no-new -- required for testing */ + + var global$g = global$F; + var fails$y = fails$N; + var checkCorrectnessOfIteration$2 = checkCorrectnessOfIteration$4; + var NATIVE_ARRAY_BUFFER_VIEWS$1 = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS; + + var ArrayBuffer$2 = global$g.ArrayBuffer; + var Int8Array$2 = global$g.Int8Array; + + var typedArrayConstructorsRequireWrappers = !NATIVE_ARRAY_BUFFER_VIEWS$1 || !fails$y(function () { + Int8Array$2(1); + }) || !fails$y(function () { + new Int8Array$2(-1); + }) || !checkCorrectnessOfIteration$2(function (iterable) { + new Int8Array$2(); + new Int8Array$2(null); + new Int8Array$2(1.5); + new Int8Array$2(iterable); + }, true) || fails$y(function () { + // Safari (11+) bug - a reason why even Safari 13 should load a typed array polyfill + return new Int8Array$2(new ArrayBuffer$2(2), 1, undefined).length !== 1; + }); + + var toInteger$5 = toInteger$b; + + var toPositiveInteger$1 = function (it) { + var result = toInteger$5(it); + if (result < 0) throw RangeError("The argument can't be less than 0"); + return result; + }; + + var toPositiveInteger = toPositiveInteger$1; + + var toOffset$2 = function (it, BYTES) { + var offset = toPositiveInteger(it); + if (offset % BYTES) throw RangeError('Wrong offset'); + return offset; + }; + + var toObject$c = toObject$i; + var toLength$i = toLength$q; + var getIteratorMethod$3 = getIteratorMethod$5; + var isArrayIteratorMethod$1 = isArrayIteratorMethod$3; + var bind$7 = functionBindContext; + var aTypedArrayConstructor$4 = arrayBufferViewCore.aTypedArrayConstructor; + + var typedArrayFrom$2 = function from(source /* , mapfn, thisArg */) { + var O = toObject$c(source); + var argumentsLength = arguments.length; + var mapfn = argumentsLength > 1 ? arguments[1] : undefined; + var mapping = mapfn !== undefined; + var iteratorMethod = getIteratorMethod$3(O); + var i, length, result, step, iterator, next; + if (iteratorMethod != undefined && !isArrayIteratorMethod$1(iteratorMethod)) { + iterator = iteratorMethod.call(O); + next = iterator.next; + O = []; + while (!(step = next.call(iterator)).done) { + O.push(step.value); + } + } + if (mapping && argumentsLength > 2) { + mapfn = bind$7(mapfn, arguments[2], 2); + } + length = toLength$i(O.length); + result = new (aTypedArrayConstructor$4(this))(length); + for (i = 0; length > i; i++) { + result[i] = mapping ? mapfn(O[i], i) : O[i]; + } + return result; + }; + + var isObject$f = isObject$r; + var setPrototypeOf$2 = objectSetPrototypeOf; + + // makes subclassing work correct for wrapped built-ins + var inheritIfRequired$4 = function ($this, dummy, Wrapper) { + var NewTarget, NewTargetPrototype; + if ( + // it can work only with native `setPrototypeOf` + setPrototypeOf$2 && + // we haven't completely correct pre-ES6 way for getting `new.target`, so use this + typeof (NewTarget = dummy.constructor) == 'function' && + NewTarget !== Wrapper && + isObject$f(NewTargetPrototype = NewTarget.prototype) && + NewTargetPrototype !== Wrapper.prototype + ) setPrototypeOf$2($this, NewTargetPrototype); + return $this; + }; + + var $$U = _export; + var global$f = global$F; + var DESCRIPTORS$c = descriptors; + var TYPED_ARRAYS_CONSTRUCTORS_REQUIRES_WRAPPERS$1 = typedArrayConstructorsRequireWrappers; + var ArrayBufferViewCore$m = arrayBufferViewCore; + var ArrayBufferModule = arrayBuffer; + var anInstance$4 = anInstance$7; + var createPropertyDescriptor$2 = createPropertyDescriptor$7; + var createNonEnumerableProperty$2 = createNonEnumerableProperty$e; + var toLength$h = toLength$q; + var toIndex = toIndex$2; + var toOffset$1 = toOffset$2; + var toPrimitive$3 = toPrimitive$7; + var has$5 = has$j; + var classof$5 = classof$b; + var isObject$e = isObject$r; + var create$9 = objectCreate; + var setPrototypeOf$1 = objectSetPrototypeOf; + var getOwnPropertyNames$2 = objectGetOwnPropertyNames.f; + var typedArrayFrom$1 = typedArrayFrom$2; + var forEach$1 = arrayIteration.forEach; + var setSpecies$2 = setSpecies$5; + var definePropertyModule$1 = objectDefineProperty; + var getOwnPropertyDescriptorModule$1 = objectGetOwnPropertyDescriptor; + var InternalStateModule$3 = internalState; + var inheritIfRequired$3 = inheritIfRequired$4; + + var getInternalState$1 = InternalStateModule$3.get; + var setInternalState$3 = InternalStateModule$3.set; + var nativeDefineProperty = definePropertyModule$1.f; + var nativeGetOwnPropertyDescriptor$1 = getOwnPropertyDescriptorModule$1.f; + var round = Math.round; + var RangeError$1 = global$f.RangeError; + var ArrayBuffer$1 = ArrayBufferModule.ArrayBuffer; + var DataView$1 = ArrayBufferModule.DataView; + var NATIVE_ARRAY_BUFFER_VIEWS = ArrayBufferViewCore$m.NATIVE_ARRAY_BUFFER_VIEWS; + var TYPED_ARRAY_TAG = ArrayBufferViewCore$m.TYPED_ARRAY_TAG; + var TypedArray = ArrayBufferViewCore$m.TypedArray; + var TypedArrayPrototype = ArrayBufferViewCore$m.TypedArrayPrototype; + var aTypedArrayConstructor$3 = ArrayBufferViewCore$m.aTypedArrayConstructor; + var isTypedArray = ArrayBufferViewCore$m.isTypedArray; + var BYTES_PER_ELEMENT = 'BYTES_PER_ELEMENT'; + var WRONG_LENGTH = 'Wrong length'; + + var fromList = function (C, list) { + var index = 0; + var length = list.length; + var result = new (aTypedArrayConstructor$3(C))(length); + while (length > index) result[index] = list[index++]; + return result; + }; + + var addGetter = function (it, key) { + nativeDefineProperty(it, key, { get: function () { + return getInternalState$1(this)[key]; + } }); + }; + + var isArrayBuffer = function (it) { + var klass; + return it instanceof ArrayBuffer$1 || (klass = classof$5(it)) == 'ArrayBuffer' || klass == 'SharedArrayBuffer'; + }; + + var isTypedArrayIndex = function (target, key) { + return isTypedArray(target) + && typeof key != 'symbol' + && key in target + && String(+key) == String(key); + }; + + var wrappedGetOwnPropertyDescriptor = function getOwnPropertyDescriptor(target, key) { + return isTypedArrayIndex(target, key = toPrimitive$3(key, true)) + ? createPropertyDescriptor$2(2, target[key]) + : nativeGetOwnPropertyDescriptor$1(target, key); + }; + + var wrappedDefineProperty = function defineProperty(target, key, descriptor) { + if (isTypedArrayIndex(target, key = toPrimitive$3(key, true)) + && isObject$e(descriptor) + && has$5(descriptor, 'value') + && !has$5(descriptor, 'get') + && !has$5(descriptor, 'set') + // TODO: add validation descriptor w/o calling accessors + && !descriptor.configurable + && (!has$5(descriptor, 'writable') || descriptor.writable) + && (!has$5(descriptor, 'enumerable') || descriptor.enumerable) + ) { + target[key] = descriptor.value; + return target; + } return nativeDefineProperty(target, key, descriptor); + }; + + if (DESCRIPTORS$c) { + if (!NATIVE_ARRAY_BUFFER_VIEWS) { + getOwnPropertyDescriptorModule$1.f = wrappedGetOwnPropertyDescriptor; + definePropertyModule$1.f = wrappedDefineProperty; + addGetter(TypedArrayPrototype, 'buffer'); + addGetter(TypedArrayPrototype, 'byteOffset'); + addGetter(TypedArrayPrototype, 'byteLength'); + addGetter(TypedArrayPrototype, 'length'); + } + + $$U({ target: 'Object', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS }, { + getOwnPropertyDescriptor: wrappedGetOwnPropertyDescriptor, + defineProperty: wrappedDefineProperty + }); + + typedArrayConstructor.exports = function (TYPE, wrapper, CLAMPED) { + var BYTES = TYPE.match(/\d+$/)[0] / 8; + var CONSTRUCTOR_NAME = TYPE + (CLAMPED ? 'Clamped' : '') + 'Array'; + var GETTER = 'get' + TYPE; + var SETTER = 'set' + TYPE; + var NativeTypedArrayConstructor = global$f[CONSTRUCTOR_NAME]; + var TypedArrayConstructor = NativeTypedArrayConstructor; + var TypedArrayConstructorPrototype = TypedArrayConstructor && TypedArrayConstructor.prototype; + var exported = {}; + + var getter = function (that, index) { + var data = getInternalState$1(that); + return data.view[GETTER](index * BYTES + data.byteOffset, true); + }; + + var setter = function (that, index, value) { + var data = getInternalState$1(that); + if (CLAMPED) value = (value = round(value)) < 0 ? 0 : value > 0xFF ? 0xFF : value & 0xFF; + data.view[SETTER](index * BYTES + data.byteOffset, value, true); + }; + + var addElement = function (that, index) { + nativeDefineProperty(that, index, { + get: function () { + return getter(this, index); + }, + set: function (value) { + return setter(this, index, value); + }, + enumerable: true + }); + }; + + if (!NATIVE_ARRAY_BUFFER_VIEWS) { + TypedArrayConstructor = wrapper(function (that, data, offset, $length) { + anInstance$4(that, TypedArrayConstructor, CONSTRUCTOR_NAME); + var index = 0; + var byteOffset = 0; + var buffer, byteLength, length; + if (!isObject$e(data)) { + length = toIndex(data); + byteLength = length * BYTES; + buffer = new ArrayBuffer$1(byteLength); + } else if (isArrayBuffer(data)) { + buffer = data; + byteOffset = toOffset$1(offset, BYTES); + var $len = data.byteLength; + if ($length === undefined) { + if ($len % BYTES) throw RangeError$1(WRONG_LENGTH); + byteLength = $len - byteOffset; + if (byteLength < 0) throw RangeError$1(WRONG_LENGTH); + } else { + byteLength = toLength$h($length) * BYTES; + if (byteLength + byteOffset > $len) throw RangeError$1(WRONG_LENGTH); + } + length = byteLength / BYTES; + } else if (isTypedArray(data)) { + return fromList(TypedArrayConstructor, data); + } else { + return typedArrayFrom$1.call(TypedArrayConstructor, data); + } + setInternalState$3(that, { + buffer: buffer, + byteOffset: byteOffset, + byteLength: byteLength, + length: length, + view: new DataView$1(buffer) + }); + while (index < length) addElement(that, index++); + }); + + if (setPrototypeOf$1) setPrototypeOf$1(TypedArrayConstructor, TypedArray); + TypedArrayConstructorPrototype = TypedArrayConstructor.prototype = create$9(TypedArrayPrototype); + } else if (TYPED_ARRAYS_CONSTRUCTORS_REQUIRES_WRAPPERS$1) { + TypedArrayConstructor = wrapper(function (dummy, data, typedArrayOffset, $length) { + anInstance$4(dummy, TypedArrayConstructor, CONSTRUCTOR_NAME); + return inheritIfRequired$3(function () { + if (!isObject$e(data)) return new NativeTypedArrayConstructor(toIndex(data)); + if (isArrayBuffer(data)) return $length !== undefined + ? new NativeTypedArrayConstructor(data, toOffset$1(typedArrayOffset, BYTES), $length) + : typedArrayOffset !== undefined + ? new NativeTypedArrayConstructor(data, toOffset$1(typedArrayOffset, BYTES)) + : new NativeTypedArrayConstructor(data); + if (isTypedArray(data)) return fromList(TypedArrayConstructor, data); + return typedArrayFrom$1.call(TypedArrayConstructor, data); + }(), dummy, TypedArrayConstructor); + }); + + if (setPrototypeOf$1) setPrototypeOf$1(TypedArrayConstructor, TypedArray); + forEach$1(getOwnPropertyNames$2(NativeTypedArrayConstructor), function (key) { + if (!(key in TypedArrayConstructor)) { + createNonEnumerableProperty$2(TypedArrayConstructor, key, NativeTypedArrayConstructor[key]); + } + }); + TypedArrayConstructor.prototype = TypedArrayConstructorPrototype; + } + + if (TypedArrayConstructorPrototype.constructor !== TypedArrayConstructor) { + createNonEnumerableProperty$2(TypedArrayConstructorPrototype, 'constructor', TypedArrayConstructor); + } + + if (TYPED_ARRAY_TAG) { + createNonEnumerableProperty$2(TypedArrayConstructorPrototype, TYPED_ARRAY_TAG, CONSTRUCTOR_NAME); + } + + exported[CONSTRUCTOR_NAME] = TypedArrayConstructor; + + $$U({ + global: true, forced: TypedArrayConstructor != NativeTypedArrayConstructor, sham: !NATIVE_ARRAY_BUFFER_VIEWS + }, exported); + + if (!(BYTES_PER_ELEMENT in TypedArrayConstructor)) { + createNonEnumerableProperty$2(TypedArrayConstructor, BYTES_PER_ELEMENT, BYTES); + } + + if (!(BYTES_PER_ELEMENT in TypedArrayConstructorPrototype)) { + createNonEnumerableProperty$2(TypedArrayConstructorPrototype, BYTES_PER_ELEMENT, BYTES); + } + + setSpecies$2(CONSTRUCTOR_NAME); + }; + } else typedArrayConstructor.exports = function () { /* empty */ }; + + var createTypedArrayConstructor$1 = typedArrayConstructor.exports; + + // `Uint8Array` constructor + // https://tc39.es/ecma262/#sec-typedarray-objects + createTypedArrayConstructor$1('Uint8', function (init) { + return function Uint8Array(data, byteOffset, length) { + return init(this, data, byteOffset, length); + }; + }); + + var toObject$b = toObject$i; + var toAbsoluteIndex$4 = toAbsoluteIndex$8; + var toLength$g = toLength$q; + + var min$7 = Math.min; + + // `Array.prototype.copyWithin` method implementation + // https://tc39.es/ecma262/#sec-array.prototype.copywithin + // eslint-disable-next-line es/no-array-prototype-copywithin -- safe + var arrayCopyWithin = [].copyWithin || function copyWithin(target /* = 0 */, start /* = 0, end = @length */) { + var O = toObject$b(this); + var len = toLength$g(O.length); + var to = toAbsoluteIndex$4(target, len); + var from = toAbsoluteIndex$4(start, len); + var end = arguments.length > 2 ? arguments[2] : undefined; + var count = min$7((end === undefined ? len : toAbsoluteIndex$4(end, len)) - from, len - to); + var inc = 1; + if (from < to && to < from + count) { + inc = -1; + from += count - 1; + to += count - 1; + } + while (count-- > 0) { + if (from in O) O[to] = O[from]; + else delete O[to]; + to += inc; + from += inc; + } return O; + }; + + var ArrayBufferViewCore$l = arrayBufferViewCore; + var $copyWithin = arrayCopyWithin; + + var aTypedArray$l = ArrayBufferViewCore$l.aTypedArray; + var exportTypedArrayMethod$m = ArrayBufferViewCore$l.exportTypedArrayMethod; + + // `%TypedArray%.prototype.copyWithin` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.copywithin + exportTypedArrayMethod$m('copyWithin', function copyWithin(target, start /* , end */) { + return $copyWithin.call(aTypedArray$l(this), target, start, arguments.length > 2 ? arguments[2] : undefined); + }); + + var ArrayBufferViewCore$k = arrayBufferViewCore; + var $every$1 = arrayIteration.every; + + var aTypedArray$k = ArrayBufferViewCore$k.aTypedArray; + var exportTypedArrayMethod$l = ArrayBufferViewCore$k.exportTypedArrayMethod; + + // `%TypedArray%.prototype.every` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.every + exportTypedArrayMethod$l('every', function every(callbackfn /* , thisArg */) { + return $every$1(aTypedArray$k(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined); + }); + + var ArrayBufferViewCore$j = arrayBufferViewCore; + var $fill = arrayFill$1; + + var aTypedArray$j = ArrayBufferViewCore$j.aTypedArray; + var exportTypedArrayMethod$k = ArrayBufferViewCore$j.exportTypedArrayMethod; + + // `%TypedArray%.prototype.fill` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.fill + // eslint-disable-next-line no-unused-vars -- required for `.length` + exportTypedArrayMethod$k('fill', function fill(value /* , start, end */) { + return $fill.apply(aTypedArray$j(this), arguments); + }); + + var aTypedArrayConstructor$2 = arrayBufferViewCore.aTypedArrayConstructor; + var speciesConstructor$5 = speciesConstructor$8; + + var typedArrayFromSpeciesAndList = function (instance, list) { + var C = speciesConstructor$5(instance, instance.constructor); + var index = 0; + var length = list.length; + var result = new (aTypedArrayConstructor$2(C))(length); + while (length > index) result[index] = list[index++]; + return result; + }; + + var ArrayBufferViewCore$i = arrayBufferViewCore; + var $filter$1 = arrayIteration.filter; + var fromSpeciesAndList = typedArrayFromSpeciesAndList; + + var aTypedArray$i = ArrayBufferViewCore$i.aTypedArray; + var exportTypedArrayMethod$j = ArrayBufferViewCore$i.exportTypedArrayMethod; + + // `%TypedArray%.prototype.filter` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.filter + exportTypedArrayMethod$j('filter', function filter(callbackfn /* , thisArg */) { + var list = $filter$1(aTypedArray$i(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined); + return fromSpeciesAndList(this, list); + }); + + var ArrayBufferViewCore$h = arrayBufferViewCore; + var $find$1 = arrayIteration.find; + + var aTypedArray$h = ArrayBufferViewCore$h.aTypedArray; + var exportTypedArrayMethod$i = ArrayBufferViewCore$h.exportTypedArrayMethod; + + // `%TypedArray%.prototype.find` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.find + exportTypedArrayMethod$i('find', function find(predicate /* , thisArg */) { + return $find$1(aTypedArray$h(this), predicate, arguments.length > 1 ? arguments[1] : undefined); + }); + + var ArrayBufferViewCore$g = arrayBufferViewCore; + var $findIndex$1 = arrayIteration.findIndex; + + var aTypedArray$g = ArrayBufferViewCore$g.aTypedArray; + var exportTypedArrayMethod$h = ArrayBufferViewCore$g.exportTypedArrayMethod; + + // `%TypedArray%.prototype.findIndex` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.findindex + exportTypedArrayMethod$h('findIndex', function findIndex(predicate /* , thisArg */) { + return $findIndex$1(aTypedArray$g(this), predicate, arguments.length > 1 ? arguments[1] : undefined); + }); + + var ArrayBufferViewCore$f = arrayBufferViewCore; + var $forEach = arrayIteration.forEach; + + var aTypedArray$f = ArrayBufferViewCore$f.aTypedArray; + var exportTypedArrayMethod$g = ArrayBufferViewCore$f.exportTypedArrayMethod; + + // `%TypedArray%.prototype.forEach` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.foreach + exportTypedArrayMethod$g('forEach', function forEach(callbackfn /* , thisArg */) { + $forEach(aTypedArray$f(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined); + }); + + var ArrayBufferViewCore$e = arrayBufferViewCore; + var $includes$1 = arrayIncludes.includes; + + var aTypedArray$e = ArrayBufferViewCore$e.aTypedArray; + var exportTypedArrayMethod$f = ArrayBufferViewCore$e.exportTypedArrayMethod; + + // `%TypedArray%.prototype.includes` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.includes + exportTypedArrayMethod$f('includes', function includes(searchElement /* , fromIndex */) { + return $includes$1(aTypedArray$e(this), searchElement, arguments.length > 1 ? arguments[1] : undefined); + }); + + var ArrayBufferViewCore$d = arrayBufferViewCore; + var $indexOf = arrayIncludes.indexOf; + + var aTypedArray$d = ArrayBufferViewCore$d.aTypedArray; + var exportTypedArrayMethod$e = ArrayBufferViewCore$d.exportTypedArrayMethod; + + // `%TypedArray%.prototype.indexOf` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.indexof + exportTypedArrayMethod$e('indexOf', function indexOf(searchElement /* , fromIndex */) { + return $indexOf(aTypedArray$d(this), searchElement, arguments.length > 1 ? arguments[1] : undefined); + }); + + var global$e = global$F; + var ArrayBufferViewCore$c = arrayBufferViewCore; + var ArrayIterators = es_array_iterator; + var wellKnownSymbol$9 = wellKnownSymbol$s; + + var ITERATOR$2 = wellKnownSymbol$9('iterator'); + var Uint8Array$2 = global$e.Uint8Array; + var arrayValues = ArrayIterators.values; + var arrayKeys = ArrayIterators.keys; + var arrayEntries = ArrayIterators.entries; + var aTypedArray$c = ArrayBufferViewCore$c.aTypedArray; + var exportTypedArrayMethod$d = ArrayBufferViewCore$c.exportTypedArrayMethod; + var nativeTypedArrayIterator = Uint8Array$2 && Uint8Array$2.prototype[ITERATOR$2]; + + var CORRECT_ITER_NAME = !!nativeTypedArrayIterator + && (nativeTypedArrayIterator.name == 'values' || nativeTypedArrayIterator.name == undefined); + + var typedArrayValues = function values() { + return arrayValues.call(aTypedArray$c(this)); + }; + + // `%TypedArray%.prototype.entries` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.entries + exportTypedArrayMethod$d('entries', function entries() { + return arrayEntries.call(aTypedArray$c(this)); + }); + // `%TypedArray%.prototype.keys` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.keys + exportTypedArrayMethod$d('keys', function keys() { + return arrayKeys.call(aTypedArray$c(this)); + }); + // `%TypedArray%.prototype.values` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.values + exportTypedArrayMethod$d('values', typedArrayValues, !CORRECT_ITER_NAME); + // `%TypedArray%.prototype[@@iterator]` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype-@@iterator + exportTypedArrayMethod$d(ITERATOR$2, typedArrayValues, !CORRECT_ITER_NAME); + + var ArrayBufferViewCore$b = arrayBufferViewCore; + + var aTypedArray$b = ArrayBufferViewCore$b.aTypedArray; + var exportTypedArrayMethod$c = ArrayBufferViewCore$b.exportTypedArrayMethod; + var $join = [].join; + + // `%TypedArray%.prototype.join` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.join + // eslint-disable-next-line no-unused-vars -- required for `.length` + exportTypedArrayMethod$c('join', function join(separator) { + return $join.apply(aTypedArray$b(this), arguments); + }); + + /* eslint-disable es/no-array-prototype-lastindexof -- safe */ + var toIndexedObject$4 = toIndexedObject$b; + var toInteger$4 = toInteger$b; + var toLength$f = toLength$q; + var arrayMethodIsStrict$5 = arrayMethodIsStrict$8; + + var min$6 = Math.min; + var $lastIndexOf$1 = [].lastIndexOf; + var NEGATIVE_ZERO = !!$lastIndexOf$1 && 1 / [1].lastIndexOf(1, -0) < 0; + var STRICT_METHOD$5 = arrayMethodIsStrict$5('lastIndexOf'); + var FORCED$e = NEGATIVE_ZERO || !STRICT_METHOD$5; + + // `Array.prototype.lastIndexOf` method implementation + // https://tc39.es/ecma262/#sec-array.prototype.lastindexof + var arrayLastIndexOf = FORCED$e ? function lastIndexOf(searchElement /* , fromIndex = @[*-1] */) { + // convert -0 to +0 + if (NEGATIVE_ZERO) return $lastIndexOf$1.apply(this, arguments) || 0; + var O = toIndexedObject$4(this); + var length = toLength$f(O.length); + var index = length - 1; + if (arguments.length > 1) index = min$6(index, toInteger$4(arguments[1])); + if (index < 0) index = length + index; + for (;index >= 0; index--) if (index in O && O[index] === searchElement) return index || 0; + return -1; + } : $lastIndexOf$1; + + var ArrayBufferViewCore$a = arrayBufferViewCore; + var $lastIndexOf = arrayLastIndexOf; + + var aTypedArray$a = ArrayBufferViewCore$a.aTypedArray; + var exportTypedArrayMethod$b = ArrayBufferViewCore$a.exportTypedArrayMethod; + + // `%TypedArray%.prototype.lastIndexOf` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.lastindexof + // eslint-disable-next-line no-unused-vars -- required for `.length` + exportTypedArrayMethod$b('lastIndexOf', function lastIndexOf(searchElement /* , fromIndex */) { + return $lastIndexOf.apply(aTypedArray$a(this), arguments); + }); + + var ArrayBufferViewCore$9 = arrayBufferViewCore; + var $map = arrayIteration.map; + var speciesConstructor$4 = speciesConstructor$8; + + var aTypedArray$9 = ArrayBufferViewCore$9.aTypedArray; + var aTypedArrayConstructor$1 = ArrayBufferViewCore$9.aTypedArrayConstructor; + var exportTypedArrayMethod$a = ArrayBufferViewCore$9.exportTypedArrayMethod; + + // `%TypedArray%.prototype.map` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.map + exportTypedArrayMethod$a('map', function map(mapfn /* , thisArg */) { + return $map(aTypedArray$9(this), mapfn, arguments.length > 1 ? arguments[1] : undefined, function (O, length) { + return new (aTypedArrayConstructor$1(speciesConstructor$4(O, O.constructor)))(length); + }); + }); + + var aFunction$4 = aFunction$9; + var toObject$a = toObject$i; + var IndexedObject$2 = indexedObject; + var toLength$e = toLength$q; + + // `Array.prototype.{ reduce, reduceRight }` methods implementation + var createMethod$3 = function (IS_RIGHT) { + return function (that, callbackfn, argumentsLength, memo) { + aFunction$4(callbackfn); + var O = toObject$a(that); + var self = IndexedObject$2(O); + var length = toLength$e(O.length); + var index = IS_RIGHT ? length - 1 : 0; + var i = IS_RIGHT ? -1 : 1; + if (argumentsLength < 2) while (true) { + if (index in self) { + memo = self[index]; + index += i; + break; + } + index += i; + if (IS_RIGHT ? index < 0 : length <= index) { + throw TypeError('Reduce of empty array with no initial value'); + } + } + for (;IS_RIGHT ? index >= 0 : length > index; index += i) if (index in self) { + memo = callbackfn(memo, self[index], index, O); + } + return memo; + }; + }; + + var arrayReduce = { + // `Array.prototype.reduce` method + // https://tc39.es/ecma262/#sec-array.prototype.reduce + left: createMethod$3(false), + // `Array.prototype.reduceRight` method + // https://tc39.es/ecma262/#sec-array.prototype.reduceright + right: createMethod$3(true) + }; + + var ArrayBufferViewCore$8 = arrayBufferViewCore; + var $reduce$1 = arrayReduce.left; + + var aTypedArray$8 = ArrayBufferViewCore$8.aTypedArray; + var exportTypedArrayMethod$9 = ArrayBufferViewCore$8.exportTypedArrayMethod; + + // `%TypedArray%.prototype.reduce` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduce + exportTypedArrayMethod$9('reduce', function reduce(callbackfn /* , initialValue */) { + return $reduce$1(aTypedArray$8(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined); + }); + + var ArrayBufferViewCore$7 = arrayBufferViewCore; + var $reduceRight = arrayReduce.right; + + var aTypedArray$7 = ArrayBufferViewCore$7.aTypedArray; + var exportTypedArrayMethod$8 = ArrayBufferViewCore$7.exportTypedArrayMethod; + + // `%TypedArray%.prototype.reduceRicht` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduceright + exportTypedArrayMethod$8('reduceRight', function reduceRight(callbackfn /* , initialValue */) { + return $reduceRight(aTypedArray$7(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined); + }); + + var ArrayBufferViewCore$6 = arrayBufferViewCore; + + var aTypedArray$6 = ArrayBufferViewCore$6.aTypedArray; + var exportTypedArrayMethod$7 = ArrayBufferViewCore$6.exportTypedArrayMethod; + var floor$5 = Math.floor; + + // `%TypedArray%.prototype.reverse` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reverse + exportTypedArrayMethod$7('reverse', function reverse() { + var that = this; + var length = aTypedArray$6(that).length; + var middle = floor$5(length / 2); + var index = 0; + var value; + while (index < middle) { + value = that[index]; + that[index++] = that[--length]; + that[length] = value; + } return that; + }); + + var ArrayBufferViewCore$5 = arrayBufferViewCore; + var toLength$d = toLength$q; + var toOffset = toOffset$2; + var toObject$9 = toObject$i; + var fails$x = fails$N; + + var aTypedArray$5 = ArrayBufferViewCore$5.aTypedArray; + var exportTypedArrayMethod$6 = ArrayBufferViewCore$5.exportTypedArrayMethod; + + var FORCED$d = fails$x(function () { + // eslint-disable-next-line es/no-typed-arrays -- required for testing + new Int8Array(1).set({}); + }); + + // `%TypedArray%.prototype.set` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.set + exportTypedArrayMethod$6('set', function set(arrayLike /* , offset */) { + aTypedArray$5(this); + var offset = toOffset(arguments.length > 1 ? arguments[1] : undefined, 1); + var length = this.length; + var src = toObject$9(arrayLike); + var len = toLength$d(src.length); + var index = 0; + if (len + offset > length) throw RangeError('Wrong length'); + while (index < len) this[offset + index] = src[index++]; + }, FORCED$d); + + var ArrayBufferViewCore$4 = arrayBufferViewCore; + var speciesConstructor$3 = speciesConstructor$8; + var fails$w = fails$N; + + var aTypedArray$4 = ArrayBufferViewCore$4.aTypedArray; + var aTypedArrayConstructor = ArrayBufferViewCore$4.aTypedArrayConstructor; + var exportTypedArrayMethod$5 = ArrayBufferViewCore$4.exportTypedArrayMethod; + var $slice$1 = [].slice; + + var FORCED$c = fails$w(function () { + // eslint-disable-next-line es/no-typed-arrays -- required for testing + new Int8Array(1).slice(); + }); + + // `%TypedArray%.prototype.slice` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.slice + exportTypedArrayMethod$5('slice', function slice(start, end) { + var list = $slice$1.call(aTypedArray$4(this), start, end); + var C = speciesConstructor$3(this, this.constructor); + var index = 0; + var length = list.length; + var result = new (aTypedArrayConstructor(C))(length); + while (length > index) result[index] = list[index++]; + return result; + }, FORCED$c); + + var ArrayBufferViewCore$3 = arrayBufferViewCore; + var $some$1 = arrayIteration.some; + + var aTypedArray$3 = ArrayBufferViewCore$3.aTypedArray; + var exportTypedArrayMethod$4 = ArrayBufferViewCore$3.exportTypedArrayMethod; + + // `%TypedArray%.prototype.some` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.some + exportTypedArrayMethod$4('some', function some(callbackfn /* , thisArg */) { + return $some$1(aTypedArray$3(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined); + }); + + // TODO: use something more complex like timsort? + var floor$4 = Math.floor; + + var mergeSort = function (array, comparefn) { + var length = array.length; + var middle = floor$4(length / 2); + return length < 8 ? insertionSort(array, comparefn) : merge$5( + mergeSort(array.slice(0, middle), comparefn), + mergeSort(array.slice(middle), comparefn), + comparefn + ); + }; + + var insertionSort = function (array, comparefn) { + var length = array.length; + var i = 1; + var element, j; + + while (i < length) { + j = i; + element = array[i]; + while (j && comparefn(array[j - 1], element) > 0) { + array[j] = array[--j]; + } + if (j !== i++) array[j] = element; + } return array; + }; + + var merge$5 = function (left, right, comparefn) { + var llength = left.length; + var rlength = right.length; + var lindex = 0; + var rindex = 0; + var result = []; + + while (lindex < llength || rindex < rlength) { + if (lindex < llength && rindex < rlength) { + result.push(comparefn(left[lindex], right[rindex]) <= 0 ? left[lindex++] : right[rindex++]); + } else { + result.push(lindex < llength ? left[lindex++] : right[rindex++]); + } + } return result; + }; + + var arraySort = mergeSort; + + var userAgent$2 = engineUserAgent; + + var firefox = userAgent$2.match(/firefox\/(\d+)/i); + + var engineFfVersion = !!firefox && +firefox[1]; + + var UA = engineUserAgent; + + var engineIsIeOrEdge = /MSIE|Trident/.test(UA); + + var userAgent$1 = engineUserAgent; + + var webkit = userAgent$1.match(/AppleWebKit\/(\d+)\./); + + var engineWebkitVersion = !!webkit && +webkit[1]; + + var ArrayBufferViewCore$2 = arrayBufferViewCore; + var global$d = global$F; + var fails$v = fails$N; + var aFunction$3 = aFunction$9; + var toLength$c = toLength$q; + var internalSort$1 = arraySort; + var FF$1 = engineFfVersion; + var IE_OR_EDGE$1 = engineIsIeOrEdge; + var V8$1 = engineV8Version; + var WEBKIT$1 = engineWebkitVersion; + + var aTypedArray$2 = ArrayBufferViewCore$2.aTypedArray; + var exportTypedArrayMethod$3 = ArrayBufferViewCore$2.exportTypedArrayMethod; + var Uint16Array = global$d.Uint16Array; + var nativeSort$1 = Uint16Array && Uint16Array.prototype.sort; + + // WebKit + var ACCEPT_INCORRECT_ARGUMENTS = !!nativeSort$1 && !fails$v(function () { + var array = new Uint16Array(2); + array.sort(null); + array.sort({}); + }); + + var STABLE_SORT$1 = !!nativeSort$1 && !fails$v(function () { + // feature detection can be too slow, so check engines versions + if (V8$1) return V8$1 < 74; + if (FF$1) return FF$1 < 67; + if (IE_OR_EDGE$1) return true; + if (WEBKIT$1) return WEBKIT$1 < 602; + + var array = new Uint16Array(516); + var expected = Array(516); + var index, mod; + + for (index = 0; index < 516; index++) { + mod = index % 4; + array[index] = 515 - index; + expected[index] = index - 2 * mod + 3; + } + + array.sort(function (a, b) { + return (a / 4 | 0) - (b / 4 | 0); + }); + + for (index = 0; index < 516; index++) { + if (array[index] !== expected[index]) return true; + } + }); + + var getSortCompare$1 = function (comparefn) { + return function (x, y) { + if (comparefn !== undefined) return +comparefn(x, y) || 0; + // eslint-disable-next-line no-self-compare -- NaN check + if (y !== y) return -1; + // eslint-disable-next-line no-self-compare -- NaN check + if (x !== x) return 1; + if (x === 0 && y === 0) return 1 / x > 0 && 1 / y < 0 ? 1 : -1; + return x > y; + }; + }; + + // `%TypedArray%.prototype.sort` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.sort + exportTypedArrayMethod$3('sort', function sort(comparefn) { + var array = this; + if (comparefn !== undefined) aFunction$3(comparefn); + if (STABLE_SORT$1) return nativeSort$1.call(array, comparefn); + + aTypedArray$2(array); + var arrayLength = toLength$c(array.length); + var items = Array(arrayLength); + var index; + + for (index = 0; index < arrayLength; index++) { + items[index] = array[index]; + } + + items = internalSort$1(array, getSortCompare$1(comparefn)); + + for (index = 0; index < arrayLength; index++) { + array[index] = items[index]; + } + + return array; + }, !STABLE_SORT$1 || ACCEPT_INCORRECT_ARGUMENTS); + + var ArrayBufferViewCore$1 = arrayBufferViewCore; + var toLength$b = toLength$q; + var toAbsoluteIndex$3 = toAbsoluteIndex$8; + var speciesConstructor$2 = speciesConstructor$8; + + var aTypedArray$1 = ArrayBufferViewCore$1.aTypedArray; + var exportTypedArrayMethod$2 = ArrayBufferViewCore$1.exportTypedArrayMethod; + + // `%TypedArray%.prototype.subarray` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.subarray + exportTypedArrayMethod$2('subarray', function subarray(begin, end) { + var O = aTypedArray$1(this); + var length = O.length; + var beginIndex = toAbsoluteIndex$3(begin, length); + return new (speciesConstructor$2(O, O.constructor))( + O.buffer, + O.byteOffset + beginIndex * O.BYTES_PER_ELEMENT, + toLength$b((end === undefined ? length : toAbsoluteIndex$3(end, length)) - beginIndex) + ); + }); + + var global$c = global$F; + var ArrayBufferViewCore = arrayBufferViewCore; + var fails$u = fails$N; + + var Int8Array$1 = global$c.Int8Array; + var aTypedArray = ArrayBufferViewCore.aTypedArray; + var exportTypedArrayMethod$1 = ArrayBufferViewCore.exportTypedArrayMethod; + var $toLocaleString = [].toLocaleString; + var $slice = [].slice; + + // iOS Safari 6.x fails here + var TO_LOCALE_STRING_BUG = !!Int8Array$1 && fails$u(function () { + $toLocaleString.call(new Int8Array$1(1)); + }); + + var FORCED$b = fails$u(function () { + return [1, 2].toLocaleString() != new Int8Array$1([1, 2]).toLocaleString(); + }) || !fails$u(function () { + Int8Array$1.prototype.toLocaleString.call([1, 2]); + }); + + // `%TypedArray%.prototype.toLocaleString` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.tolocalestring + exportTypedArrayMethod$1('toLocaleString', function toLocaleString() { + return $toLocaleString.apply(TO_LOCALE_STRING_BUG ? $slice.call(aTypedArray(this)) : aTypedArray(this), arguments); + }, FORCED$b); + + var exportTypedArrayMethod = arrayBufferViewCore.exportTypedArrayMethod; + var fails$t = fails$N; + var global$b = global$F; + + var Uint8Array$1 = global$b.Uint8Array; + var Uint8ArrayPrototype = Uint8Array$1 && Uint8Array$1.prototype || {}; + var arrayToString = [].toString; + var arrayJoin = [].join; + + if (fails$t(function () { arrayToString.call({}); })) { + arrayToString = function toString() { + return arrayJoin.call(this); + }; + } + + var IS_NOT_ARRAY_METHOD = Uint8ArrayPrototype.toString != arrayToString; + + // `%TypedArray%.prototype.toString` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.tostring + exportTypedArrayMethod('toString', arrayToString, IS_NOT_ARRAY_METHOD); + + var $$T = _export; + var IndexedObject$1 = indexedObject; + var toIndexedObject$3 = toIndexedObject$b; + var arrayMethodIsStrict$4 = arrayMethodIsStrict$8; + + var nativeJoin = [].join; + + var ES3_STRINGS = IndexedObject$1 != Object; + var STRICT_METHOD$4 = arrayMethodIsStrict$4('join', ','); + + // `Array.prototype.join` method + // https://tc39.es/ecma262/#sec-array.prototype.join + $$T({ target: 'Array', proto: true, forced: ES3_STRINGS || !STRICT_METHOD$4 }, { + join: function join(separator) { + return nativeJoin.call(toIndexedObject$3(this), separator === undefined ? ',' : separator); + } + }); + + var toPrimitive$2 = toPrimitive$7; + var definePropertyModule = objectDefineProperty; + var createPropertyDescriptor$1 = createPropertyDescriptor$7; + + var createProperty$4 = function (object, key, value) { + var propertyKey = toPrimitive$2(key); + if (propertyKey in object) definePropertyModule.f(object, propertyKey, createPropertyDescriptor$1(0, value)); + else object[propertyKey] = value; + }; + + var $$S = _export; + var isObject$d = isObject$r; + var isArray$2 = isArray$6; + var toAbsoluteIndex$2 = toAbsoluteIndex$8; + var toLength$a = toLength$q; + var toIndexedObject$2 = toIndexedObject$b; + var createProperty$3 = createProperty$4; + var wellKnownSymbol$8 = wellKnownSymbol$s; + var arrayMethodHasSpeciesSupport$3 = arrayMethodHasSpeciesSupport$5; + + var HAS_SPECIES_SUPPORT$2 = arrayMethodHasSpeciesSupport$3('slice'); + + var SPECIES$1 = wellKnownSymbol$8('species'); + var nativeSlice = [].slice; + var max$3 = Math.max; + + // `Array.prototype.slice` method + // https://tc39.es/ecma262/#sec-array.prototype.slice + // fallback for not array-like ES3 strings and DOM objects + $$S({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$2 }, { + slice: function slice(start, end) { + var O = toIndexedObject$2(this); + var length = toLength$a(O.length); + var k = toAbsoluteIndex$2(start, length); + var fin = toAbsoluteIndex$2(end === undefined ? length : end, length); + // inline `ArraySpeciesCreate` for usage native `Array#slice` where it's possible + var Constructor, result, n; + if (isArray$2(O)) { + Constructor = O.constructor; + // cross-realm fallback + if (typeof Constructor == 'function' && (Constructor === Array || isArray$2(Constructor.prototype))) { + Constructor = undefined; + } else if (isObject$d(Constructor)) { + Constructor = Constructor[SPECIES$1]; + if (Constructor === null) Constructor = undefined; + } + if (Constructor === Array || Constructor === undefined) { + return nativeSlice.call(O, k, fin); + } + } + result = new (Constructor === undefined ? Array : Constructor)(max$3(fin - k, 0)); + for (n = 0; k < fin; k++, n++) if (k in O) createProperty$3(result, n, O[k]); + result.length = n; + return result; + } + }); + + var fails$s = fails$N; + var wellKnownSymbol$7 = wellKnownSymbol$s; + var IS_PURE = isPure; + + var ITERATOR$1 = wellKnownSymbol$7('iterator'); + + var nativeUrl = !fails$s(function () { + var url = new URL('b?a=1&b=2&c=3', 'http://a'); + var searchParams = url.searchParams; + var result = ''; + url.pathname = 'c%20d'; + searchParams.forEach(function (value, key) { + searchParams['delete']('b'); + result += key + value; + }); + return (IS_PURE && !url.toJSON) + || !searchParams.sort + || url.href !== 'http://a/c%20d?a=1&c=3' + || searchParams.get('c') !== '3' + || String(new URLSearchParams('?a=1')) !== 'a=1' + || !searchParams[ITERATOR$1] + // throws in Edge + || new URL('https://a@b').username !== 'a' + || new URLSearchParams(new URLSearchParams('a=b')).get('a') !== 'b' + // not punycoded in Edge + || new URL('http://тест').host !== 'xn--e1aybc' + // not escaped in Chrome 62- + || new URL('http://a#б').hash !== '#%D0%B1' + // fails in Chrome 66- + || result !== 'a1c3' + // throws in Safari + || new URL('http://x', undefined).host !== 'x'; + }); + + var DESCRIPTORS$b = descriptors; + var fails$r = fails$N; + var objectKeys$1 = objectKeys$4; + var getOwnPropertySymbolsModule = objectGetOwnPropertySymbols; + var propertyIsEnumerableModule = objectPropertyIsEnumerable; + var toObject$8 = toObject$i; + var IndexedObject = indexedObject; + + // eslint-disable-next-line es/no-object-assign -- safe + var $assign = Object.assign; + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + var defineProperty$5 = Object.defineProperty; + + // `Object.assign` method + // https://tc39.es/ecma262/#sec-object.assign + var objectAssign = !$assign || fails$r(function () { + // should have correct order of operations (Edge bug) + if (DESCRIPTORS$b && $assign({ b: 1 }, $assign(defineProperty$5({}, 'a', { + enumerable: true, + get: function () { + defineProperty$5(this, 'b', { + value: 3, + enumerable: false + }); + } + }), { b: 2 })).b !== 1) return true; + // should work with symbols and should have deterministic property order (V8 bug) + var A = {}; + var B = {}; + // eslint-disable-next-line es/no-symbol -- safe + var symbol = Symbol(); + var alphabet = 'abcdefghijklmnopqrst'; + A[symbol] = 7; + alphabet.split('').forEach(function (chr) { B[chr] = chr; }); + return $assign({}, A)[symbol] != 7 || objectKeys$1($assign({}, B)).join('') != alphabet; + }) ? function assign(target, source) { // eslint-disable-line no-unused-vars -- required for `.length` + var T = toObject$8(target); + var argumentsLength = arguments.length; + var index = 1; + var getOwnPropertySymbols = getOwnPropertySymbolsModule.f; + var propertyIsEnumerable = propertyIsEnumerableModule.f; + while (argumentsLength > index) { + var S = IndexedObject(arguments[index++]); + var keys = getOwnPropertySymbols ? objectKeys$1(S).concat(getOwnPropertySymbols(S)) : objectKeys$1(S); + var length = keys.length; + var j = 0; + var key; + while (length > j) { + key = keys[j++]; + if (!DESCRIPTORS$b || propertyIsEnumerable.call(S, key)) T[key] = S[key]; + } + } return T; + } : $assign; + + var anObject$a = anObject$m; + var iteratorClose = iteratorClose$2; + + // call something on iterator step with safe closing on error + var callWithSafeIterationClosing$1 = function (iterator, fn, value, ENTRIES) { + try { + return ENTRIES ? fn(anObject$a(value)[0], value[1]) : fn(value); + } catch (error) { + iteratorClose(iterator); + throw error; + } + }; + + var bind$6 = functionBindContext; + var toObject$7 = toObject$i; + var callWithSafeIterationClosing = callWithSafeIterationClosing$1; + var isArrayIteratorMethod = isArrayIteratorMethod$3; + var toLength$9 = toLength$q; + var createProperty$2 = createProperty$4; + var getIteratorMethod$2 = getIteratorMethod$5; + + // `Array.from` method implementation + // https://tc39.es/ecma262/#sec-array.from + var arrayFrom$1 = function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) { + var O = toObject$7(arrayLike); + var C = typeof this == 'function' ? this : Array; + var argumentsLength = arguments.length; + var mapfn = argumentsLength > 1 ? arguments[1] : undefined; + var mapping = mapfn !== undefined; + var iteratorMethod = getIteratorMethod$2(O); + var index = 0; + var length, result, step, iterator, next, value; + if (mapping) mapfn = bind$6(mapfn, argumentsLength > 2 ? arguments[2] : undefined, 2); + // if the target is not iterable or it's an array with the default iterator - use a simple case + if (iteratorMethod != undefined && !(C == Array && isArrayIteratorMethod(iteratorMethod))) { + iterator = iteratorMethod.call(O); + next = iterator.next; + result = new C(); + for (;!(step = next.call(iterator)).done; index++) { + value = mapping ? callWithSafeIterationClosing(iterator, mapfn, [step.value, index], true) : step.value; + createProperty$2(result, index, value); + } + } else { + length = toLength$9(O.length); + result = new C(length); + for (;length > index; index++) { + value = mapping ? mapfn(O[index], index) : O[index]; + createProperty$2(result, index, value); + } + } + result.length = index; + return result; + }; + + // based on https://github.com/bestiejs/punycode.js/blob/master/punycode.js + var maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1 + var base = 36; + var tMin = 1; + var tMax = 26; + var skew = 38; + var damp = 700; + var initialBias = 72; + var initialN = 128; // 0x80 + var delimiter = '-'; // '\x2D' + var regexNonASCII = /[^\0-\u007E]/; // non-ASCII chars + var regexSeparators = /[.\u3002\uFF0E\uFF61]/g; // RFC 3490 separators + var OVERFLOW_ERROR = 'Overflow: input needs wider integers to process'; + var baseMinusTMin = base - tMin; + var floor$3 = Math.floor; + var stringFromCharCode = String.fromCharCode; + + /** + * Creates an array containing the numeric code points of each Unicode + * character in the string. While JavaScript uses UCS-2 internally, + * this function will convert a pair of surrogate halves (each of which + * UCS-2 exposes as separate characters) into a single code point, + * matching UTF-16. + */ + var ucs2decode = function (string) { + var output = []; + var counter = 0; + var length = string.length; + while (counter < length) { + var value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // It's a high surrogate, and there is a next character. + var extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // Low surrogate. + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // It's an unmatched surrogate; only append this code unit, in case the + // next code unit is the high surrogate of a surrogate pair. + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; + }; + + /** + * Converts a digit/integer into a basic code point. + */ + var digitToBasic = function (digit) { + // 0..25 map to ASCII a..z or A..Z + // 26..35 map to ASCII 0..9 + return digit + 22 + 75 * (digit < 26); + }; + + /** + * Bias adaptation function as per section 3.4 of RFC 3492. + * https://tools.ietf.org/html/rfc3492#section-3.4 + */ + var adapt = function (delta, numPoints, firstTime) { + var k = 0; + delta = firstTime ? floor$3(delta / damp) : delta >> 1; + delta += floor$3(delta / numPoints); + for (; delta > baseMinusTMin * tMax >> 1; k += base) { + delta = floor$3(delta / baseMinusTMin); + } + return floor$3(k + (baseMinusTMin + 1) * delta / (delta + skew)); + }; + + /** + * Converts a string of Unicode symbols (e.g. a domain name label) to a + * Punycode string of ASCII-only symbols. + */ + // eslint-disable-next-line max-statements -- TODO + var encode = function (input) { + var output = []; + + // Convert the input in UCS-2 to an array of Unicode code points. + input = ucs2decode(input); + + // Cache the length. + var inputLength = input.length; + + // Initialize the state. + var n = initialN; + var delta = 0; + var bias = initialBias; + var i, currentValue; + + // Handle the basic code points. + for (i = 0; i < input.length; i++) { + currentValue = input[i]; + if (currentValue < 0x80) { + output.push(stringFromCharCode(currentValue)); + } + } + + var basicLength = output.length; // number of basic code points. + var handledCPCount = basicLength; // number of code points that have been handled; + + // Finish the basic string with a delimiter unless it's empty. + if (basicLength) { + output.push(delimiter); + } + + // Main encoding loop: + while (handledCPCount < inputLength) { + // All non-basic code points < n have been handled already. Find the next larger one: + var m = maxInt; + for (i = 0; i < input.length; i++) { + currentValue = input[i]; + if (currentValue >= n && currentValue < m) { + m = currentValue; + } + } + + // Increase `delta` enough to advance the decoder's state to , but guard against overflow. + var handledCPCountPlusOne = handledCPCount + 1; + if (m - n > floor$3((maxInt - delta) / handledCPCountPlusOne)) { + throw RangeError(OVERFLOW_ERROR); + } + + delta += (m - n) * handledCPCountPlusOne; + n = m; + + for (i = 0; i < input.length; i++) { + currentValue = input[i]; + if (currentValue < n && ++delta > maxInt) { + throw RangeError(OVERFLOW_ERROR); + } + if (currentValue == n) { + // Represent delta as a generalized variable-length integer. + var q = delta; + for (var k = base; /* no condition */; k += base) { + var t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + if (q < t) break; + var qMinusT = q - t; + var baseMinusT = base - t; + output.push(stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT))); + q = floor$3(qMinusT / baseMinusT); + } + + output.push(stringFromCharCode(digitToBasic(q))); + bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); + delta = 0; + ++handledCPCount; + } + } + + ++delta; + ++n; + } + return output.join(''); + }; + + var stringPunycodeToAscii = function (input) { + var encoded = []; + var labels = input.toLowerCase().replace(regexSeparators, '\u002E').split('.'); + var i, label; + for (i = 0; i < labels.length; i++) { + label = labels[i]; + encoded.push(regexNonASCII.test(label) ? 'xn--' + encode(label) : label); + } + return encoded.join('.'); + }; + + var anObject$9 = anObject$m; + var getIteratorMethod$1 = getIteratorMethod$5; + + var getIterator$1 = function (it) { + var iteratorMethod = getIteratorMethod$1(it); + if (typeof iteratorMethod != 'function') { + throw TypeError(String(it) + ' is not iterable'); + } return anObject$9(iteratorMethod.call(it)); + }; + + // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env` + + var $$R = _export; + var getBuiltIn$2 = getBuiltIn$9; + var USE_NATIVE_URL$1 = nativeUrl; + var redefine$7 = redefine$g.exports; + var redefineAll$1 = redefineAll$4; + var setToStringTag$4 = setToStringTag$a; + var createIteratorConstructor = createIteratorConstructor$2; + var InternalStateModule$2 = internalState; + var anInstance$3 = anInstance$7; + var hasOwn = has$j; + var bind$5 = functionBindContext; + var classof$4 = classof$b; + var anObject$8 = anObject$m; + var isObject$c = isObject$r; + var create$8 = objectCreate; + var createPropertyDescriptor = createPropertyDescriptor$7; + var getIterator = getIterator$1; + var getIteratorMethod = getIteratorMethod$5; + var wellKnownSymbol$6 = wellKnownSymbol$s; + + var $fetch = getBuiltIn$2('fetch'); + var Headers$1 = getBuiltIn$2('Headers'); + var ITERATOR = wellKnownSymbol$6('iterator'); + var URL_SEARCH_PARAMS = 'URLSearchParams'; + var URL_SEARCH_PARAMS_ITERATOR = URL_SEARCH_PARAMS + 'Iterator'; + var setInternalState$2 = InternalStateModule$2.set; + var getInternalParamsState = InternalStateModule$2.getterFor(URL_SEARCH_PARAMS); + var getInternalIteratorState = InternalStateModule$2.getterFor(URL_SEARCH_PARAMS_ITERATOR); + + var plus = /\+/g; + var sequences = Array(4); + + var percentSequence = function (bytes) { + return sequences[bytes - 1] || (sequences[bytes - 1] = RegExp('((?:%[\\da-f]{2}){' + bytes + '})', 'gi')); + }; + + var percentDecode = function (sequence) { + try { + return decodeURIComponent(sequence); + } catch (error) { + return sequence; + } + }; + + var deserialize = function (it) { + var result = it.replace(plus, ' '); + var bytes = 4; + try { + return decodeURIComponent(result); + } catch (error) { + while (bytes) { + result = result.replace(percentSequence(bytes--), percentDecode); + } + return result; + } + }; + + var find$1 = /[!'()~]|%20/g; + + var replace$1 = { + '!': '%21', + "'": '%27', + '(': '%28', + ')': '%29', + '~': '%7E', + '%20': '+' + }; + + var replacer = function (match) { + return replace$1[match]; + }; + + var serialize = function (it) { + return encodeURIComponent(it).replace(find$1, replacer); + }; + + var parseSearchParams = function (result, query) { + if (query) { + var attributes = query.split('&'); + var index = 0; + var attribute, entry; + while (index < attributes.length) { + attribute = attributes[index++]; + if (attribute.length) { + entry = attribute.split('='); + result.push({ + key: deserialize(entry.shift()), + value: deserialize(entry.join('=')) + }); + } + } + } + }; + + var updateSearchParams = function (query) { + this.entries.length = 0; + parseSearchParams(this.entries, query); + }; + + var validateArgumentsLength = function (passed, required) { + if (passed < required) throw TypeError('Not enough arguments'); + }; + + var URLSearchParamsIterator = createIteratorConstructor(function Iterator(params, kind) { + setInternalState$2(this, { + type: URL_SEARCH_PARAMS_ITERATOR, + iterator: getIterator(getInternalParamsState(params).entries), + kind: kind + }); + }, 'Iterator', function next() { + var state = getInternalIteratorState(this); + var kind = state.kind; + var step = state.iterator.next(); + var entry = step.value; + if (!step.done) { + step.value = kind === 'keys' ? entry.key : kind === 'values' ? entry.value : [entry.key, entry.value]; + } return step; + }); + + // `URLSearchParams` constructor + // https://url.spec.whatwg.org/#interface-urlsearchparams + var URLSearchParamsConstructor = function URLSearchParams(/* init */) { + anInstance$3(this, URLSearchParamsConstructor, URL_SEARCH_PARAMS); + var init = arguments.length > 0 ? arguments[0] : undefined; + var that = this; + var entries = []; + var iteratorMethod, iterator, next, step, entryIterator, entryNext, first, second, key; + + setInternalState$2(that, { + type: URL_SEARCH_PARAMS, + entries: entries, + updateURL: function () { /* empty */ }, + updateSearchParams: updateSearchParams + }); + + if (init !== undefined) { + if (isObject$c(init)) { + iteratorMethod = getIteratorMethod(init); + if (typeof iteratorMethod === 'function') { + iterator = iteratorMethod.call(init); + next = iterator.next; + while (!(step = next.call(iterator)).done) { + entryIterator = getIterator(anObject$8(step.value)); + entryNext = entryIterator.next; + if ( + (first = entryNext.call(entryIterator)).done || + (second = entryNext.call(entryIterator)).done || + !entryNext.call(entryIterator).done + ) throw TypeError('Expected sequence with length 2'); + entries.push({ key: first.value + '', value: second.value + '' }); + } + } else for (key in init) if (hasOwn(init, key)) entries.push({ key: key, value: init[key] + '' }); + } else { + parseSearchParams(entries, typeof init === 'string' ? init.charAt(0) === '?' ? init.slice(1) : init : init + ''); + } + } + }; + + var URLSearchParamsPrototype = URLSearchParamsConstructor.prototype; + + redefineAll$1(URLSearchParamsPrototype, { + // `URLSearchParams.prototype.append` method + // https://url.spec.whatwg.org/#dom-urlsearchparams-append + append: function append(name, value) { + validateArgumentsLength(arguments.length, 2); + var state = getInternalParamsState(this); + state.entries.push({ key: name + '', value: value + '' }); + state.updateURL(); + }, + // `URLSearchParams.prototype.delete` method + // https://url.spec.whatwg.org/#dom-urlsearchparams-delete + 'delete': function (name) { + validateArgumentsLength(arguments.length, 1); + var state = getInternalParamsState(this); + var entries = state.entries; + var key = name + ''; + var index = 0; + while (index < entries.length) { + if (entries[index].key === key) entries.splice(index, 1); + else index++; + } + state.updateURL(); + }, + // `URLSearchParams.prototype.get` method + // https://url.spec.whatwg.org/#dom-urlsearchparams-get + get: function get(name) { + validateArgumentsLength(arguments.length, 1); + var entries = getInternalParamsState(this).entries; + var key = name + ''; + var index = 0; + for (; index < entries.length; index++) { + if (entries[index].key === key) return entries[index].value; + } + return null; + }, + // `URLSearchParams.prototype.getAll` method + // https://url.spec.whatwg.org/#dom-urlsearchparams-getall + getAll: function getAll(name) { + validateArgumentsLength(arguments.length, 1); + var entries = getInternalParamsState(this).entries; + var key = name + ''; + var result = []; + var index = 0; + for (; index < entries.length; index++) { + if (entries[index].key === key) result.push(entries[index].value); + } + return result; + }, + // `URLSearchParams.prototype.has` method + // https://url.spec.whatwg.org/#dom-urlsearchparams-has + has: function has(name) { + validateArgumentsLength(arguments.length, 1); + var entries = getInternalParamsState(this).entries; + var key = name + ''; + var index = 0; + while (index < entries.length) { + if (entries[index++].key === key) return true; + } + return false; + }, + // `URLSearchParams.prototype.set` method + // https://url.spec.whatwg.org/#dom-urlsearchparams-set + set: function set(name, value) { + validateArgumentsLength(arguments.length, 1); + var state = getInternalParamsState(this); + var entries = state.entries; + var found = false; + var key = name + ''; + var val = value + ''; + var index = 0; + var entry; + for (; index < entries.length; index++) { + entry = entries[index]; + if (entry.key === key) { + if (found) entries.splice(index--, 1); + else { + found = true; + entry.value = val; + } + } + } + if (!found) entries.push({ key: key, value: val }); + state.updateURL(); + }, + // `URLSearchParams.prototype.sort` method + // https://url.spec.whatwg.org/#dom-urlsearchparams-sort + sort: function sort() { + var state = getInternalParamsState(this); + var entries = state.entries; + // Array#sort is not stable in some engines + var slice = entries.slice(); + var entry, entriesIndex, sliceIndex; + entries.length = 0; + for (sliceIndex = 0; sliceIndex < slice.length; sliceIndex++) { + entry = slice[sliceIndex]; + for (entriesIndex = 0; entriesIndex < sliceIndex; entriesIndex++) { + if (entries[entriesIndex].key > entry.key) { + entries.splice(entriesIndex, 0, entry); + break; + } + } + if (entriesIndex === sliceIndex) entries.push(entry); + } + state.updateURL(); + }, + // `URLSearchParams.prototype.forEach` method + forEach: function forEach(callback /* , thisArg */) { + var entries = getInternalParamsState(this).entries; + var boundFunction = bind$5(callback, arguments.length > 1 ? arguments[1] : undefined, 3); + var index = 0; + var entry; + while (index < entries.length) { + entry = entries[index++]; + boundFunction(entry.value, entry.key, this); + } + }, + // `URLSearchParams.prototype.keys` method + keys: function keys() { + return new URLSearchParamsIterator(this, 'keys'); + }, + // `URLSearchParams.prototype.values` method + values: function values() { + return new URLSearchParamsIterator(this, 'values'); + }, + // `URLSearchParams.prototype.entries` method + entries: function entries() { + return new URLSearchParamsIterator(this, 'entries'); + } + }, { enumerable: true }); + + // `URLSearchParams.prototype[@@iterator]` method + redefine$7(URLSearchParamsPrototype, ITERATOR, URLSearchParamsPrototype.entries); + + // `URLSearchParams.prototype.toString` method + // https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior + redefine$7(URLSearchParamsPrototype, 'toString', function toString() { + var entries = getInternalParamsState(this).entries; + var result = []; + var index = 0; + var entry; + while (index < entries.length) { + entry = entries[index++]; + result.push(serialize(entry.key) + '=' + serialize(entry.value)); + } return result.join('&'); + }, { enumerable: true }); + + setToStringTag$4(URLSearchParamsConstructor, URL_SEARCH_PARAMS); + + $$R({ global: true, forced: !USE_NATIVE_URL$1 }, { + URLSearchParams: URLSearchParamsConstructor + }); + + // Wrap `fetch` for correct work with polyfilled `URLSearchParams` + // https://github.com/zloirock/core-js/issues/674 + if (!USE_NATIVE_URL$1 && typeof $fetch == 'function' && typeof Headers$1 == 'function') { + $$R({ global: true, enumerable: true, forced: true }, { + fetch: function fetch(input /* , init */) { + var args = [input]; + var init, body, headers; + if (arguments.length > 1) { + init = arguments[1]; + if (isObject$c(init)) { + body = init.body; + if (classof$4(body) === URL_SEARCH_PARAMS) { + headers = init.headers ? new Headers$1(init.headers) : new Headers$1(); + if (!headers.has('content-type')) { + headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8'); + } + init = create$8(init, { + body: createPropertyDescriptor(0, String(body)), + headers: createPropertyDescriptor(0, headers) + }); + } + } + args.push(init); + } return $fetch.apply(this, args); + } + }); + } + + var web_urlSearchParams = { + URLSearchParams: URLSearchParamsConstructor, + getState: getInternalParamsState + }; + + // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env` + + var $$Q = _export; + var DESCRIPTORS$a = descriptors; + var USE_NATIVE_URL = nativeUrl; + var global$a = global$F; + var defineProperties$1 = objectDefineProperties; + var redefine$6 = redefine$g.exports; + var anInstance$2 = anInstance$7; + var has$4 = has$j; + var assign$2 = objectAssign; + var arrayFrom = arrayFrom$1; + var codeAt = stringMultibyte.codeAt; + var toASCII = stringPunycodeToAscii; + var setToStringTag$3 = setToStringTag$a; + var URLSearchParamsModule = web_urlSearchParams; + var InternalStateModule$1 = internalState; + + var NativeURL = global$a.URL; + var URLSearchParams$1 = URLSearchParamsModule.URLSearchParams; + var getInternalSearchParamsState = URLSearchParamsModule.getState; + var setInternalState$1 = InternalStateModule$1.set; + var getInternalURLState = InternalStateModule$1.getterFor('URL'); + var floor$2 = Math.floor; + var pow$1 = Math.pow; + + var INVALID_AUTHORITY = 'Invalid authority'; + var INVALID_SCHEME = 'Invalid scheme'; + var INVALID_HOST = 'Invalid host'; + var INVALID_PORT = 'Invalid port'; + + var ALPHA = /[A-Za-z]/; + // eslint-disable-next-line regexp/no-obscure-range -- safe + var ALPHANUMERIC = /[\d+-.A-Za-z]/; + var DIGIT = /\d/; + var HEX_START = /^0x/i; + var OCT = /^[0-7]+$/; + var DEC = /^\d+$/; + var HEX = /^[\dA-Fa-f]+$/; + /* eslint-disable no-control-regex -- safe */ + var FORBIDDEN_HOST_CODE_POINT = /[\0\t\n\r #%/:<>?@[\\\]^|]/; + var FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT = /[\0\t\n\r #/:<>?@[\\\]^|]/; + var LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE = /^[\u0000-\u001F ]+|[\u0000-\u001F ]+$/g; + var TAB_AND_NEW_LINE = /[\t\n\r]/g; + /* eslint-enable no-control-regex -- safe */ + var EOF; + + var parseHost = function (url, input) { + var result, codePoints, index; + if (input.charAt(0) == '[') { + if (input.charAt(input.length - 1) != ']') return INVALID_HOST; + result = parseIPv6(input.slice(1, -1)); + if (!result) return INVALID_HOST; + url.host = result; + // opaque host + } else if (!isSpecial(url)) { + if (FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT.test(input)) return INVALID_HOST; + result = ''; + codePoints = arrayFrom(input); + for (index = 0; index < codePoints.length; index++) { + result += percentEncode(codePoints[index], C0ControlPercentEncodeSet); + } + url.host = result; + } else { + input = toASCII(input); + if (FORBIDDEN_HOST_CODE_POINT.test(input)) return INVALID_HOST; + result = parseIPv4(input); + if (result === null) return INVALID_HOST; + url.host = result; + } + }; + + var parseIPv4 = function (input) { + var parts = input.split('.'); + var partsLength, numbers, index, part, radix, number, ipv4; + if (parts.length && parts[parts.length - 1] == '') { + parts.pop(); + } + partsLength = parts.length; + if (partsLength > 4) return input; + numbers = []; + for (index = 0; index < partsLength; index++) { + part = parts[index]; + if (part == '') return input; + radix = 10; + if (part.length > 1 && part.charAt(0) == '0') { + radix = HEX_START.test(part) ? 16 : 8; + part = part.slice(radix == 8 ? 1 : 2); + } + if (part === '') { + number = 0; + } else { + if (!(radix == 10 ? DEC : radix == 8 ? OCT : HEX).test(part)) return input; + number = parseInt(part, radix); + } + numbers.push(number); + } + for (index = 0; index < partsLength; index++) { + number = numbers[index]; + if (index == partsLength - 1) { + if (number >= pow$1(256, 5 - partsLength)) return null; + } else if (number > 255) return null; + } + ipv4 = numbers.pop(); + for (index = 0; index < numbers.length; index++) { + ipv4 += numbers[index] * pow$1(256, 3 - index); + } + return ipv4; + }; + + // eslint-disable-next-line max-statements -- TODO + var parseIPv6 = function (input) { + var address = [0, 0, 0, 0, 0, 0, 0, 0]; + var pieceIndex = 0; + var compress = null; + var pointer = 0; + var value, length, numbersSeen, ipv4Piece, number, swaps, swap; + + var char = function () { + return input.charAt(pointer); + }; + + if (char() == ':') { + if (input.charAt(1) != ':') return; + pointer += 2; + pieceIndex++; + compress = pieceIndex; + } + while (char()) { + if (pieceIndex == 8) return; + if (char() == ':') { + if (compress !== null) return; + pointer++; + pieceIndex++; + compress = pieceIndex; + continue; + } + value = length = 0; + while (length < 4 && HEX.test(char())) { + value = value * 16 + parseInt(char(), 16); + pointer++; + length++; + } + if (char() == '.') { + if (length == 0) return; + pointer -= length; + if (pieceIndex > 6) return; + numbersSeen = 0; + while (char()) { + ipv4Piece = null; + if (numbersSeen > 0) { + if (char() == '.' && numbersSeen < 4) pointer++; + else return; + } + if (!DIGIT.test(char())) return; + while (DIGIT.test(char())) { + number = parseInt(char(), 10); + if (ipv4Piece === null) ipv4Piece = number; + else if (ipv4Piece == 0) return; + else ipv4Piece = ipv4Piece * 10 + number; + if (ipv4Piece > 255) return; + pointer++; + } + address[pieceIndex] = address[pieceIndex] * 256 + ipv4Piece; + numbersSeen++; + if (numbersSeen == 2 || numbersSeen == 4) pieceIndex++; + } + if (numbersSeen != 4) return; + break; + } else if (char() == ':') { + pointer++; + if (!char()) return; + } else if (char()) return; + address[pieceIndex++] = value; + } + if (compress !== null) { + swaps = pieceIndex - compress; + pieceIndex = 7; + while (pieceIndex != 0 && swaps > 0) { + swap = address[pieceIndex]; + address[pieceIndex--] = address[compress + swaps - 1]; + address[compress + --swaps] = swap; + } + } else if (pieceIndex != 8) return; + return address; + }; + + var findLongestZeroSequence = function (ipv6) { + var maxIndex = null; + var maxLength = 1; + var currStart = null; + var currLength = 0; + var index = 0; + for (; index < 8; index++) { + if (ipv6[index] !== 0) { + if (currLength > maxLength) { + maxIndex = currStart; + maxLength = currLength; + } + currStart = null; + currLength = 0; + } else { + if (currStart === null) currStart = index; + ++currLength; + } + } + if (currLength > maxLength) { + maxIndex = currStart; + maxLength = currLength; + } + return maxIndex; + }; + + var serializeHost = function (host) { + var result, index, compress, ignore0; + // ipv4 + if (typeof host == 'number') { + result = []; + for (index = 0; index < 4; index++) { + result.unshift(host % 256); + host = floor$2(host / 256); + } return result.join('.'); + // ipv6 + } else if (typeof host == 'object') { + result = ''; + compress = findLongestZeroSequence(host); + for (index = 0; index < 8; index++) { + if (ignore0 && host[index] === 0) continue; + if (ignore0) ignore0 = false; + if (compress === index) { + result += index ? ':' : '::'; + ignore0 = true; + } else { + result += host[index].toString(16); + if (index < 7) result += ':'; + } + } + return '[' + result + ']'; + } return host; + }; + + var C0ControlPercentEncodeSet = {}; + var fragmentPercentEncodeSet = assign$2({}, C0ControlPercentEncodeSet, { + ' ': 1, '"': 1, '<': 1, '>': 1, '`': 1 + }); + var pathPercentEncodeSet = assign$2({}, fragmentPercentEncodeSet, { + '#': 1, '?': 1, '{': 1, '}': 1 + }); + var userinfoPercentEncodeSet = assign$2({}, pathPercentEncodeSet, { + '/': 1, ':': 1, ';': 1, '=': 1, '@': 1, '[': 1, '\\': 1, ']': 1, '^': 1, '|': 1 + }); + + var percentEncode = function (char, set) { + var code = codeAt(char, 0); + return code > 0x20 && code < 0x7F && !has$4(set, char) ? char : encodeURIComponent(char); + }; + + var specialSchemes = { + ftp: 21, + file: null, + http: 80, + https: 443, + ws: 80, + wss: 443 + }; + + var isSpecial = function (url) { + return has$4(specialSchemes, url.scheme); + }; + + var includesCredentials = function (url) { + return url.username != '' || url.password != ''; + }; + + var cannotHaveUsernamePasswordPort = function (url) { + return !url.host || url.cannotBeABaseURL || url.scheme == 'file'; + }; + + var isWindowsDriveLetter = function (string, normalized) { + var second; + return string.length == 2 && ALPHA.test(string.charAt(0)) + && ((second = string.charAt(1)) == ':' || (!normalized && second == '|')); + }; + + var startsWithWindowsDriveLetter = function (string) { + var third; + return string.length > 1 && isWindowsDriveLetter(string.slice(0, 2)) && ( + string.length == 2 || + ((third = string.charAt(2)) === '/' || third === '\\' || third === '?' || third === '#') + ); + }; + + var shortenURLsPath = function (url) { + var path = url.path; + var pathSize = path.length; + if (pathSize && (url.scheme != 'file' || pathSize != 1 || !isWindowsDriveLetter(path[0], true))) { + path.pop(); + } + }; + + var isSingleDot = function (segment) { + return segment === '.' || segment.toLowerCase() === '%2e'; + }; + + var isDoubleDot = function (segment) { + segment = segment.toLowerCase(); + return segment === '..' || segment === '%2e.' || segment === '.%2e' || segment === '%2e%2e'; + }; + + // States: + var SCHEME_START = {}; + var SCHEME = {}; + var NO_SCHEME = {}; + var SPECIAL_RELATIVE_OR_AUTHORITY = {}; + var PATH_OR_AUTHORITY = {}; + var RELATIVE = {}; + var RELATIVE_SLASH = {}; + var SPECIAL_AUTHORITY_SLASHES = {}; + var SPECIAL_AUTHORITY_IGNORE_SLASHES = {}; + var AUTHORITY = {}; + var HOST = {}; + var HOSTNAME = {}; + var PORT = {}; + var FILE = {}; + var FILE_SLASH = {}; + var FILE_HOST = {}; + var PATH_START = {}; + var PATH = {}; + var CANNOT_BE_A_BASE_URL_PATH = {}; + var QUERY = {}; + var FRAGMENT = {}; + + // eslint-disable-next-line max-statements -- TODO + var parseURL = function (url, input, stateOverride, base) { + var state = stateOverride || SCHEME_START; + var pointer = 0; + var buffer = ''; + var seenAt = false; + var seenBracket = false; + var seenPasswordToken = false; + var codePoints, char, bufferCodePoints, failure; + + if (!stateOverride) { + url.scheme = ''; + url.username = ''; + url.password = ''; + url.host = null; + url.port = null; + url.path = []; + url.query = null; + url.fragment = null; + url.cannotBeABaseURL = false; + input = input.replace(LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE, ''); + } + + input = input.replace(TAB_AND_NEW_LINE, ''); + + codePoints = arrayFrom(input); + + while (pointer <= codePoints.length) { + char = codePoints[pointer]; + switch (state) { + case SCHEME_START: + if (char && ALPHA.test(char)) { + buffer += char.toLowerCase(); + state = SCHEME; + } else if (!stateOverride) { + state = NO_SCHEME; + continue; + } else return INVALID_SCHEME; + break; + + case SCHEME: + if (char && (ALPHANUMERIC.test(char) || char == '+' || char == '-' || char == '.')) { + buffer += char.toLowerCase(); + } else if (char == ':') { + if (stateOverride && ( + (isSpecial(url) != has$4(specialSchemes, buffer)) || + (buffer == 'file' && (includesCredentials(url) || url.port !== null)) || + (url.scheme == 'file' && !url.host) + )) return; + url.scheme = buffer; + if (stateOverride) { + if (isSpecial(url) && specialSchemes[url.scheme] == url.port) url.port = null; + return; + } + buffer = ''; + if (url.scheme == 'file') { + state = FILE; + } else if (isSpecial(url) && base && base.scheme == url.scheme) { + state = SPECIAL_RELATIVE_OR_AUTHORITY; + } else if (isSpecial(url)) { + state = SPECIAL_AUTHORITY_SLASHES; + } else if (codePoints[pointer + 1] == '/') { + state = PATH_OR_AUTHORITY; + pointer++; + } else { + url.cannotBeABaseURL = true; + url.path.push(''); + state = CANNOT_BE_A_BASE_URL_PATH; + } + } else if (!stateOverride) { + buffer = ''; + state = NO_SCHEME; + pointer = 0; + continue; + } else return INVALID_SCHEME; + break; + + case NO_SCHEME: + if (!base || (base.cannotBeABaseURL && char != '#')) return INVALID_SCHEME; + if (base.cannotBeABaseURL && char == '#') { + url.scheme = base.scheme; + url.path = base.path.slice(); + url.query = base.query; + url.fragment = ''; + url.cannotBeABaseURL = true; + state = FRAGMENT; + break; + } + state = base.scheme == 'file' ? FILE : RELATIVE; + continue; + + case SPECIAL_RELATIVE_OR_AUTHORITY: + if (char == '/' && codePoints[pointer + 1] == '/') { + state = SPECIAL_AUTHORITY_IGNORE_SLASHES; + pointer++; + } else { + state = RELATIVE; + continue; + } break; + + case PATH_OR_AUTHORITY: + if (char == '/') { + state = AUTHORITY; + break; + } else { + state = PATH; + continue; + } + + case RELATIVE: + url.scheme = base.scheme; + if (char == EOF) { + url.username = base.username; + url.password = base.password; + url.host = base.host; + url.port = base.port; + url.path = base.path.slice(); + url.query = base.query; + } else if (char == '/' || (char == '\\' && isSpecial(url))) { + state = RELATIVE_SLASH; + } else if (char == '?') { + url.username = base.username; + url.password = base.password; + url.host = base.host; + url.port = base.port; + url.path = base.path.slice(); + url.query = ''; + state = QUERY; + } else if (char == '#') { + url.username = base.username; + url.password = base.password; + url.host = base.host; + url.port = base.port; + url.path = base.path.slice(); + url.query = base.query; + url.fragment = ''; + state = FRAGMENT; + } else { + url.username = base.username; + url.password = base.password; + url.host = base.host; + url.port = base.port; + url.path = base.path.slice(); + url.path.pop(); + state = PATH; + continue; + } break; + + case RELATIVE_SLASH: + if (isSpecial(url) && (char == '/' || char == '\\')) { + state = SPECIAL_AUTHORITY_IGNORE_SLASHES; + } else if (char == '/') { + state = AUTHORITY; + } else { + url.username = base.username; + url.password = base.password; + url.host = base.host; + url.port = base.port; + state = PATH; + continue; + } break; + + case SPECIAL_AUTHORITY_SLASHES: + state = SPECIAL_AUTHORITY_IGNORE_SLASHES; + if (char != '/' || buffer.charAt(pointer + 1) != '/') continue; + pointer++; + break; + + case SPECIAL_AUTHORITY_IGNORE_SLASHES: + if (char != '/' && char != '\\') { + state = AUTHORITY; + continue; + } break; + + case AUTHORITY: + if (char == '@') { + if (seenAt) buffer = '%40' + buffer; + seenAt = true; + bufferCodePoints = arrayFrom(buffer); + for (var i = 0; i < bufferCodePoints.length; i++) { + var codePoint = bufferCodePoints[i]; + if (codePoint == ':' && !seenPasswordToken) { + seenPasswordToken = true; + continue; + } + var encodedCodePoints = percentEncode(codePoint, userinfoPercentEncodeSet); + if (seenPasswordToken) url.password += encodedCodePoints; + else url.username += encodedCodePoints; + } + buffer = ''; + } else if ( + char == EOF || char == '/' || char == '?' || char == '#' || + (char == '\\' && isSpecial(url)) + ) { + if (seenAt && buffer == '') return INVALID_AUTHORITY; + pointer -= arrayFrom(buffer).length + 1; + buffer = ''; + state = HOST; + } else buffer += char; + break; + + case HOST: + case HOSTNAME: + if (stateOverride && url.scheme == 'file') { + state = FILE_HOST; + continue; + } else if (char == ':' && !seenBracket) { + if (buffer == '') return INVALID_HOST; + failure = parseHost(url, buffer); + if (failure) return failure; + buffer = ''; + state = PORT; + if (stateOverride == HOSTNAME) return; + } else if ( + char == EOF || char == '/' || char == '?' || char == '#' || + (char == '\\' && isSpecial(url)) + ) { + if (isSpecial(url) && buffer == '') return INVALID_HOST; + if (stateOverride && buffer == '' && (includesCredentials(url) || url.port !== null)) return; + failure = parseHost(url, buffer); + if (failure) return failure; + buffer = ''; + state = PATH_START; + if (stateOverride) return; + continue; + } else { + if (char == '[') seenBracket = true; + else if (char == ']') seenBracket = false; + buffer += char; + } break; + + case PORT: + if (DIGIT.test(char)) { + buffer += char; + } else if ( + char == EOF || char == '/' || char == '?' || char == '#' || + (char == '\\' && isSpecial(url)) || + stateOverride + ) { + if (buffer != '') { + var port = parseInt(buffer, 10); + if (port > 0xFFFF) return INVALID_PORT; + url.port = (isSpecial(url) && port === specialSchemes[url.scheme]) ? null : port; + buffer = ''; + } + if (stateOverride) return; + state = PATH_START; + continue; + } else return INVALID_PORT; + break; + + case FILE: + url.scheme = 'file'; + if (char == '/' || char == '\\') state = FILE_SLASH; + else if (base && base.scheme == 'file') { + if (char == EOF) { + url.host = base.host; + url.path = base.path.slice(); + url.query = base.query; + } else if (char == '?') { + url.host = base.host; + url.path = base.path.slice(); + url.query = ''; + state = QUERY; + } else if (char == '#') { + url.host = base.host; + url.path = base.path.slice(); + url.query = base.query; + url.fragment = ''; + state = FRAGMENT; + } else { + if (!startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) { + url.host = base.host; + url.path = base.path.slice(); + shortenURLsPath(url); + } + state = PATH; + continue; + } + } else { + state = PATH; + continue; + } break; + + case FILE_SLASH: + if (char == '/' || char == '\\') { + state = FILE_HOST; + break; + } + if (base && base.scheme == 'file' && !startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) { + if (isWindowsDriveLetter(base.path[0], true)) url.path.push(base.path[0]); + else url.host = base.host; + } + state = PATH; + continue; + + case FILE_HOST: + if (char == EOF || char == '/' || char == '\\' || char == '?' || char == '#') { + if (!stateOverride && isWindowsDriveLetter(buffer)) { + state = PATH; + } else if (buffer == '') { + url.host = ''; + if (stateOverride) return; + state = PATH_START; + } else { + failure = parseHost(url, buffer); + if (failure) return failure; + if (url.host == 'localhost') url.host = ''; + if (stateOverride) return; + buffer = ''; + state = PATH_START; + } continue; + } else buffer += char; + break; + + case PATH_START: + if (isSpecial(url)) { + state = PATH; + if (char != '/' && char != '\\') continue; + } else if (!stateOverride && char == '?') { + url.query = ''; + state = QUERY; + } else if (!stateOverride && char == '#') { + url.fragment = ''; + state = FRAGMENT; + } else if (char != EOF) { + state = PATH; + if (char != '/') continue; + } break; + + case PATH: + if ( + char == EOF || char == '/' || + (char == '\\' && isSpecial(url)) || + (!stateOverride && (char == '?' || char == '#')) + ) { + if (isDoubleDot(buffer)) { + shortenURLsPath(url); + if (char != '/' && !(char == '\\' && isSpecial(url))) { + url.path.push(''); + } + } else if (isSingleDot(buffer)) { + if (char != '/' && !(char == '\\' && isSpecial(url))) { + url.path.push(''); + } + } else { + if (url.scheme == 'file' && !url.path.length && isWindowsDriveLetter(buffer)) { + if (url.host) url.host = ''; + buffer = buffer.charAt(0) + ':'; // normalize windows drive letter + } + url.path.push(buffer); + } + buffer = ''; + if (url.scheme == 'file' && (char == EOF || char == '?' || char == '#')) { + while (url.path.length > 1 && url.path[0] === '') { + url.path.shift(); + } + } + if (char == '?') { + url.query = ''; + state = QUERY; + } else if (char == '#') { + url.fragment = ''; + state = FRAGMENT; + } + } else { + buffer += percentEncode(char, pathPercentEncodeSet); + } break; + + case CANNOT_BE_A_BASE_URL_PATH: + if (char == '?') { + url.query = ''; + state = QUERY; + } else if (char == '#') { + url.fragment = ''; + state = FRAGMENT; + } else if (char != EOF) { + url.path[0] += percentEncode(char, C0ControlPercentEncodeSet); + } break; + + case QUERY: + if (!stateOverride && char == '#') { + url.fragment = ''; + state = FRAGMENT; + } else if (char != EOF) { + if (char == "'" && isSpecial(url)) url.query += '%27'; + else if (char == '#') url.query += '%23'; + else url.query += percentEncode(char, C0ControlPercentEncodeSet); + } break; + + case FRAGMENT: + if (char != EOF) url.fragment += percentEncode(char, fragmentPercentEncodeSet); + break; + } + + pointer++; + } + }; + + // `URL` constructor + // https://url.spec.whatwg.org/#url-class + var URLConstructor = function URL(url /* , base */) { + var that = anInstance$2(this, URLConstructor, 'URL'); + var base = arguments.length > 1 ? arguments[1] : undefined; + var urlString = String(url); + var state = setInternalState$1(that, { type: 'URL' }); + var baseState, failure; + if (base !== undefined) { + if (base instanceof URLConstructor) baseState = getInternalURLState(base); + else { + failure = parseURL(baseState = {}, String(base)); + if (failure) throw TypeError(failure); + } + } + failure = parseURL(state, urlString, null, baseState); + if (failure) throw TypeError(failure); + var searchParams = state.searchParams = new URLSearchParams$1(); + var searchParamsState = getInternalSearchParamsState(searchParams); + searchParamsState.updateSearchParams(state.query); + searchParamsState.updateURL = function () { + state.query = String(searchParams) || null; + }; + if (!DESCRIPTORS$a) { + that.href = serializeURL.call(that); + that.origin = getOrigin.call(that); + that.protocol = getProtocol.call(that); + that.username = getUsername.call(that); + that.password = getPassword.call(that); + that.host = getHost.call(that); + that.hostname = getHostname.call(that); + that.port = getPort.call(that); + that.pathname = getPathname.call(that); + that.search = getSearch.call(that); + that.searchParams = getSearchParams.call(that); + that.hash = getHash.call(that); + } + }; + + var URLPrototype = URLConstructor.prototype; + + var serializeURL = function () { + var url = getInternalURLState(this); + var scheme = url.scheme; + var username = url.username; + var password = url.password; + var host = url.host; + var port = url.port; + var path = url.path; + var query = url.query; + var fragment = url.fragment; + var output = scheme + ':'; + if (host !== null) { + output += '//'; + if (includesCredentials(url)) { + output += username + (password ? ':' + password : '') + '@'; + } + output += serializeHost(host); + if (port !== null) output += ':' + port; + } else if (scheme == 'file') output += '//'; + output += url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : ''; + if (query !== null) output += '?' + query; + if (fragment !== null) output += '#' + fragment; + return output; + }; + + var getOrigin = function () { + var url = getInternalURLState(this); + var scheme = url.scheme; + var port = url.port; + if (scheme == 'blob') try { + return new URLConstructor(scheme.path[0]).origin; + } catch (error) { + return 'null'; + } + if (scheme == 'file' || !isSpecial(url)) return 'null'; + return scheme + '://' + serializeHost(url.host) + (port !== null ? ':' + port : ''); + }; + + var getProtocol = function () { + return getInternalURLState(this).scheme + ':'; + }; + + var getUsername = function () { + return getInternalURLState(this).username; + }; + + var getPassword = function () { + return getInternalURLState(this).password; + }; + + var getHost = function () { + var url = getInternalURLState(this); + var host = url.host; + var port = url.port; + return host === null ? '' + : port === null ? serializeHost(host) + : serializeHost(host) + ':' + port; + }; + + var getHostname = function () { + var host = getInternalURLState(this).host; + return host === null ? '' : serializeHost(host); + }; + + var getPort = function () { + var port = getInternalURLState(this).port; + return port === null ? '' : String(port); + }; + + var getPathname = function () { + var url = getInternalURLState(this); + var path = url.path; + return url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : ''; + }; + + var getSearch = function () { + var query = getInternalURLState(this).query; + return query ? '?' + query : ''; + }; + + var getSearchParams = function () { + return getInternalURLState(this).searchParams; + }; + + var getHash = function () { + var fragment = getInternalURLState(this).fragment; + return fragment ? '#' + fragment : ''; + }; + + var accessorDescriptor = function (getter, setter) { + return { get: getter, set: setter, configurable: true, enumerable: true }; + }; + + if (DESCRIPTORS$a) { + defineProperties$1(URLPrototype, { + // `URL.prototype.href` accessors pair + // https://url.spec.whatwg.org/#dom-url-href + href: accessorDescriptor(serializeURL, function (href) { + var url = getInternalURLState(this); + var urlString = String(href); + var failure = parseURL(url, urlString); + if (failure) throw TypeError(failure); + getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query); + }), + // `URL.prototype.origin` getter + // https://url.spec.whatwg.org/#dom-url-origin + origin: accessorDescriptor(getOrigin), + // `URL.prototype.protocol` accessors pair + // https://url.spec.whatwg.org/#dom-url-protocol + protocol: accessorDescriptor(getProtocol, function (protocol) { + var url = getInternalURLState(this); + parseURL(url, String(protocol) + ':', SCHEME_START); + }), + // `URL.prototype.username` accessors pair + // https://url.spec.whatwg.org/#dom-url-username + username: accessorDescriptor(getUsername, function (username) { + var url = getInternalURLState(this); + var codePoints = arrayFrom(String(username)); + if (cannotHaveUsernamePasswordPort(url)) return; + url.username = ''; + for (var i = 0; i < codePoints.length; i++) { + url.username += percentEncode(codePoints[i], userinfoPercentEncodeSet); + } + }), + // `URL.prototype.password` accessors pair + // https://url.spec.whatwg.org/#dom-url-password + password: accessorDescriptor(getPassword, function (password) { + var url = getInternalURLState(this); + var codePoints = arrayFrom(String(password)); + if (cannotHaveUsernamePasswordPort(url)) return; + url.password = ''; + for (var i = 0; i < codePoints.length; i++) { + url.password += percentEncode(codePoints[i], userinfoPercentEncodeSet); + } + }), + // `URL.prototype.host` accessors pair + // https://url.spec.whatwg.org/#dom-url-host + host: accessorDescriptor(getHost, function (host) { + var url = getInternalURLState(this); + if (url.cannotBeABaseURL) return; + parseURL(url, String(host), HOST); + }), + // `URL.prototype.hostname` accessors pair + // https://url.spec.whatwg.org/#dom-url-hostname + hostname: accessorDescriptor(getHostname, function (hostname) { + var url = getInternalURLState(this); + if (url.cannotBeABaseURL) return; + parseURL(url, String(hostname), HOSTNAME); + }), + // `URL.prototype.port` accessors pair + // https://url.spec.whatwg.org/#dom-url-port + port: accessorDescriptor(getPort, function (port) { + var url = getInternalURLState(this); + if (cannotHaveUsernamePasswordPort(url)) return; + port = String(port); + if (port == '') url.port = null; + else parseURL(url, port, PORT); + }), + // `URL.prototype.pathname` accessors pair + // https://url.spec.whatwg.org/#dom-url-pathname + pathname: accessorDescriptor(getPathname, function (pathname) { + var url = getInternalURLState(this); + if (url.cannotBeABaseURL) return; + url.path = []; + parseURL(url, pathname + '', PATH_START); + }), + // `URL.prototype.search` accessors pair + // https://url.spec.whatwg.org/#dom-url-search + search: accessorDescriptor(getSearch, function (search) { + var url = getInternalURLState(this); + search = String(search); + if (search == '') { + url.query = null; + } else { + if ('?' == search.charAt(0)) search = search.slice(1); + url.query = ''; + parseURL(url, search, QUERY); + } + getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query); + }), + // `URL.prototype.searchParams` getter + // https://url.spec.whatwg.org/#dom-url-searchparams + searchParams: accessorDescriptor(getSearchParams), + // `URL.prototype.hash` accessors pair + // https://url.spec.whatwg.org/#dom-url-hash + hash: accessorDescriptor(getHash, function (hash) { + var url = getInternalURLState(this); + hash = String(hash); + if (hash == '') { + url.fragment = null; + return; + } + if ('#' == hash.charAt(0)) hash = hash.slice(1); + url.fragment = ''; + parseURL(url, hash, FRAGMENT); + }) + }); + } + + // `URL.prototype.toJSON` method + // https://url.spec.whatwg.org/#dom-url-tojson + redefine$6(URLPrototype, 'toJSON', function toJSON() { + return serializeURL.call(this); + }, { enumerable: true }); + + // `URL.prototype.toString` method + // https://url.spec.whatwg.org/#URL-stringification-behavior + redefine$6(URLPrototype, 'toString', function toString() { + return serializeURL.call(this); + }, { enumerable: true }); + + if (NativeURL) { + var nativeCreateObjectURL = NativeURL.createObjectURL; + var nativeRevokeObjectURL = NativeURL.revokeObjectURL; + // `URL.createObjectURL` method + // https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL + // eslint-disable-next-line no-unused-vars -- required for `.length` + if (nativeCreateObjectURL) redefine$6(URLConstructor, 'createObjectURL', function createObjectURL(blob) { + return nativeCreateObjectURL.apply(NativeURL, arguments); + }); + // `URL.revokeObjectURL` method + // https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL + // eslint-disable-next-line no-unused-vars -- required for `.length` + if (nativeRevokeObjectURL) redefine$6(URLConstructor, 'revokeObjectURL', function revokeObjectURL(url) { + return nativeRevokeObjectURL.apply(NativeURL, arguments); + }); + } + + setToStringTag$3(URLConstructor, 'URL'); + + $$Q({ global: true, forced: !USE_NATIVE_URL, sham: !DESCRIPTORS$a }, { + URL: URLConstructor + }); + + var anObject$7 = anObject$m; + + // `RegExp.prototype.flags` getter implementation + // https://tc39.es/ecma262/#sec-get-regexp.prototype.flags + var regexpFlags$1 = function () { + var that = anObject$7(this); + var result = ''; + if (that.global) result += 'g'; + if (that.ignoreCase) result += 'i'; + if (that.multiline) result += 'm'; + if (that.dotAll) result += 's'; + if (that.unicode) result += 'u'; + if (that.sticky) result += 'y'; + return result; + }; + + var redefine$5 = redefine$g.exports; + var anObject$6 = anObject$m; + var fails$q = fails$N; + var flags = regexpFlags$1; + + var TO_STRING = 'toString'; + var RegExpPrototype$2 = RegExp.prototype; + var nativeToString = RegExpPrototype$2[TO_STRING]; + + var NOT_GENERIC = fails$q(function () { return nativeToString.call({ source: 'a', flags: 'b' }) != '/a/b'; }); + // FF44- RegExp#toString has a wrong name + var INCORRECT_NAME = nativeToString.name != TO_STRING; + + // `RegExp.prototype.toString` method + // https://tc39.es/ecma262/#sec-regexp.prototype.tostring + if (NOT_GENERIC || INCORRECT_NAME) { + redefine$5(RegExp.prototype, TO_STRING, function toString() { + var R = anObject$6(this); + var p = String(R.source); + var rf = R.flags; + var f = String(rf === undefined && R instanceof RegExp && !('flags' in RegExpPrototype$2) ? flags.call(R) : rf); + return '/' + p + '/' + f; + }, { unsafe: true }); + } + + var regexpStickyHelpers = {}; + + var fails$p = fails$N; + + // babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError, + var RE = function (s, f) { + return RegExp(s, f); + }; + + regexpStickyHelpers.UNSUPPORTED_Y = fails$p(function () { + var re = RE('a', 'y'); + re.lastIndex = 2; + return re.exec('abcd') != null; + }); + + regexpStickyHelpers.BROKEN_CARET = fails$p(function () { + // https://bugzilla.mozilla.org/show_bug.cgi?id=773687 + var re = RE('^r', 'gy'); + re.lastIndex = 2; + return re.exec('str') != null; + }); + + var fails$o = fails$N; + + var regexpUnsupportedDotAll = fails$o(function () { + // babel-minify transpiles RegExp('.', 's') -> /./s and it causes SyntaxError + var re = RegExp('.', (typeof '').charAt(0)); + return !(re.dotAll && re.exec('\n') && re.flags === 's'); + }); + + var fails$n = fails$N; + + var regexpUnsupportedNcg = fails$n(function () { + // babel-minify transpiles RegExp('.', 'g') -> /./g and it causes SyntaxError + var re = RegExp('(?b)', (typeof '').charAt(5)); + return re.exec('b').groups.a !== 'b' || + 'b'.replace(re, '$c') !== 'bc'; + }); + + /* eslint-disable regexp/no-assertion-capturing-group, regexp/no-empty-group, regexp/no-lazy-ends -- testing */ + /* eslint-disable regexp/no-useless-quantifier -- testing */ + var regexpFlags = regexpFlags$1; + var stickyHelpers$2 = regexpStickyHelpers; + var shared = shared$5.exports; + var create$7 = objectCreate; + var getInternalState = internalState.get; + var UNSUPPORTED_DOT_ALL$1 = regexpUnsupportedDotAll; + var UNSUPPORTED_NCG$1 = regexpUnsupportedNcg; + + var nativeExec = RegExp.prototype.exec; + var nativeReplace = shared('native-string-replace', String.prototype.replace); + + var patchedExec = nativeExec; + + var UPDATES_LAST_INDEX_WRONG = (function () { + var re1 = /a/; + var re2 = /b*/g; + nativeExec.call(re1, 'a'); + nativeExec.call(re2, 'a'); + return re1.lastIndex !== 0 || re2.lastIndex !== 0; + })(); + + var UNSUPPORTED_Y$2 = stickyHelpers$2.UNSUPPORTED_Y || stickyHelpers$2.BROKEN_CARET; + + // nonparticipating capturing group, copied from es5-shim's String#split patch. + var NPCG_INCLUDED = /()??/.exec('')[1] !== undefined; + + var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED || UNSUPPORTED_Y$2 || UNSUPPORTED_DOT_ALL$1 || UNSUPPORTED_NCG$1; + + if (PATCH) { + // eslint-disable-next-line max-statements -- TODO + patchedExec = function exec(str) { + var re = this; + var state = getInternalState(re); + var raw = state.raw; + var result, reCopy, lastIndex, match, i, object, group; + + if (raw) { + raw.lastIndex = re.lastIndex; + result = patchedExec.call(raw, str); + re.lastIndex = raw.lastIndex; + return result; + } + + var groups = state.groups; + var sticky = UNSUPPORTED_Y$2 && re.sticky; + var flags = regexpFlags.call(re); + var source = re.source; + var charsAdded = 0; + var strCopy = str; + + if (sticky) { + flags = flags.replace('y', ''); + if (flags.indexOf('g') === -1) { + flags += 'g'; + } + + strCopy = String(str).slice(re.lastIndex); + // Support anchored sticky behavior. + if (re.lastIndex > 0 && (!re.multiline || re.multiline && str[re.lastIndex - 1] !== '\n')) { + source = '(?: ' + source + ')'; + strCopy = ' ' + strCopy; + charsAdded++; + } + // ^(? + rx + ) is needed, in combination with some str slicing, to + // simulate the 'y' flag. + reCopy = new RegExp('^(?:' + source + ')', flags); + } + + if (NPCG_INCLUDED) { + reCopy = new RegExp('^' + source + '$(?!\\s)', flags); + } + if (UPDATES_LAST_INDEX_WRONG) lastIndex = re.lastIndex; + + match = nativeExec.call(sticky ? reCopy : re, strCopy); + + if (sticky) { + if (match) { + match.input = match.input.slice(charsAdded); + match[0] = match[0].slice(charsAdded); + match.index = re.lastIndex; + re.lastIndex += match[0].length; + } else re.lastIndex = 0; + } else if (UPDATES_LAST_INDEX_WRONG && match) { + re.lastIndex = re.global ? match.index + match[0].length : lastIndex; + } + if (NPCG_INCLUDED && match && match.length > 1) { + // Fix browsers whose `exec` methods don't consistently return `undefined` + // for NPCG, like IE8. NOTE: This doesn' work for /(.?)?/ + nativeReplace.call(match[0], reCopy, function () { + for (i = 1; i < arguments.length - 2; i++) { + if (arguments[i] === undefined) match[i] = undefined; + } + }); + } + + if (match && groups) { + match.groups = object = create$7(null); + for (i = 0; i < groups.length; i++) { + group = groups[i]; + object[group[0]] = match[group[1]]; + } + } + + return match; + }; + } + + var regexpExec$3 = patchedExec; + + var $$P = _export; + var exec = regexpExec$3; + + // `RegExp.prototype.exec` method + // https://tc39.es/ecma262/#sec-regexp.prototype.exec + $$P({ target: 'RegExp', proto: true, forced: /./.exec !== exec }, { + exec: exec + }); + + // TODO: Remove from `core-js@4` since it's moved to entry points + + var redefine$4 = redefine$g.exports; + var regexpExec$2 = regexpExec$3; + var fails$m = fails$N; + var wellKnownSymbol$5 = wellKnownSymbol$s; + var createNonEnumerableProperty$1 = createNonEnumerableProperty$e; + + var SPECIES = wellKnownSymbol$5('species'); + var RegExpPrototype$1 = RegExp.prototype; + + var fixRegexpWellKnownSymbolLogic = function (KEY, exec, FORCED, SHAM) { + var SYMBOL = wellKnownSymbol$5(KEY); + + var DELEGATES_TO_SYMBOL = !fails$m(function () { + // String methods call symbol-named RegEp methods + var O = {}; + O[SYMBOL] = function () { return 7; }; + return ''[KEY](O) != 7; + }); + + var DELEGATES_TO_EXEC = DELEGATES_TO_SYMBOL && !fails$m(function () { + // Symbol-named RegExp methods call .exec + var execCalled = false; + var re = /a/; + + if (KEY === 'split') { + // We can't use real regex here since it causes deoptimization + // and serious performance degradation in V8 + // https://github.com/zloirock/core-js/issues/306 + re = {}; + // RegExp[@@split] doesn't call the regex's exec method, but first creates + // a new one. We need to return the patched regex when creating the new one. + re.constructor = {}; + re.constructor[SPECIES] = function () { return re; }; + re.flags = ''; + re[SYMBOL] = /./[SYMBOL]; + } + + re.exec = function () { execCalled = true; return null; }; + + re[SYMBOL](''); + return !execCalled; + }); + + if ( + !DELEGATES_TO_SYMBOL || + !DELEGATES_TO_EXEC || + FORCED + ) { + var nativeRegExpMethod = /./[SYMBOL]; + var methods = exec(SYMBOL, ''[KEY], function (nativeMethod, regexp, str, arg2, forceStringMethod) { + var $exec = regexp.exec; + if ($exec === regexpExec$2 || $exec === RegExpPrototype$1.exec) { + if (DELEGATES_TO_SYMBOL && !forceStringMethod) { + // The native String method already delegates to @@method (this + // polyfilled function), leasing to infinite recursion. + // We avoid it by directly calling the native @@method method. + return { done: true, value: nativeRegExpMethod.call(regexp, str, arg2) }; + } + return { done: true, value: nativeMethod.call(str, regexp, arg2) }; + } + return { done: false }; + }); + + redefine$4(String.prototype, KEY, methods[0]); + redefine$4(RegExpPrototype$1, SYMBOL, methods[1]); + } + + if (SHAM) createNonEnumerableProperty$1(RegExpPrototype$1[SYMBOL], 'sham', true); + }; + + var charAt = stringMultibyte.charAt; + + // `AdvanceStringIndex` abstract operation + // https://tc39.es/ecma262/#sec-advancestringindex + var advanceStringIndex$3 = function (S, index, unicode) { + return index + (unicode ? charAt(S, index).length : 1); + }; + + var toObject$6 = toObject$i; + + var floor$1 = Math.floor; + var replace = ''.replace; + var SUBSTITUTION_SYMBOLS = /\$([$&'`]|\d{1,2}|<[^>]*>)/g; + var SUBSTITUTION_SYMBOLS_NO_NAMED = /\$([$&'`]|\d{1,2})/g; + + // `GetSubstitution` abstract operation + // https://tc39.es/ecma262/#sec-getsubstitution + var getSubstitution$1 = function (matched, str, position, captures, namedCaptures, replacement) { + var tailPos = position + matched.length; + var m = captures.length; + var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED; + if (namedCaptures !== undefined) { + namedCaptures = toObject$6(namedCaptures); + symbols = SUBSTITUTION_SYMBOLS; + } + return replace.call(replacement, symbols, function (match, ch) { + var capture; + switch (ch.charAt(0)) { + case '$': return '$'; + case '&': return matched; + case '`': return str.slice(0, position); + case "'": return str.slice(tailPos); + case '<': + capture = namedCaptures[ch.slice(1, -1)]; + break; + default: // \d\d? + var n = +ch; + if (n === 0) return match; + if (n > m) { + var f = floor$1(n / 10); + if (f === 0) return match; + if (f <= m) return captures[f - 1] === undefined ? ch.charAt(1) : captures[f - 1] + ch.charAt(1); + return match; + } + capture = captures[n - 1]; + } + return capture === undefined ? '' : capture; + }); + }; + + var classof$3 = classofRaw$1; + var regexpExec$1 = regexpExec$3; + + // `RegExpExec` abstract operation + // https://tc39.es/ecma262/#sec-regexpexec + var regexpExecAbstract = function (R, S) { + var exec = R.exec; + if (typeof exec === 'function') { + var result = exec.call(R, S); + if (typeof result !== 'object') { + throw TypeError('RegExp exec method returned something other than an Object or null'); + } + return result; + } + + if (classof$3(R) !== 'RegExp') { + throw TypeError('RegExp#exec called on incompatible receiver'); + } + + return regexpExec$1.call(R, S); + }; + + var fixRegExpWellKnownSymbolLogic$3 = fixRegexpWellKnownSymbolLogic; + var fails$l = fails$N; + var anObject$5 = anObject$m; + var toLength$8 = toLength$q; + var toInteger$3 = toInteger$b; + var requireObjectCoercible$a = requireObjectCoercible$e; + var advanceStringIndex$2 = advanceStringIndex$3; + var getSubstitution = getSubstitution$1; + var regExpExec$2 = regexpExecAbstract; + var wellKnownSymbol$4 = wellKnownSymbol$s; + + var REPLACE = wellKnownSymbol$4('replace'); + var max$2 = Math.max; + var min$5 = Math.min; + + var maybeToString = function (it) { + return it === undefined ? it : String(it); + }; + + // IE <= 11 replaces $0 with the whole match, as if it was $& + // https://stackoverflow.com/questions/6024666/getting-ie-to-replace-a-regex-with-the-literal-string-0 + var REPLACE_KEEPS_$0 = (function () { + // eslint-disable-next-line regexp/prefer-escape-replacement-dollar-char -- required for testing + return 'a'.replace(/./, '$0') === '$0'; + })(); + + // Safari <= 13.0.3(?) substitutes nth capture where n>m with an empty string + var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = (function () { + if (/./[REPLACE]) { + return /./[REPLACE]('a', '$0') === ''; + } + return false; + })(); + + var REPLACE_SUPPORTS_NAMED_GROUPS = !fails$l(function () { + var re = /./; + re.exec = function () { + var result = []; + result.groups = { a: '7' }; + return result; + }; + return ''.replace(re, '$') !== '7'; + }); + + // @@replace logic + fixRegExpWellKnownSymbolLogic$3('replace', function (_, nativeReplace, maybeCallNative) { + var UNSAFE_SUBSTITUTE = REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE ? '$' : '$0'; + + return [ + // `String.prototype.replace` method + // https://tc39.es/ecma262/#sec-string.prototype.replace + function replace(searchValue, replaceValue) { + var O = requireObjectCoercible$a(this); + var replacer = searchValue == undefined ? undefined : searchValue[REPLACE]; + return replacer !== undefined + ? replacer.call(searchValue, O, replaceValue) + : nativeReplace.call(String(O), searchValue, replaceValue); + }, + // `RegExp.prototype[@@replace]` method + // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace + function (string, replaceValue) { + if ( + typeof replaceValue === 'string' && + replaceValue.indexOf(UNSAFE_SUBSTITUTE) === -1 && + replaceValue.indexOf('$<') === -1 + ) { + var res = maybeCallNative(nativeReplace, this, string, replaceValue); + if (res.done) return res.value; + } + + var rx = anObject$5(this); + var S = String(string); + + var functionalReplace = typeof replaceValue === 'function'; + if (!functionalReplace) replaceValue = String(replaceValue); + + var global = rx.global; + if (global) { + var fullUnicode = rx.unicode; + rx.lastIndex = 0; + } + var results = []; + while (true) { + var result = regExpExec$2(rx, S); + if (result === null) break; + + results.push(result); + if (!global) break; + + var matchStr = String(result[0]); + if (matchStr === '') rx.lastIndex = advanceStringIndex$2(S, toLength$8(rx.lastIndex), fullUnicode); + } + + var accumulatedResult = ''; + var nextSourcePosition = 0; + for (var i = 0; i < results.length; i++) { + result = results[i]; + + var matched = String(result[0]); + var position = max$2(min$5(toInteger$3(result.index), S.length), 0); + var captures = []; + // NOTE: This is equivalent to + // captures = result.slice(1).map(maybeToString) + // but for some reason `nativeSlice.call(result, 1, result.length)` (called in + // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and + // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it. + for (var j = 1; j < result.length; j++) captures.push(maybeToString(result[j])); + var namedCaptures = result.groups; + if (functionalReplace) { + var replacerArgs = [matched].concat(captures, position, S); + if (namedCaptures !== undefined) replacerArgs.push(namedCaptures); + var replacement = String(replaceValue.apply(undefined, replacerArgs)); + } else { + replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue); + } + if (position >= nextSourcePosition) { + accumulatedResult += S.slice(nextSourcePosition, position) + replacement; + nextSourcePosition = position + matched.length; + } + } + return accumulatedResult + S.slice(nextSourcePosition); + } + ]; + }, !REPLACE_SUPPORTS_NAMED_GROUPS || !REPLACE_KEEPS_$0 || REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE); + + var isObject$b = isObject$r; + var classof$2 = classofRaw$1; + var wellKnownSymbol$3 = wellKnownSymbol$s; + + var MATCH$2 = wellKnownSymbol$3('match'); + + // `IsRegExp` abstract operation + // https://tc39.es/ecma262/#sec-isregexp + var isRegexp = function (it) { + var isRegExp; + return isObject$b(it) && ((isRegExp = it[MATCH$2]) !== undefined ? !!isRegExp : classof$2(it) == 'RegExp'); + }; + + var fixRegExpWellKnownSymbolLogic$2 = fixRegexpWellKnownSymbolLogic; + var isRegExp$2 = isRegexp; + var anObject$4 = anObject$m; + var requireObjectCoercible$9 = requireObjectCoercible$e; + var speciesConstructor$1 = speciesConstructor$8; + var advanceStringIndex$1 = advanceStringIndex$3; + var toLength$7 = toLength$q; + var callRegExpExec = regexpExecAbstract; + var regexpExec = regexpExec$3; + var stickyHelpers$1 = regexpStickyHelpers; + var fails$k = fails$N; + + var UNSUPPORTED_Y$1 = stickyHelpers$1.UNSUPPORTED_Y; + var arrayPush = [].push; + var min$4 = Math.min; + var MAX_UINT32 = 0xFFFFFFFF; + + // Chrome 51 has a buggy "split" implementation when RegExp#exec !== nativeExec + // Weex JS has frozen built-in prototypes, so use try / catch wrapper + var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = !fails$k(function () { + // eslint-disable-next-line regexp/no-empty-group -- required for testing + var re = /(?:)/; + var originalExec = re.exec; + re.exec = function () { return originalExec.apply(this, arguments); }; + var result = 'ab'.split(re); + return result.length !== 2 || result[0] !== 'a' || result[1] !== 'b'; + }); + + // @@split logic + fixRegExpWellKnownSymbolLogic$2('split', function (SPLIT, nativeSplit, maybeCallNative) { + var internalSplit; + if ( + 'abbc'.split(/(b)*/)[1] == 'c' || + // eslint-disable-next-line regexp/no-empty-group -- required for testing + 'test'.split(/(?:)/, -1).length != 4 || + 'ab'.split(/(?:ab)*/).length != 2 || + '.'.split(/(.?)(.?)/).length != 4 || + // eslint-disable-next-line regexp/no-assertion-capturing-group, regexp/no-empty-group -- required for testing + '.'.split(/()()/).length > 1 || + ''.split(/.?/).length + ) { + // based on es5-shim implementation, need to rework it + internalSplit = function (separator, limit) { + var string = String(requireObjectCoercible$9(this)); + var lim = limit === undefined ? MAX_UINT32 : limit >>> 0; + if (lim === 0) return []; + if (separator === undefined) return [string]; + // If `separator` is not a regex, use native split + if (!isRegExp$2(separator)) { + return nativeSplit.call(string, separator, lim); + } + var output = []; + var flags = (separator.ignoreCase ? 'i' : '') + + (separator.multiline ? 'm' : '') + + (separator.unicode ? 'u' : '') + + (separator.sticky ? 'y' : ''); + var lastLastIndex = 0; + // Make `global` and avoid `lastIndex` issues by working with a copy + var separatorCopy = new RegExp(separator.source, flags + 'g'); + var match, lastIndex, lastLength; + while (match = regexpExec.call(separatorCopy, string)) { + lastIndex = separatorCopy.lastIndex; + if (lastIndex > lastLastIndex) { + output.push(string.slice(lastLastIndex, match.index)); + if (match.length > 1 && match.index < string.length) arrayPush.apply(output, match.slice(1)); + lastLength = match[0].length; + lastLastIndex = lastIndex; + if (output.length >= lim) break; + } + if (separatorCopy.lastIndex === match.index) separatorCopy.lastIndex++; // Avoid an infinite loop + } + if (lastLastIndex === string.length) { + if (lastLength || !separatorCopy.test('')) output.push(''); + } else output.push(string.slice(lastLastIndex)); + return output.length > lim ? output.slice(0, lim) : output; + }; + // Chakra, V8 + } else if ('0'.split(undefined, 0).length) { + internalSplit = function (separator, limit) { + return separator === undefined && limit === 0 ? [] : nativeSplit.call(this, separator, limit); + }; + } else internalSplit = nativeSplit; + + return [ + // `String.prototype.split` method + // https://tc39.es/ecma262/#sec-string.prototype.split + function split(separator, limit) { + var O = requireObjectCoercible$9(this); + var splitter = separator == undefined ? undefined : separator[SPLIT]; + return splitter !== undefined + ? splitter.call(separator, O, limit) + : internalSplit.call(String(O), separator, limit); + }, + // `RegExp.prototype[@@split]` method + // https://tc39.es/ecma262/#sec-regexp.prototype-@@split + // + // NOTE: This cannot be properly polyfilled in engines that don't support + // the 'y' flag. + function (string, limit) { + var res = maybeCallNative(internalSplit, this, string, limit, internalSplit !== nativeSplit); + if (res.done) return res.value; + + var rx = anObject$4(this); + var S = String(string); + var C = speciesConstructor$1(rx, RegExp); + + var unicodeMatching = rx.unicode; + var flags = (rx.ignoreCase ? 'i' : '') + + (rx.multiline ? 'm' : '') + + (rx.unicode ? 'u' : '') + + (UNSUPPORTED_Y$1 ? 'g' : 'y'); + + // ^(? + rx + ) is needed, in combination with some S slicing, to + // simulate the 'y' flag. + var splitter = new C(UNSUPPORTED_Y$1 ? '^(?:' + rx.source + ')' : rx, flags); + var lim = limit === undefined ? MAX_UINT32 : limit >>> 0; + if (lim === 0) return []; + if (S.length === 0) return callRegExpExec(splitter, S) === null ? [S] : []; + var p = 0; + var q = 0; + var A = []; + while (q < S.length) { + splitter.lastIndex = UNSUPPORTED_Y$1 ? 0 : q; + var z = callRegExpExec(splitter, UNSUPPORTED_Y$1 ? S.slice(q) : S); + var e; + if ( + z === null || + (e = min$4(toLength$7(splitter.lastIndex + (UNSUPPORTED_Y$1 ? q : 0)), S.length)) === p + ) { + q = advanceStringIndex$1(S, q, unicodeMatching); + } else { + A.push(S.slice(p, q)); + if (A.length === lim) return A; + for (var i = 1; i <= z.length - 1; i++) { + A.push(z[i]); + if (A.length === lim) return A; + } + q = p = e; + } + } + A.push(S.slice(p)); + return A; + } + ]; + }, !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC, UNSUPPORTED_Y$1); + + // a string of all valid unicode whitespaces + var whitespaces$4 = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002' + + '\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF'; + + var requireObjectCoercible$8 = requireObjectCoercible$e; + var whitespaces$3 = whitespaces$4; + + var whitespace = '[' + whitespaces$3 + ']'; + var ltrim = RegExp('^' + whitespace + whitespace + '*'); + var rtrim$2 = RegExp(whitespace + whitespace + '*$'); + + // `String.prototype.{ trim, trimStart, trimEnd, trimLeft, trimRight }` methods implementation + var createMethod$2 = function (TYPE) { + return function ($this) { + var string = String(requireObjectCoercible$8($this)); + if (TYPE & 1) string = string.replace(ltrim, ''); + if (TYPE & 2) string = string.replace(rtrim$2, ''); + return string; + }; + }; + + var stringTrim = { + // `String.prototype.{ trimLeft, trimStart }` methods + // https://tc39.es/ecma262/#sec-string.prototype.trimstart + start: createMethod$2(1), + // `String.prototype.{ trimRight, trimEnd }` methods + // https://tc39.es/ecma262/#sec-string.prototype.trimend + end: createMethod$2(2), + // `String.prototype.trim` method + // https://tc39.es/ecma262/#sec-string.prototype.trim + trim: createMethod$2(3) + }; + + var fails$j = fails$N; + var whitespaces$2 = whitespaces$4; + + var non = '\u200B\u0085\u180E'; + + // check that a method works with the correct list + // of whitespaces and has a correct name + var stringTrimForced = function (METHOD_NAME) { + return fails$j(function () { + return !!whitespaces$2[METHOD_NAME]() || non[METHOD_NAME]() != non || whitespaces$2[METHOD_NAME].name !== METHOD_NAME; + }); + }; + + var $$O = _export; + var $trim = stringTrim.trim; + var forcedStringTrimMethod = stringTrimForced; + + // `String.prototype.trim` method + // https://tc39.es/ecma262/#sec-string.prototype.trim + $$O({ target: 'String', proto: true, forced: forcedStringTrimMethod('trim') }, { + trim: function trim() { + return $trim(this); + } + }); + + var DESCRIPTORS$9 = descriptors; + var defineProperty$4 = objectDefineProperty.f; + + var FunctionPrototype = Function.prototype; + var FunctionPrototypeToString = FunctionPrototype.toString; + var nameRE = /^\s*function ([^ (]*)/; + var NAME = 'name'; + + // Function instances `.name` property + // https://tc39.es/ecma262/#sec-function-instances-name + if (DESCRIPTORS$9 && !(NAME in FunctionPrototype)) { + defineProperty$4(FunctionPrototype, NAME, { + configurable: true, + get: function () { + try { + return FunctionPrototypeToString.call(this).match(nameRE)[1]; + } catch (error) { + return ''; + } + } + }); + } + + var $$N = _export; + var DESCRIPTORS$8 = descriptors; + var create$6 = objectCreate; + + // `Object.create` method + // https://tc39.es/ecma262/#sec-object.create + $$N({ target: 'Object', stat: true, sham: !DESCRIPTORS$8 }, { + create: create$6 + }); + + var $$M = _export; + var global$9 = global$F; + var userAgent = engineUserAgent; + + var slice$3 = [].slice; + var MSIE = /MSIE .\./.test(userAgent); // <- dirty ie9- check + + var wrap$1 = function (scheduler) { + return function (handler, timeout /* , ...arguments */) { + var boundArgs = arguments.length > 2; + var args = boundArgs ? slice$3.call(arguments, 2) : undefined; + return scheduler(boundArgs ? function () { + // eslint-disable-next-line no-new-func -- spec requirement + (typeof handler == 'function' ? handler : Function(handler)).apply(this, args); + } : handler, timeout); + }; + }; + + // ie9- setTimeout & setInterval additional parameters fix + // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers + $$M({ global: true, bind: true, forced: MSIE }, { + // `setTimeout` method + // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-settimeout + setTimeout: wrap$1(global$9.setTimeout), + // `setInterval` method + // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval + setInterval: wrap$1(global$9.setInterval) + }); + + var global$8 = typeof globalThis !== 'undefined' && globalThis || typeof self !== 'undefined' && self || typeof global$8 !== 'undefined' && global$8; + var support = { + searchParams: 'URLSearchParams' in global$8, + iterable: 'Symbol' in global$8 && 'iterator' in Symbol, + blob: 'FileReader' in global$8 && 'Blob' in global$8 && function () { + try { + new Blob(); + return true; + } catch (e) { + return false; + } + }(), + formData: 'FormData' in global$8, + arrayBuffer: 'ArrayBuffer' in global$8 + }; + + function isDataView(obj) { + return obj && DataView.prototype.isPrototypeOf(obj); + } + + if (support.arrayBuffer) { + var viewClasses = ['[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]']; + + var isArrayBufferView = ArrayBuffer.isView || function (obj) { + return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1; + }; + } + + function normalizeName(name) { + if (typeof name !== 'string') { + name = String(name); + } + + if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') { + throw new TypeError('Invalid character in header field name: "' + name + '"'); + } + + return name.toLowerCase(); + } + + function normalizeValue(value) { + if (typeof value !== 'string') { + value = String(value); + } + + return value; + } // Build a destructive iterator for the value list + + + function iteratorFor(items) { + var iterator = { + next: function next() { + var value = items.shift(); + return { + done: value === undefined, + value: value + }; + } + }; + + if (support.iterable) { + iterator[Symbol.iterator] = function () { + return iterator; + }; + } + + return iterator; + } + + function Headers(headers) { + this.map = {}; + + if (headers instanceof Headers) { + headers.forEach(function (value, name) { + this.append(name, value); + }, this); + } else if (Array.isArray(headers)) { + headers.forEach(function (header) { + this.append(header[0], header[1]); + }, this); + } else if (headers) { + Object.getOwnPropertyNames(headers).forEach(function (name) { + this.append(name, headers[name]); + }, this); + } + } + + Headers.prototype.append = function (name, value) { + name = normalizeName(name); + value = normalizeValue(value); + var oldValue = this.map[name]; + this.map[name] = oldValue ? oldValue + ', ' + value : value; + }; + + Headers.prototype['delete'] = function (name) { + delete this.map[normalizeName(name)]; + }; + + Headers.prototype.get = function (name) { + name = normalizeName(name); + return this.has(name) ? this.map[name] : null; + }; + + Headers.prototype.has = function (name) { + return this.map.hasOwnProperty(normalizeName(name)); + }; + + Headers.prototype.set = function (name, value) { + this.map[normalizeName(name)] = normalizeValue(value); + }; + + Headers.prototype.forEach = function (callback, thisArg) { + for (var name in this.map) { + if (this.map.hasOwnProperty(name)) { + callback.call(thisArg, this.map[name], name, this); + } + } + }; + + Headers.prototype.keys = function () { + var items = []; + this.forEach(function (value, name) { + items.push(name); + }); + return iteratorFor(items); + }; + + Headers.prototype.values = function () { + var items = []; + this.forEach(function (value) { + items.push(value); + }); + return iteratorFor(items); + }; + + Headers.prototype.entries = function () { + var items = []; + this.forEach(function (value, name) { + items.push([name, value]); + }); + return iteratorFor(items); + }; + + if (support.iterable) { + Headers.prototype[Symbol.iterator] = Headers.prototype.entries; + } + + function consumed(body) { + if (body.bodyUsed) { + return Promise.reject(new TypeError('Already read')); + } + + body.bodyUsed = true; + } + + function fileReaderReady(reader) { + return new Promise(function (resolve, reject) { + reader.onload = function () { + resolve(reader.result); + }; + + reader.onerror = function () { + reject(reader.error); + }; + }); + } + + function readBlobAsArrayBuffer(blob) { + var reader = new FileReader(); + var promise = fileReaderReady(reader); + reader.readAsArrayBuffer(blob); + return promise; + } + + function readBlobAsText(blob) { + var reader = new FileReader(); + var promise = fileReaderReady(reader); + reader.readAsText(blob); + return promise; + } + + function readArrayBufferAsText(buf) { + var view = new Uint8Array(buf); + var chars = new Array(view.length); + + for (var i = 0; i < view.length; i++) { + chars[i] = String.fromCharCode(view[i]); + } + + return chars.join(''); + } + + function bufferClone(buf) { + if (buf.slice) { + return buf.slice(0); + } else { + var view = new Uint8Array(buf.byteLength); + view.set(new Uint8Array(buf)); + return view.buffer; + } + } + + function Body() { + this.bodyUsed = false; + + this._initBody = function (body) { + /* + fetch-mock wraps the Response object in an ES6 Proxy to + provide useful test harness features such as flush. However, on + ES5 browsers without fetch or Proxy support pollyfills must be used; + the proxy-pollyfill is unable to proxy an attribute unless it exists + on the object before the Proxy is created. This change ensures + Response.bodyUsed exists on the instance, while maintaining the + semantic of setting Request.bodyUsed in the constructor before + _initBody is called. + */ + this.bodyUsed = this.bodyUsed; + this._bodyInit = body; + + if (!body) { + this._bodyText = ''; + } else if (typeof body === 'string') { + this._bodyText = body; + } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { + this._bodyBlob = body; + } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { + this._bodyFormData = body; + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this._bodyText = body.toString(); + } else if (support.arrayBuffer && support.blob && isDataView(body)) { + this._bodyArrayBuffer = bufferClone(body.buffer); // IE 10-11 can't handle a DataView body. + + this._bodyInit = new Blob([this._bodyArrayBuffer]); + } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { + this._bodyArrayBuffer = bufferClone(body); + } else { + this._bodyText = body = Object.prototype.toString.call(body); + } + + if (!this.headers.get('content-type')) { + if (typeof body === 'string') { + this.headers.set('content-type', 'text/plain;charset=UTF-8'); + } else if (this._bodyBlob && this._bodyBlob.type) { + this.headers.set('content-type', this._bodyBlob.type); + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8'); + } + } + }; + + if (support.blob) { + this.blob = function () { + var rejected = consumed(this); + + if (rejected) { + return rejected; + } + + if (this._bodyBlob) { + return Promise.resolve(this._bodyBlob); + } else if (this._bodyArrayBuffer) { + return Promise.resolve(new Blob([this._bodyArrayBuffer])); + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as blob'); + } else { + return Promise.resolve(new Blob([this._bodyText])); + } + }; + + this.arrayBuffer = function () { + if (this._bodyArrayBuffer) { + var isConsumed = consumed(this); + + if (isConsumed) { + return isConsumed; + } + + if (ArrayBuffer.isView(this._bodyArrayBuffer)) { + return Promise.resolve(this._bodyArrayBuffer.buffer.slice(this._bodyArrayBuffer.byteOffset, this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength)); + } else { + return Promise.resolve(this._bodyArrayBuffer); + } + } else { + return this.blob().then(readBlobAsArrayBuffer); + } + }; + } + + this.text = function () { + var rejected = consumed(this); + + if (rejected) { + return rejected; + } + + if (this._bodyBlob) { + return readBlobAsText(this._bodyBlob); + } else if (this._bodyArrayBuffer) { + return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)); + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as text'); + } else { + return Promise.resolve(this._bodyText); + } + }; + + if (support.formData) { + this.formData = function () { + return this.text().then(decode); + }; + } + + this.json = function () { + return this.text().then(JSON.parse); + }; + + return this; + } // HTTP methods whose capitalization should be normalized + + + var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']; + + function normalizeMethod(method) { + var upcased = method.toUpperCase(); + return methods.indexOf(upcased) > -1 ? upcased : method; + } + + function Request(input, options) { + if (!(this instanceof Request)) { + throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.'); + } + + options = options || {}; + var body = options.body; + + if (input instanceof Request) { + if (input.bodyUsed) { + throw new TypeError('Already read'); + } + + this.url = input.url; + this.credentials = input.credentials; + + if (!options.headers) { + this.headers = new Headers(input.headers); + } + + this.method = input.method; + this.mode = input.mode; + this.signal = input.signal; + + if (!body && input._bodyInit != null) { + body = input._bodyInit; + input.bodyUsed = true; + } + } else { + this.url = String(input); + } + + this.credentials = options.credentials || this.credentials || 'same-origin'; + + if (options.headers || !this.headers) { + this.headers = new Headers(options.headers); + } + + this.method = normalizeMethod(options.method || this.method || 'GET'); + this.mode = options.mode || this.mode || null; + this.signal = options.signal || this.signal; + this.referrer = null; + + if ((this.method === 'GET' || this.method === 'HEAD') && body) { + throw new TypeError('Body not allowed for GET or HEAD requests'); + } + + this._initBody(body); + + if (this.method === 'GET' || this.method === 'HEAD') { + if (options.cache === 'no-store' || options.cache === 'no-cache') { + // Search for a '_' parameter in the query string + var reParamSearch = /([?&])_=[^&]*/; + + if (reParamSearch.test(this.url)) { + // If it already exists then set the value with the current time + this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime()); + } else { + // Otherwise add a new '_' parameter to the end with the current time + var reQueryString = /\?/; + this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime(); + } + } + } + } + + Request.prototype.clone = function () { + return new Request(this, { + body: this._bodyInit + }); + }; + + function decode(body) { + var form = new FormData(); + body.trim().split('&').forEach(function (bytes) { + if (bytes) { + var split = bytes.split('='); + var name = split.shift().replace(/\+/g, ' '); + var value = split.join('=').replace(/\+/g, ' '); + form.append(decodeURIComponent(name), decodeURIComponent(value)); + } + }); + return form; + } + + function parseHeaders(rawHeaders) { + var headers = new Headers(); // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space + // https://tools.ietf.org/html/rfc7230#section-3.2 + + var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' '); // Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill + // https://github.com/github/fetch/issues/748 + // https://github.com/zloirock/core-js/issues/751 + + preProcessedHeaders.split('\r').map(function (header) { + return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header; + }).forEach(function (line) { + var parts = line.split(':'); + var key = parts.shift().trim(); + + if (key) { + var value = parts.join(':').trim(); + headers.append(key, value); + } + }); + return headers; + } + + Body.call(Request.prototype); + function Response(bodyInit, options) { + if (!(this instanceof Response)) { + throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.'); + } + + if (!options) { + options = {}; + } + + this.type = 'default'; + this.status = options.status === undefined ? 200 : options.status; + this.ok = this.status >= 200 && this.status < 300; + this.statusText = options.statusText === undefined ? '' : '' + options.statusText; + this.headers = new Headers(options.headers); + this.url = options.url || ''; + + this._initBody(bodyInit); + } + Body.call(Response.prototype); + + Response.prototype.clone = function () { + return new Response(this._bodyInit, { + status: this.status, + statusText: this.statusText, + headers: new Headers(this.headers), + url: this.url + }); + }; + + Response.error = function () { + var response = new Response(null, { + status: 0, + statusText: '' + }); + response.type = 'error'; + return response; + }; + + var redirectStatuses = [301, 302, 303, 307, 308]; + + Response.redirect = function (url, status) { + if (redirectStatuses.indexOf(status) === -1) { + throw new RangeError('Invalid status code'); + } + + return new Response(null, { + status: status, + headers: { + location: url + } + }); + }; + + var DOMException$1 = global$8.DOMException; + + try { + new DOMException$1(); + } catch (err) { + DOMException$1 = function DOMException(message, name) { + this.message = message; + this.name = name; + var error = Error(message); + this.stack = error.stack; + }; + + DOMException$1.prototype = Object.create(Error.prototype); + DOMException$1.prototype.constructor = DOMException$1; + } + + function fetch$1(input, init) { + return new Promise(function (resolve, reject) { + var request = new Request(input, init); + + if (request.signal && request.signal.aborted) { + return reject(new DOMException$1('Aborted', 'AbortError')); + } + + var xhr = new XMLHttpRequest(); + + function abortXhr() { + xhr.abort(); + } + + xhr.onload = function () { + var options = { + status: xhr.status, + statusText: xhr.statusText, + headers: parseHeaders(xhr.getAllResponseHeaders() || '') + }; + options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL'); + var body = 'response' in xhr ? xhr.response : xhr.responseText; + setTimeout(function () { + resolve(new Response(body, options)); + }, 0); + }; + + xhr.onerror = function () { + setTimeout(function () { + reject(new TypeError('Network request failed')); + }, 0); + }; + + xhr.ontimeout = function () { + setTimeout(function () { + reject(new TypeError('Network request failed')); + }, 0); + }; + + xhr.onabort = function () { + setTimeout(function () { + reject(new DOMException$1('Aborted', 'AbortError')); + }, 0); + }; + + function fixUrl(url) { + try { + return url === '' && global$8.location.href ? global$8.location.href : url; + } catch (e) { + return url; + } + } + + xhr.open(request.method, fixUrl(request.url), true); + + if (request.credentials === 'include') { + xhr.withCredentials = true; + } else if (request.credentials === 'omit') { + xhr.withCredentials = false; + } + + if ('responseType' in xhr) { + if (support.blob) { + xhr.responseType = 'blob'; + } else if (support.arrayBuffer && request.headers.get('Content-Type') && request.headers.get('Content-Type').indexOf('application/octet-stream') !== -1) { + xhr.responseType = 'arraybuffer'; + } + } + + if (init && _typeof(init.headers) === 'object' && !(init.headers instanceof Headers)) { + Object.getOwnPropertyNames(init.headers).forEach(function (name) { + xhr.setRequestHeader(name, normalizeValue(init.headers[name])); + }); + } else { + request.headers.forEach(function (value, name) { + xhr.setRequestHeader(name, value); + }); + } + + if (request.signal) { + request.signal.addEventListener('abort', abortXhr); + + xhr.onreadystatechange = function () { + // DONE (success or failure) + if (xhr.readyState === 4) { + request.signal.removeEventListener('abort', abortXhr); + } + }; + } + + xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit); + }); + } + fetch$1.polyfill = true; + + if (!global$8.fetch) { + global$8.fetch = fetch$1; + global$8.Headers = Headers; + global$8.Request = Request; + global$8.Response = Response; + } + + var $$L = _export; + var DESCRIPTORS$7 = descriptors; + var objectDefinePropertyModile = objectDefineProperty; + + // `Object.defineProperty` method + // https://tc39.es/ecma262/#sec-object.defineproperty + $$L({ target: 'Object', stat: true, forced: !DESCRIPTORS$7, sham: !DESCRIPTORS$7 }, { + defineProperty: objectDefinePropertyModile.f + }); + + var $$K = _export; + var setPrototypeOf = objectSetPrototypeOf; + + // `Object.setPrototypeOf` method + // https://tc39.es/ecma262/#sec-object.setprototypeof + $$K({ target: 'Object', stat: true }, { + setPrototypeOf: setPrototypeOf + }); + + var $$J = _export; + var fails$i = fails$N; + var toObject$5 = toObject$i; + var nativeGetPrototypeOf = objectGetPrototypeOf; + var CORRECT_PROTOTYPE_GETTER = correctPrototypeGetter; + + var FAILS_ON_PRIMITIVES$3 = fails$i(function () { nativeGetPrototypeOf(1); }); + + // `Object.getPrototypeOf` method + // https://tc39.es/ecma262/#sec-object.getprototypeof + $$J({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$3, sham: !CORRECT_PROTOTYPE_GETTER }, { + getPrototypeOf: function getPrototypeOf(it) { + return nativeGetPrototypeOf(toObject$5(it)); + } + }); + + var aFunction$2 = aFunction$9; + var isObject$a = isObject$r; + + var slice$2 = [].slice; + var factories = {}; + + var construct = function (C, argsLength, args) { + if (!(argsLength in factories)) { + for (var list = [], i = 0; i < argsLength; i++) list[i] = 'a[' + i + ']'; + // eslint-disable-next-line no-new-func -- we have no proper alternatives, IE8- only + factories[argsLength] = Function('C,a', 'return new C(' + list.join(',') + ')'); + } return factories[argsLength](C, args); + }; + + // `Function.prototype.bind` method implementation + // https://tc39.es/ecma262/#sec-function.prototype.bind + var functionBind = Function.bind || function bind(that /* , ...args */) { + var fn = aFunction$2(this); + var partArgs = slice$2.call(arguments, 1); + var boundFunction = function bound(/* args... */) { + var args = partArgs.concat(slice$2.call(arguments)); + return this instanceof boundFunction ? construct(fn, args.length, args) : fn.apply(that, args); + }; + if (isObject$a(fn.prototype)) boundFunction.prototype = fn.prototype; + return boundFunction; + }; + + var $$I = _export; + var getBuiltIn$1 = getBuiltIn$9; + var aFunction$1 = aFunction$9; + var anObject$3 = anObject$m; + var isObject$9 = isObject$r; + var create$5 = objectCreate; + var bind$4 = functionBind; + var fails$h = fails$N; + + var nativeConstruct = getBuiltIn$1('Reflect', 'construct'); + + // `Reflect.construct` method + // https://tc39.es/ecma262/#sec-reflect.construct + // MS Edge supports only 2 arguments and argumentsList argument is optional + // FF Nightly sets third argument as `new.target`, but does not create `this` from it + var NEW_TARGET_BUG = fails$h(function () { + function F() { /* empty */ } + return !(nativeConstruct(function () { /* empty */ }, [], F) instanceof F); + }); + var ARGS_BUG = !fails$h(function () { + nativeConstruct(function () { /* empty */ }); + }); + var FORCED$a = NEW_TARGET_BUG || ARGS_BUG; + + $$I({ target: 'Reflect', stat: true, forced: FORCED$a, sham: FORCED$a }, { + construct: function construct(Target, args /* , newTarget */) { + aFunction$1(Target); + anObject$3(args); + var newTarget = arguments.length < 3 ? Target : aFunction$1(arguments[2]); + if (ARGS_BUG && !NEW_TARGET_BUG) return nativeConstruct(Target, args, newTarget); + if (Target == newTarget) { + // w/o altered newTarget, optimization for 0-4 arguments + switch (args.length) { + case 0: return new Target(); + case 1: return new Target(args[0]); + case 2: return new Target(args[0], args[1]); + case 3: return new Target(args[0], args[1], args[2]); + case 4: return new Target(args[0], args[1], args[2], args[3]); + } + // w/o altered newTarget, lot of arguments case + var $args = [null]; + $args.push.apply($args, args); + return new (bind$4.apply(Target, $args))(); + } + // with altered newTarget, not support built-in constructors + var proto = newTarget.prototype; + var instance = create$5(isObject$9(proto) ? proto : Object.prototype); + var result = Function.apply.call(Target, instance, args); + return isObject$9(result) ? result : instance; + } + }); + + var $$H = _export; + var isObject$8 = isObject$r; + var anObject$2 = anObject$m; + var has$3 = has$j; + var getOwnPropertyDescriptorModule = objectGetOwnPropertyDescriptor; + var getPrototypeOf = objectGetPrototypeOf; + + // `Reflect.get` method + // https://tc39.es/ecma262/#sec-reflect.get + function get$3(target, propertyKey /* , receiver */) { + var receiver = arguments.length < 3 ? target : arguments[2]; + var descriptor, prototype; + if (anObject$2(target) === receiver) return target[propertyKey]; + if (descriptor = getOwnPropertyDescriptorModule.f(target, propertyKey)) return has$3(descriptor, 'value') + ? descriptor.value + : descriptor.get === undefined + ? undefined + : descriptor.get.call(receiver); + if (isObject$8(prototype = getPrototypeOf(target))) return get$3(prototype, propertyKey, receiver); + } + + $$H({ target: 'Reflect', stat: true }, { + get: get$3 + }); + + var $$G = _export; + var fails$g = fails$N; + var toIndexedObject$1 = toIndexedObject$b; + var nativeGetOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f; + var DESCRIPTORS$6 = descriptors; + + var FAILS_ON_PRIMITIVES$2 = fails$g(function () { nativeGetOwnPropertyDescriptor(1); }); + var FORCED$9 = !DESCRIPTORS$6 || FAILS_ON_PRIMITIVES$2; + + // `Object.getOwnPropertyDescriptor` method + // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor + $$G({ target: 'Object', stat: true, forced: FORCED$9, sham: !DESCRIPTORS$6 }, { + getOwnPropertyDescriptor: function getOwnPropertyDescriptor(it, key) { + return nativeGetOwnPropertyDescriptor(toIndexedObject$1(it), key); + } + }); + + var $$F = _export; + var toAbsoluteIndex$1 = toAbsoluteIndex$8; + var toInteger$2 = toInteger$b; + var toLength$6 = toLength$q; + var toObject$4 = toObject$i; + var arraySpeciesCreate$1 = arraySpeciesCreate$3; + var createProperty$1 = createProperty$4; + var arrayMethodHasSpeciesSupport$2 = arrayMethodHasSpeciesSupport$5; + + var HAS_SPECIES_SUPPORT$1 = arrayMethodHasSpeciesSupport$2('splice'); + + var max$1 = Math.max; + var min$3 = Math.min; + var MAX_SAFE_INTEGER$1 = 0x1FFFFFFFFFFFFF; + var MAXIMUM_ALLOWED_LENGTH_EXCEEDED = 'Maximum allowed length exceeded'; + + // `Array.prototype.splice` method + // https://tc39.es/ecma262/#sec-array.prototype.splice + // with adding support of @@species + $$F({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$1 }, { + splice: function splice(start, deleteCount /* , ...items */) { + var O = toObject$4(this); + var len = toLength$6(O.length); + var actualStart = toAbsoluteIndex$1(start, len); + var argumentsLength = arguments.length; + var insertCount, actualDeleteCount, A, k, from, to; + if (argumentsLength === 0) { + insertCount = actualDeleteCount = 0; + } else if (argumentsLength === 1) { + insertCount = 0; + actualDeleteCount = len - actualStart; + } else { + insertCount = argumentsLength - 2; + actualDeleteCount = min$3(max$1(toInteger$2(deleteCount), 0), len - actualStart); + } + if (len + insertCount - actualDeleteCount > MAX_SAFE_INTEGER$1) { + throw TypeError(MAXIMUM_ALLOWED_LENGTH_EXCEEDED); + } + A = arraySpeciesCreate$1(O, actualDeleteCount); + for (k = 0; k < actualDeleteCount; k++) { + from = actualStart + k; + if (from in O) createProperty$1(A, k, O[from]); + } + A.length = actualDeleteCount; + if (insertCount < actualDeleteCount) { + for (k = actualStart; k < len - actualDeleteCount; k++) { + from = k + actualDeleteCount; + to = k + insertCount; + if (from in O) O[to] = O[from]; + else delete O[to]; + } + for (k = len; k > len - actualDeleteCount + insertCount; k--) delete O[k - 1]; + } else if (insertCount > actualDeleteCount) { + for (k = len - actualDeleteCount; k > actualStart; k--) { + from = k + actualDeleteCount - 1; + to = k + insertCount - 1; + if (from in O) O[to] = O[from]; + else delete O[to]; + } + } + for (k = 0; k < insertCount; k++) { + O[k + actualStart] = arguments[k + 2]; + } + O.length = len - actualDeleteCount + insertCount; + return A; + } + }); + + var defineWellKnownSymbol$1 = defineWellKnownSymbol$4; + + // `Symbol.toStringTag` well-known symbol + // https://tc39.es/ecma262/#sec-symbol.tostringtag + defineWellKnownSymbol$1('toStringTag'); + + var global$7 = global$F; + var setToStringTag$2 = setToStringTag$a; + + // JSON[@@toStringTag] property + // https://tc39.es/ecma262/#sec-json-@@tostringtag + setToStringTag$2(global$7.JSON, 'JSON', true); + + var setToStringTag$1 = setToStringTag$a; + + // Math[@@toStringTag] property + // https://tc39.es/ecma262/#sec-math-@@tostringtag + setToStringTag$1(Math, 'Math', true); + + (function (factory) { + factory(); + })(function () { + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function"); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + writable: true, + configurable: true + } + }); + if (superClass) _setPrototypeOf(subClass, superClass); + } + + function _getPrototypeOf(o) { + _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { + return o.__proto__ || Object.getPrototypeOf(o); + }; + return _getPrototypeOf(o); + } + + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }; + + return _setPrototypeOf(o, p); + } + + function _isNativeReflectConstruct() { + if (typeof Reflect === "undefined" || !Reflect.construct) return false; + if (Reflect.construct.sham) return false; + if (typeof Proxy === "function") return true; + + try { + Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); + return true; + } catch (e) { + return false; + } + } + + function _assertThisInitialized(self) { + if (self === void 0) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return self; + } + + function _possibleConstructorReturn(self, call) { + if (call && (_typeof(call) === "object" || typeof call === "function")) { + return call; + } + + return _assertThisInitialized(self); + } + + function _createSuper(Derived) { + var hasNativeReflectConstruct = _isNativeReflectConstruct(); + + return function _createSuperInternal() { + var Super = _getPrototypeOf(Derived), + result; + + if (hasNativeReflectConstruct) { + var NewTarget = _getPrototypeOf(this).constructor; + + result = Reflect.construct(Super, arguments, NewTarget); + } else { + result = Super.apply(this, arguments); + } + + return _possibleConstructorReturn(this, result); + }; + } + + function _superPropBase(object, property) { + while (!Object.prototype.hasOwnProperty.call(object, property)) { + object = _getPrototypeOf(object); + if (object === null) break; + } + + return object; + } + + function _get(target, property, receiver) { + if (typeof Reflect !== "undefined" && Reflect.get) { + _get = Reflect.get; + } else { + _get = function _get(target, property, receiver) { + var base = _superPropBase(target, property); + + if (!base) return; + var desc = Object.getOwnPropertyDescriptor(base, property); + + if (desc.get) { + return desc.get.call(receiver); + } + + return desc.value; + }; + } + + return _get(target, property, receiver || target); + } + + var Emitter = /*#__PURE__*/function () { + function Emitter() { + _classCallCheck(this, Emitter); + + Object.defineProperty(this, 'listeners', { + value: {}, + writable: true, + configurable: true + }); + } + + _createClass(Emitter, [{ + key: "addEventListener", + value: function addEventListener(type, callback, options) { + if (!(type in this.listeners)) { + this.listeners[type] = []; + } + + this.listeners[type].push({ + callback: callback, + options: options + }); + } + }, { + key: "removeEventListener", + value: function removeEventListener(type, callback) { + if (!(type in this.listeners)) { + return; + } + + var stack = this.listeners[type]; + + for (var i = 0, l = stack.length; i < l; i++) { + if (stack[i].callback === callback) { + stack.splice(i, 1); + return; + } + } + } + }, { + key: "dispatchEvent", + value: function dispatchEvent(event) { + if (!(event.type in this.listeners)) { + return; + } + + var stack = this.listeners[event.type]; + var stackToCall = stack.slice(); + + for (var i = 0, l = stackToCall.length; i < l; i++) { + var listener = stackToCall[i]; + + try { + listener.callback.call(this, event); + } catch (e) { + Promise.resolve().then(function () { + throw e; + }); + } + + if (listener.options && listener.options.once) { + this.removeEventListener(event.type, listener.callback); + } + } + + return !event.defaultPrevented; + } + }]); + + return Emitter; + }(); + + var AbortSignal = /*#__PURE__*/function (_Emitter) { + _inherits(AbortSignal, _Emitter); + + var _super = _createSuper(AbortSignal); + + function AbortSignal() { + var _this; + + _classCallCheck(this, AbortSignal); + + _this = _super.call(this); // Some versions of babel does not transpile super() correctly for IE <= 10, if the parent + // constructor has failed to run, then "this.listeners" will still be undefined and then we call + // the parent constructor directly instead as a workaround. For general details, see babel bug: + // https://github.com/babel/babel/issues/3041 + // This hack was added as a fix for the issue described here: + // https://github.com/Financial-Times/polyfill-library/pull/59#issuecomment-477558042 + + if (!_this.listeners) { + Emitter.call(_assertThisInitialized(_this)); + } // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and + // we want Object.keys(new AbortController().signal) to be [] for compat with the native impl + + + Object.defineProperty(_assertThisInitialized(_this), 'aborted', { + value: false, + writable: true, + configurable: true + }); + Object.defineProperty(_assertThisInitialized(_this), 'onabort', { + value: null, + writable: true, + configurable: true + }); + return _this; + } + + _createClass(AbortSignal, [{ + key: "toString", + value: function toString() { + return '[object AbortSignal]'; + } + }, { + key: "dispatchEvent", + value: function dispatchEvent(event) { + if (event.type === 'abort') { + this.aborted = true; + + if (typeof this.onabort === 'function') { + this.onabort.call(this, event); + } + } + + _get(_getPrototypeOf(AbortSignal.prototype), "dispatchEvent", this).call(this, event); + } + }]); + + return AbortSignal; + }(Emitter); + + var AbortController = /*#__PURE__*/function () { + function AbortController() { + _classCallCheck(this, AbortController); // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and + // we want Object.keys(new AbortController()) to be [] for compat with the native impl + + + Object.defineProperty(this, 'signal', { + value: new AbortSignal(), + writable: true, + configurable: true + }); + } + + _createClass(AbortController, [{ + key: "abort", + value: function abort() { + var event; + + try { + event = new Event('abort'); + } catch (e) { + if (typeof document !== 'undefined') { + if (!document.createEvent) { + // For Internet Explorer 8: + event = document.createEventObject(); + event.type = 'abort'; + } else { + // For Internet Explorer 11: + event = document.createEvent('Event'); + event.initEvent('abort', false, false); + } + } else { + // Fallback where document isn't available: + event = { + type: 'abort', + bubbles: false, + cancelable: false + }; + } + } + + this.signal.dispatchEvent(event); + } + }, { + key: "toString", + value: function toString() { + return '[object AbortController]'; + } + }]); + + return AbortController; + }(); + + if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { + // These are necessary to make sure that we get correct output for: + // Object.prototype.toString.call(new AbortController()) + AbortController.prototype[Symbol.toStringTag] = 'AbortController'; + AbortSignal.prototype[Symbol.toStringTag] = 'AbortSignal'; + } + + function polyfillNeeded(self) { + if (self.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) { + console.log('__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL=true is set, will force install polyfill'); + return true; + } // Note that the "unfetch" minimal fetch polyfill defines fetch() without + // defining window.Request, and this polyfill need to work on top of unfetch + // so the below feature detection needs the !self.AbortController part. + // The Request.prototype check is also needed because Safari versions 11.1.2 + // up to and including 12.1.x has a window.AbortController present but still + // does NOT correctly implement abortable fetch: + // https://bugs.webkit.org/show_bug.cgi?id=174980#c2 + + + return typeof self.Request === 'function' && !self.Request.prototype.hasOwnProperty('signal') || !self.AbortController; + } + /** + * Note: the "fetch.Request" default value is available for fetch imported from + * the "node-fetch" package and not in browsers. This is OK since browsers + * will be importing umd-polyfill.js from that path "self" is passed the + * decorator so the default value will not be used (because browsers that define + * fetch also has Request). One quirky setup where self.fetch exists but + * self.Request does not is when the "unfetch" minimal fetch polyfill is used + * on top of IE11; for this case the browser will try to use the fetch.Request + * default value which in turn will be undefined but then then "if (Request)" + * will ensure that you get a patched fetch but still no Request (as expected). + * @param {fetch, Request = fetch.Request} + * @returns {fetch: abortableFetch, Request: AbortableRequest} + */ + + + function abortableFetchDecorator(patchTargets) { + if ('function' === typeof patchTargets) { + patchTargets = { + fetch: patchTargets + }; + } + + var _patchTargets = patchTargets, + fetch = _patchTargets.fetch, + _patchTargets$Request = _patchTargets.Request, + NativeRequest = _patchTargets$Request === void 0 ? fetch.Request : _patchTargets$Request, + NativeAbortController = _patchTargets.AbortController, + _patchTargets$__FORCE = _patchTargets.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL, + __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL = _patchTargets$__FORCE === void 0 ? false : _patchTargets$__FORCE; + + if (!polyfillNeeded({ + fetch: fetch, + Request: NativeRequest, + AbortController: NativeAbortController, + __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL: __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL + })) { + return { + fetch: fetch, + Request: Request + }; + } + + var Request = NativeRequest; // Note that the "unfetch" minimal fetch polyfill defines fetch() without + // defining window.Request, and this polyfill need to work on top of unfetch + // hence we only patch it if it's available. Also we don't patch it if signal + // is already available on the Request prototype because in this case support + // is present and the patching below can cause a crash since it assigns to + // request.signal which is technically a read-only property. This latter error + // happens when you run the main5.js node-fetch example in the repo + // "abortcontroller-polyfill-examples". The exact error is: + // request.signal = init.signal; + // ^ + // TypeError: Cannot set property signal of # which has only a getter + + if (Request && !Request.prototype.hasOwnProperty('signal') || __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) { + Request = function Request(input, init) { + var signal; + + if (init && init.signal) { + signal = init.signal; // Never pass init.signal to the native Request implementation when the polyfill has + // been installed because if we're running on top of a browser with a + // working native AbortController (i.e. the polyfill was installed due to + // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our + // fake AbortSignal to the native fetch will trigger: + // TypeError: Failed to construct 'Request': member signal is not of type AbortSignal. + + delete init.signal; + } + + var request = new NativeRequest(input, init); + + if (signal) { + Object.defineProperty(request, 'signal', { + writable: false, + enumerable: false, + configurable: true, + value: signal + }); + } + + return request; + }; + + Request.prototype = NativeRequest.prototype; + } + + var realFetch = fetch; + + var abortableFetch = function abortableFetch(input, init) { + var signal = Request && Request.prototype.isPrototypeOf(input) ? input.signal : init ? init.signal : undefined; + + if (signal) { + var abortError; + + try { + abortError = new DOMException('Aborted', 'AbortError'); + } catch (err) { + // IE 11 does not support calling the DOMException constructor, use a + // regular error object on it instead. + abortError = new Error('Aborted'); + abortError.name = 'AbortError'; + } // Return early if already aborted, thus avoiding making an HTTP request + + + if (signal.aborted) { + return Promise.reject(abortError); + } // Turn an event into a promise, reject it once `abort` is dispatched + + + var cancellation = new Promise(function (_, reject) { + signal.addEventListener('abort', function () { + return reject(abortError); + }, { + once: true + }); + }); + + if (init && init.signal) { + // Never pass .signal to the native implementation when the polyfill has + // been installed because if we're running on top of a browser with a + // working native AbortController (i.e. the polyfill was installed due to + // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our + // fake AbortSignal to the native fetch will trigger: + // TypeError: Failed to execute 'fetch' on 'Window': member signal is not of type AbortSignal. + delete init.signal; + } // Return the fastest promise (don't need to wait for request to finish) + + + return Promise.race([cancellation, realFetch(input, init)]); + } + + return realFetch(input, init); + }; + + return { + fetch: abortableFetch, + Request: Request + }; + } + + (function (self) { + if (!polyfillNeeded(self)) { + return; + } + + if (!self.fetch) { + console.warn('fetch() is not available, cannot install abortcontroller-polyfill'); + return; + } + + var _abortableFetch = abortableFetchDecorator(self), + fetch = _abortableFetch.fetch, + Request = _abortableFetch.Request; + + self.fetch = fetch; + self.Request = Request; + Object.defineProperty(self, 'AbortController', { + writable: true, + enumerable: false, + configurable: true, + value: AbortController + }); + Object.defineProperty(self, 'AbortSignal', { + writable: true, + enumerable: false, + configurable: true, + value: AbortSignal + }); + })(typeof self !== 'undefined' ? self : commonjsGlobal); + }); + + function actionAddEntity(way) { + return function (graph) { + return graph.replace(way); + }; + } + + var $$E = _export; + var fails$f = fails$N; + var isArray$1 = isArray$6; + var isObject$7 = isObject$r; + var toObject$3 = toObject$i; + var toLength$5 = toLength$q; + var createProperty = createProperty$4; + var arraySpeciesCreate = arraySpeciesCreate$3; + var arrayMethodHasSpeciesSupport$1 = arrayMethodHasSpeciesSupport$5; + var wellKnownSymbol$2 = wellKnownSymbol$s; + var V8_VERSION = engineV8Version; + + var IS_CONCAT_SPREADABLE = wellKnownSymbol$2('isConcatSpreadable'); + var MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF; + var MAXIMUM_ALLOWED_INDEX_EXCEEDED = 'Maximum allowed index exceeded'; + + // We can't use this feature detection in V8 since it causes + // deoptimization and serious performance degradation + // https://github.com/zloirock/core-js/issues/679 + var IS_CONCAT_SPREADABLE_SUPPORT = V8_VERSION >= 51 || !fails$f(function () { + var array = []; + array[IS_CONCAT_SPREADABLE] = false; + return array.concat()[0] !== array; + }); + + var SPECIES_SUPPORT = arrayMethodHasSpeciesSupport$1('concat'); + + var isConcatSpreadable = function (O) { + if (!isObject$7(O)) return false; + var spreadable = O[IS_CONCAT_SPREADABLE]; + return spreadable !== undefined ? !!spreadable : isArray$1(O); + }; + + var FORCED$8 = !IS_CONCAT_SPREADABLE_SUPPORT || !SPECIES_SUPPORT; + + // `Array.prototype.concat` method + // https://tc39.es/ecma262/#sec-array.prototype.concat + // with adding support of @@isConcatSpreadable and @@species + $$E({ target: 'Array', proto: true, forced: FORCED$8 }, { + // eslint-disable-next-line no-unused-vars -- required for `.length` + concat: function concat(arg) { + var O = toObject$3(this); + var A = arraySpeciesCreate(O, 0); + var n = 0; + var i, k, length, len, E; + for (i = -1, length = arguments.length; i < length; i++) { + E = i === -1 ? O : arguments[i]; + if (isConcatSpreadable(E)) { + len = toLength$5(E.length); + if (n + len > MAX_SAFE_INTEGER) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED); + for (k = 0; k < len; k++, n++) if (k in E) createProperty(A, n, E[k]); + } else { + if (n >= MAX_SAFE_INTEGER) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED); + createProperty(A, n++, E); + } + } + A.length = n; + return A; + } + }); + + var $$D = _export; + var assign$1 = objectAssign; + + // `Object.assign` method + // https://tc39.es/ecma262/#sec-object.assign + // eslint-disable-next-line es/no-object-assign -- required for testing + $$D({ target: 'Object', stat: true, forced: Object.assign !== assign$1 }, { + assign: assign$1 + }); + + var $$C = _export; + var $filter = arrayIteration.filter; + var arrayMethodHasSpeciesSupport = arrayMethodHasSpeciesSupport$5; + + var HAS_SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('filter'); + + // `Array.prototype.filter` method + // https://tc39.es/ecma262/#sec-array.prototype.filter + // with adding support of @@species + $$C({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT }, { + filter: function filter(callbackfn /* , thisArg */) { + return $filter(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); + } + }); + + var $$B = _export; + var toObject$2 = toObject$i; + var nativeKeys = objectKeys$4; + var fails$e = fails$N; + + var FAILS_ON_PRIMITIVES$1 = fails$e(function () { nativeKeys(1); }); + + // `Object.keys` method + // https://tc39.es/ecma262/#sec-object.keys + $$B({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$1 }, { + keys: function keys(it) { + return nativeKeys(toObject$2(it)); + } + }); + + var $$A = _export; + var isArray = isArray$6; + + var nativeReverse = [].reverse; + var test$1 = [1, 2]; + + // `Array.prototype.reverse` method + // https://tc39.es/ecma262/#sec-array.prototype.reverse + // fix for Safari 12.0 bug + // https://bugs.webkit.org/show_bug.cgi?id=188794 + $$A({ target: 'Array', proto: true, forced: String(test$1) === String(test$1.reverse()) }, { + reverse: function reverse() { + // eslint-disable-next-line no-self-assign -- dirty hack + if (isArray(this)) this.length = this.length; + return nativeReverse.call(this); + } + }); + + var global$6 = global$F; + var trim$4 = stringTrim.trim; + var whitespaces$1 = whitespaces$4; + + var $parseFloat = global$6.parseFloat; + var FORCED$7 = 1 / $parseFloat(whitespaces$1 + '-0') !== -Infinity; + + // `parseFloat` method + // https://tc39.es/ecma262/#sec-parsefloat-string + var numberParseFloat = FORCED$7 ? function parseFloat(string) { + var trimmedString = trim$4(String(string)); + var result = $parseFloat(trimmedString); + return result === 0 && trimmedString.charAt(0) == '-' ? -0 : result; + } : $parseFloat; + + var $$z = _export; + var parseFloatImplementation = numberParseFloat; + + // `parseFloat` method + // https://tc39.es/ecma262/#sec-parsefloat-string + $$z({ global: true, forced: parseFloat != parseFloatImplementation }, { + parseFloat: parseFloatImplementation + }); + + /* + Order the nodes of a way in reverse order and reverse any direction dependent tags + other than `oneway`. (We assume that correcting a backwards oneway is the primary + reason for reversing a way.) + + In addition, numeric-valued `incline` tags are negated. + + The JOSM implementation was used as a guide, but transformations that were of unclear benefit + or adjusted tags that don't seem to be used in practice were omitted. + + References: + http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right + http://wiki.openstreetmap.org/wiki/Key:direction#Steps + http://wiki.openstreetmap.org/wiki/Key:incline + http://wiki.openstreetmap.org/wiki/Route#Members + http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java + http://wiki.openstreetmap.org/wiki/Tag:highway%3Dstop + http://wiki.openstreetmap.org/wiki/Key:traffic_sign#On_a_way_or_area + */ + function actionReverse(entityID, options) { + var ignoreKey = /^.*(_|:)?(description|name|note|website|ref|source|comment|watch|attribution)(_|:)?/; + var numeric = /^([+\-]?)(?=[\d.])/; + var directionKey = /direction$/; + var turn_lanes = /^turn:lanes:?/; + var keyReplacements = [[/:right$/, ':left'], [/:left$/, ':right'], [/:forward$/, ':backward'], [/:backward$/, ':forward'], [/:right:/, ':left:'], [/:left:/, ':right:'], [/:forward:/, ':backward:'], [/:backward:/, ':forward:']]; + var valueReplacements = { + left: 'right', + right: 'left', + up: 'down', + down: 'up', + forward: 'backward', + backward: 'forward', + forwards: 'backward', + backwards: 'forward' + }; + var roleReplacements = { + forward: 'backward', + backward: 'forward', + forwards: 'backward', + backwards: 'forward' + }; + var onewayReplacements = { + yes: '-1', + '1': '-1', + '-1': 'yes' + }; + var compassReplacements = { + N: 'S', + NNE: 'SSW', + NE: 'SW', + ENE: 'WSW', + E: 'W', + ESE: 'WNW', + SE: 'NW', + SSE: 'NNW', + S: 'N', + SSW: 'NNE', + SW: 'NE', + WSW: 'ENE', + W: 'E', + WNW: 'ESE', + NW: 'SE', + NNW: 'SSE' + }; + + function reverseKey(key) { + for (var i = 0; i < keyReplacements.length; ++i) { + var replacement = keyReplacements[i]; + + if (replacement[0].test(key)) { + return key.replace(replacement[0], replacement[1]); + } + } + + return key; + } + + function reverseValue(key, value, includeAbsolute) { + if (ignoreKey.test(key)) return value; // Turn lanes are left/right to key (not way) direction - #5674 + + if (turn_lanes.test(key)) { + return value; + } else if (key === 'incline' && numeric.test(value)) { + return value.replace(numeric, function (_, sign) { + return sign === '-' ? '' : '-'; + }); + } else if (options && options.reverseOneway && key === 'oneway') { + return onewayReplacements[value] || value; + } else if (includeAbsolute && directionKey.test(key)) { + if (compassReplacements[value]) return compassReplacements[value]; + var degrees = parseFloat(value); + + if (typeof degrees === 'number' && !isNaN(degrees)) { + if (degrees < 180) { + degrees += 180; + } else { + degrees -= 180; + } + + return degrees.toString(); + } + } + + return valueReplacements[value] || value; + } // Reverse the direction of tags attached to the nodes - #3076 + + + function reverseNodeTags(graph, nodeIDs) { + for (var i = 0; i < nodeIDs.length; i++) { + var node = graph.hasEntity(nodeIDs[i]); + if (!node || !Object.keys(node.tags).length) continue; + var tags = {}; + + for (var key in node.tags) { + tags[reverseKey(key)] = reverseValue(key, node.tags[key], node.id === entityID); + } + + graph = graph.replace(node.update({ + tags: tags + })); + } + + return graph; + } + + function reverseWay(graph, way) { + var nodes = way.nodes.slice().reverse(); + var tags = {}; + var role; + + for (var key in way.tags) { + tags[reverseKey(key)] = reverseValue(key, way.tags[key]); + } + + graph.parentRelations(way).forEach(function (relation) { + relation.members.forEach(function (member, index) { + if (member.id === way.id && (role = roleReplacements[member.role])) { + relation = relation.updateMember({ + role: role + }, index); + graph = graph.replace(relation); + } + }); + }); // Reverse any associated directions on nodes on the way and then replace + // the way itself with the reversed node ids and updated way tags + + return reverseNodeTags(graph, nodes).replace(way.update({ + nodes: nodes, + tags: tags + })); + } + + var action = function action(graph) { + var entity = graph.entity(entityID); + + if (entity.type === 'way') { + return reverseWay(graph, entity); + } + + return reverseNodeTags(graph, [entityID]); + }; + + action.disabled = function (graph) { + var entity = graph.hasEntity(entityID); + if (!entity || entity.type === 'way') return false; + + for (var key in entity.tags) { + var value = entity.tags[key]; + + if (reverseKey(key) !== key || reverseValue(key, value, true) !== value) { + return false; + } + } + + return 'nondirectional_node'; + }; + + action.entityID = function () { + return entityID; + }; + + return action; + } + + function osmIsInterestingTag(key) { + return key !== 'attribution' && key !== 'created_by' && key !== 'source' && key !== 'odbl' && key.indexOf('source:') !== 0 && key.indexOf('source_ref') !== 0 && // purposely exclude colon + key.indexOf('tiger:') !== 0; + } + var osmAreaKeys = {}; + function osmSetAreaKeys(value) { + osmAreaKeys = value; + } // returns an object with the tag from `tags` that implies an area geometry, if any + + function osmTagSuggestingArea(tags) { + if (tags.area === 'yes') return { + area: 'yes' + }; + if (tags.area === 'no') return null; // `highway` and `railway` are typically linear features, but there + // are a few exceptions that should be treated as areas, even in the + // absence of a proper `area=yes` or `areaKeys` tag.. see #4194 + + var lineKeys = { + highway: { + rest_area: true, + services: true + }, + railway: { + roundhouse: true, + station: true, + traverser: true, + turntable: true, + wash: true + } + }; + var returnTags = {}; + + for (var key in tags) { + if (key in osmAreaKeys && !(tags[key] in osmAreaKeys[key])) { + returnTags[key] = tags[key]; + return returnTags; + } + + if (key in lineKeys && tags[key] in lineKeys[key]) { + returnTags[key] = tags[key]; + return returnTags; + } + } + + return null; + } // Tags that indicate a node can be a standalone point + // e.g. { amenity: { bar: true, parking: true, ... } ... } + + var osmPointTags = {}; + function osmSetPointTags(value) { + osmPointTags = value; + } // Tags that indicate a node can be part of a way + // e.g. { amenity: { parking: true, ... }, highway: { stop: true ... } ... } + + var osmVertexTags = {}; + function osmSetVertexTags(value) { + osmVertexTags = value; + } + function osmNodeGeometriesForTags(nodeTags) { + var geometries = {}; + + for (var key in nodeTags) { + if (osmPointTags[key] && (osmPointTags[key]['*'] || osmPointTags[key][nodeTags[key]])) { + geometries.point = true; + } + + if (osmVertexTags[key] && (osmVertexTags[key]['*'] || osmVertexTags[key][nodeTags[key]])) { + geometries.vertex = true; + } // break early if both are already supported + + + if (geometries.point && geometries.vertex) break; + } + + return geometries; + } + var osmOneWayTags = { + 'aerialway': { + 'chair_lift': true, + 'drag_lift': true, + 'j-bar': true, + 'magic_carpet': true, + 'mixed_lift': true, + 'platter': true, + 'rope_tow': true, + 't-bar': true, + 'zip_line': true + }, + 'highway': { + 'motorway': true + }, + 'junction': { + 'circular': true, + 'roundabout': true + }, + 'man_made': { + 'goods_conveyor': true, + 'piste:halfpipe': true + }, + 'piste:type': { + 'downhill': true, + 'sled': true, + 'yes': true + }, + 'waterway': { + 'canal': true, + 'ditch': true, + 'drain': true, + 'fish_pass': true, + 'river': true, + 'stream': true, + 'tidal_channel': true + } + }; // solid and smooth surfaces akin to the assumed default road surface in OSM + + var osmPavedTags = { + 'surface': { + 'paved': true, + 'asphalt': true, + 'concrete': true, + 'concrete:lanes': true, + 'concrete:plates': true + }, + 'tracktype': { + 'grade1': true + } + }; // solid, if somewhat uncommon surfaces with a high range of smoothness + + var osmSemipavedTags = { + 'surface': { + 'cobblestone': true, + 'cobblestone:flattened': true, + 'unhewn_cobblestone': true, + 'sett': true, + 'paving_stones': true, + 'metal': true, + 'wood': true + } + }; + var osmRightSideIsInsideTags = { + 'natural': { + 'cliff': true, + 'coastline': 'coastline' + }, + 'barrier': { + 'retaining_wall': true, + 'kerb': true, + 'guard_rail': true, + 'city_wall': true + }, + 'man_made': { + 'embankment': true + }, + 'waterway': { + 'weir': true + } + }; // "highway" tag values for pedestrian or vehicle right-of-ways that make up the routable network + // (does not include `raceway`) + + var osmRoutableHighwayTagValues = { + motorway: true, + trunk: true, + primary: true, + secondary: true, + tertiary: true, + residential: true, + motorway_link: true, + trunk_link: true, + primary_link: true, + secondary_link: true, + tertiary_link: true, + unclassified: true, + road: true, + service: true, + track: true, + living_street: true, + bus_guideway: true, + path: true, + footway: true, + cycleway: true, + bridleway: true, + pedestrian: true, + corridor: true, + steps: true + }; // "highway" tag values that generally do not allow motor vehicles + + var osmPathHighwayTagValues = { + path: true, + footway: true, + cycleway: true, + bridleway: true, + pedestrian: true, + corridor: true, + steps: true + }; // "railway" tag values representing existing railroad tracks (purposely does not include 'abandoned') + + var osmRailwayTrackTagValues = { + rail: true, + light_rail: true, + tram: true, + subway: true, + monorail: true, + funicular: true, + miniature: true, + narrow_gauge: true, + disused: true, + preserved: true + }; // "waterway" tag values for line features representing water flow + + var osmFlowingWaterwayTagValues = { + canal: true, + ditch: true, + drain: true, + fish_pass: true, + river: true, + stream: true, + tidal_channel: true + }; + + var global$5 = global$F; + var trim$3 = stringTrim.trim; + var whitespaces = whitespaces$4; + + var $parseInt = global$5.parseInt; + var hex$2 = /^[+-]?0[Xx]/; + var FORCED$6 = $parseInt(whitespaces + '08') !== 8 || $parseInt(whitespaces + '0x16') !== 22; + + // `parseInt` method + // https://tc39.es/ecma262/#sec-parseint-string-radix + var numberParseInt = FORCED$6 ? function parseInt(string, radix) { + var S = trim$3(String(string)); + return $parseInt(S, (radix >>> 0) || (hex$2.test(S) ? 16 : 10)); + } : $parseInt; + + var $$y = _export; + var parseIntImplementation = numberParseInt; + + // `parseInt` method + // https://tc39.es/ecma262/#sec-parseint-string-radix + $$y({ global: true, forced: parseInt != parseIntImplementation }, { + parseInt: parseIntImplementation + }); + + var internalMetadata = {exports: {}}; + + var fails$d = fails$N; + + var freezing = !fails$d(function () { + // eslint-disable-next-line es/no-object-isextensible, es/no-object-preventextensions -- required for testing + return Object.isExtensible(Object.preventExtensions({})); + }); + + var hiddenKeys = hiddenKeys$6; + var isObject$6 = isObject$r; + var has$2 = has$j; + var defineProperty$3 = objectDefineProperty.f; + var uid = uid$5; + var FREEZING$1 = freezing; + + var METADATA = uid('meta'); + var id$1 = 0; + + // eslint-disable-next-line es/no-object-isextensible -- safe + var isExtensible = Object.isExtensible || function () { + return true; + }; + + var setMetadata = function (it) { + defineProperty$3(it, METADATA, { value: { + objectID: 'O' + ++id$1, // object ID + weakData: {} // weak collections IDs + } }); + }; + + var fastKey$1 = function (it, create) { + // return a primitive with prefix + if (!isObject$6(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it; + if (!has$2(it, METADATA)) { + // can't set metadata to uncaught frozen object + if (!isExtensible(it)) return 'F'; + // not necessary to add metadata + if (!create) return 'E'; + // add missing metadata + setMetadata(it); + // return object ID + } return it[METADATA].objectID; + }; + + var getWeakData = function (it, create) { + if (!has$2(it, METADATA)) { + // can't set metadata to uncaught frozen object + if (!isExtensible(it)) return true; + // not necessary to add metadata + if (!create) return false; + // add missing metadata + setMetadata(it); + // return the store of weak collections IDs + } return it[METADATA].weakData; + }; + + // add metadata on freeze-family methods calling + var onFreeze$1 = function (it) { + if (FREEZING$1 && meta.REQUIRED && isExtensible(it) && !has$2(it, METADATA)) setMetadata(it); + return it; + }; + + var meta = internalMetadata.exports = { + REQUIRED: false, + fastKey: fastKey$1, + getWeakData: getWeakData, + onFreeze: onFreeze$1 + }; + + hiddenKeys[METADATA] = true; + + var $$x = _export; + var global$4 = global$F; + var isForced$2 = isForced_1; + var redefine$3 = redefine$g.exports; + var InternalMetadataModule = internalMetadata.exports; + var iterate$1 = iterate$3; + var anInstance$1 = anInstance$7; + var isObject$5 = isObject$r; + var fails$c = fails$N; + var checkCorrectnessOfIteration$1 = checkCorrectnessOfIteration$4; + var setToStringTag = setToStringTag$a; + var inheritIfRequired$2 = inheritIfRequired$4; + + var collection$2 = function (CONSTRUCTOR_NAME, wrapper, common) { + var IS_MAP = CONSTRUCTOR_NAME.indexOf('Map') !== -1; + var IS_WEAK = CONSTRUCTOR_NAME.indexOf('Weak') !== -1; + var ADDER = IS_MAP ? 'set' : 'add'; + var NativeConstructor = global$4[CONSTRUCTOR_NAME]; + var NativePrototype = NativeConstructor && NativeConstructor.prototype; + var Constructor = NativeConstructor; + var exported = {}; + + var fixMethod = function (KEY) { + var nativeMethod = NativePrototype[KEY]; + redefine$3(NativePrototype, KEY, + KEY == 'add' ? function add(value) { + nativeMethod.call(this, value === 0 ? 0 : value); + return this; + } : KEY == 'delete' ? function (key) { + return IS_WEAK && !isObject$5(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key); + } : KEY == 'get' ? function get(key) { + return IS_WEAK && !isObject$5(key) ? undefined : nativeMethod.call(this, key === 0 ? 0 : key); + } : KEY == 'has' ? function has(key) { + return IS_WEAK && !isObject$5(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key); + } : function set(key, value) { + nativeMethod.call(this, key === 0 ? 0 : key, value); + return this; + } + ); + }; + + var REPLACE = isForced$2( + CONSTRUCTOR_NAME, + typeof NativeConstructor != 'function' || !(IS_WEAK || NativePrototype.forEach && !fails$c(function () { + new NativeConstructor().entries().next(); + })) + ); + + if (REPLACE) { + // create collection constructor + Constructor = common.getConstructor(wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER); + InternalMetadataModule.REQUIRED = true; + } else if (isForced$2(CONSTRUCTOR_NAME, true)) { + var instance = new Constructor(); + // early implementations not supports chaining + var HASNT_CHAINING = instance[ADDER](IS_WEAK ? {} : -0, 1) != instance; + // V8 ~ Chromium 40- weak-collections throws on primitives, but should return false + var THROWS_ON_PRIMITIVES = fails$c(function () { instance.has(1); }); + // most early implementations doesn't supports iterables, most modern - not close it correctly + // eslint-disable-next-line no-new -- required for testing + var ACCEPT_ITERABLES = checkCorrectnessOfIteration$1(function (iterable) { new NativeConstructor(iterable); }); + // for early implementations -0 and +0 not the same + var BUGGY_ZERO = !IS_WEAK && fails$c(function () { + // V8 ~ Chromium 42- fails only with 5+ elements + var $instance = new NativeConstructor(); + var index = 5; + while (index--) $instance[ADDER](index, index); + return !$instance.has(-0); + }); + + if (!ACCEPT_ITERABLES) { + Constructor = wrapper(function (dummy, iterable) { + anInstance$1(dummy, Constructor, CONSTRUCTOR_NAME); + var that = inheritIfRequired$2(new NativeConstructor(), dummy, Constructor); + if (iterable != undefined) iterate$1(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP }); + return that; + }); + Constructor.prototype = NativePrototype; + NativePrototype.constructor = Constructor; + } + + if (THROWS_ON_PRIMITIVES || BUGGY_ZERO) { + fixMethod('delete'); + fixMethod('has'); + IS_MAP && fixMethod('get'); + } + + if (BUGGY_ZERO || HASNT_CHAINING) fixMethod(ADDER); + + // weak collections should not contains .clear method + if (IS_WEAK && NativePrototype.clear) delete NativePrototype.clear; + } + + exported[CONSTRUCTOR_NAME] = Constructor; + $$x({ global: true, forced: Constructor != NativeConstructor }, exported); + + setToStringTag(Constructor, CONSTRUCTOR_NAME); + + if (!IS_WEAK) common.setStrong(Constructor, CONSTRUCTOR_NAME, IS_MAP); + + return Constructor; + }; + + var defineProperty$2 = objectDefineProperty.f; + var create$4 = objectCreate; + var redefineAll = redefineAll$4; + var bind$3 = functionBindContext; + var anInstance = anInstance$7; + var iterate = iterate$3; + var defineIterator = defineIterator$3; + var setSpecies$1 = setSpecies$5; + var DESCRIPTORS$5 = descriptors; + var fastKey = internalMetadata.exports.fastKey; + var InternalStateModule = internalState; + + var setInternalState = InternalStateModule.set; + var internalStateGetterFor = InternalStateModule.getterFor; + + var collectionStrong$2 = { + getConstructor: function (wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER) { + var C = wrapper(function (that, iterable) { + anInstance(that, C, CONSTRUCTOR_NAME); + setInternalState(that, { + type: CONSTRUCTOR_NAME, + index: create$4(null), + first: undefined, + last: undefined, + size: 0 + }); + if (!DESCRIPTORS$5) that.size = 0; + if (iterable != undefined) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP }); + }); + + var getInternalState = internalStateGetterFor(CONSTRUCTOR_NAME); + + var define = function (that, key, value) { + var state = getInternalState(that); + var entry = getEntry(that, key); + var previous, index; + // change existing entry + if (entry) { + entry.value = value; + // create new entry + } else { + state.last = entry = { + index: index = fastKey(key, true), + key: key, + value: value, + previous: previous = state.last, + next: undefined, + removed: false + }; + if (!state.first) state.first = entry; + if (previous) previous.next = entry; + if (DESCRIPTORS$5) state.size++; + else that.size++; + // add to index + if (index !== 'F') state.index[index] = entry; + } return that; + }; + + var getEntry = function (that, key) { + var state = getInternalState(that); + // fast case + var index = fastKey(key); + var entry; + if (index !== 'F') return state.index[index]; + // frozen object case + for (entry = state.first; entry; entry = entry.next) { + if (entry.key == key) return entry; + } + }; + + redefineAll(C.prototype, { + // `{ Map, Set }.prototype.clear()` methods + // https://tc39.es/ecma262/#sec-map.prototype.clear + // https://tc39.es/ecma262/#sec-set.prototype.clear + clear: function clear() { + var that = this; + var state = getInternalState(that); + var data = state.index; + var entry = state.first; + while (entry) { + entry.removed = true; + if (entry.previous) entry.previous = entry.previous.next = undefined; + delete data[entry.index]; + entry = entry.next; + } + state.first = state.last = undefined; + if (DESCRIPTORS$5) state.size = 0; + else that.size = 0; + }, + // `{ Map, Set }.prototype.delete(key)` methods + // https://tc39.es/ecma262/#sec-map.prototype.delete + // https://tc39.es/ecma262/#sec-set.prototype.delete + 'delete': function (key) { + var that = this; + var state = getInternalState(that); + var entry = getEntry(that, key); + if (entry) { + var next = entry.next; + var prev = entry.previous; + delete state.index[entry.index]; + entry.removed = true; + if (prev) prev.next = next; + if (next) next.previous = prev; + if (state.first == entry) state.first = next; + if (state.last == entry) state.last = prev; + if (DESCRIPTORS$5) state.size--; + else that.size--; + } return !!entry; + }, + // `{ Map, Set }.prototype.forEach(callbackfn, thisArg = undefined)` methods + // https://tc39.es/ecma262/#sec-map.prototype.foreach + // https://tc39.es/ecma262/#sec-set.prototype.foreach + forEach: function forEach(callbackfn /* , that = undefined */) { + var state = getInternalState(this); + var boundFunction = bind$3(callbackfn, arguments.length > 1 ? arguments[1] : undefined, 3); + var entry; + while (entry = entry ? entry.next : state.first) { + boundFunction(entry.value, entry.key, this); + // revert to the last existing entry + while (entry && entry.removed) entry = entry.previous; + } + }, + // `{ Map, Set}.prototype.has(key)` methods + // https://tc39.es/ecma262/#sec-map.prototype.has + // https://tc39.es/ecma262/#sec-set.prototype.has + has: function has(key) { + return !!getEntry(this, key); + } + }); + + redefineAll(C.prototype, IS_MAP ? { + // `Map.prototype.get(key)` method + // https://tc39.es/ecma262/#sec-map.prototype.get + get: function get(key) { + var entry = getEntry(this, key); + return entry && entry.value; + }, + // `Map.prototype.set(key, value)` method + // https://tc39.es/ecma262/#sec-map.prototype.set + set: function set(key, value) { + return define(this, key === 0 ? 0 : key, value); + } + } : { + // `Set.prototype.add(value)` method + // https://tc39.es/ecma262/#sec-set.prototype.add + add: function add(value) { + return define(this, value = value === 0 ? 0 : value, value); + } + }); + if (DESCRIPTORS$5) defineProperty$2(C.prototype, 'size', { + get: function () { + return getInternalState(this).size; + } + }); + return C; + }, + setStrong: function (C, CONSTRUCTOR_NAME, IS_MAP) { + var ITERATOR_NAME = CONSTRUCTOR_NAME + ' Iterator'; + var getInternalCollectionState = internalStateGetterFor(CONSTRUCTOR_NAME); + var getInternalIteratorState = internalStateGetterFor(ITERATOR_NAME); + // `{ Map, Set }.prototype.{ keys, values, entries, @@iterator }()` methods + // https://tc39.es/ecma262/#sec-map.prototype.entries + // https://tc39.es/ecma262/#sec-map.prototype.keys + // https://tc39.es/ecma262/#sec-map.prototype.values + // https://tc39.es/ecma262/#sec-map.prototype-@@iterator + // https://tc39.es/ecma262/#sec-set.prototype.entries + // https://tc39.es/ecma262/#sec-set.prototype.keys + // https://tc39.es/ecma262/#sec-set.prototype.values + // https://tc39.es/ecma262/#sec-set.prototype-@@iterator + defineIterator(C, CONSTRUCTOR_NAME, function (iterated, kind) { + setInternalState(this, { + type: ITERATOR_NAME, + target: iterated, + state: getInternalCollectionState(iterated), + kind: kind, + last: undefined + }); + }, function () { + var state = getInternalIteratorState(this); + var kind = state.kind; + var entry = state.last; + // revert to the last existing entry + while (entry && entry.removed) entry = entry.previous; + // get next entry + if (!state.target || !(state.last = entry = entry ? entry.next : state.state.first)) { + // or finish the iteration + state.target = undefined; + return { value: undefined, done: true }; + } + // return step by kind + if (kind == 'keys') return { value: entry.key, done: false }; + if (kind == 'values') return { value: entry.value, done: false }; + return { value: [entry.key, entry.value], done: false }; + }, IS_MAP ? 'entries' : 'values', !IS_MAP, true); + + // `{ Map, Set }.prototype[@@species]` accessors + // https://tc39.es/ecma262/#sec-get-map-@@species + // https://tc39.es/ecma262/#sec-get-set-@@species + setSpecies$1(CONSTRUCTOR_NAME); + } + }; + + var collection$1 = collection$2; + var collectionStrong$1 = collectionStrong$2; + + // `Set` constructor + // https://tc39.es/ecma262/#sec-set-objects + collection$1('Set', function (init) { + return function Set() { return init(this, arguments.length ? arguments[0] : undefined); }; + }, collectionStrong$1); + + function d3_ascending (a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; + } + + function d3_bisector (f) { + var delta = f; + var compare = f; + + if (f.length === 1) { + delta = function delta(d, x) { + return f(d) - x; + }; + + compare = ascendingComparator(f); + } + + function left(a, x, lo, hi) { + if (lo == null) lo = 0; + if (hi == null) hi = a.length; + + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) < 0) lo = mid + 1;else hi = mid; + } + + return lo; + } + + function right(a, x, lo, hi) { + if (lo == null) lo = 0; + if (hi == null) hi = a.length; + + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) > 0) hi = mid;else lo = mid + 1; + } + + return lo; + } + + function center(a, x, lo, hi) { + if (lo == null) lo = 0; + if (hi == null) hi = a.length; + var i = left(a, x, lo, hi - 1); + return i > lo && delta(a[i - 1], x) > -delta(a[i], x) ? i - 1 : i; + } + + return { + left: left, + center: center, + right: right + }; + } + + function ascendingComparator(f) { + return function (d, x) { + return d3_ascending(f(d), x); + }; + } + + var defineWellKnownSymbol = defineWellKnownSymbol$4; + + // `Symbol.asyncIterator` well-known symbol + // https://tc39.es/ecma262/#sec-symbol.asynciterator + defineWellKnownSymbol('asyncIterator'); + + var runtime = {exports: {}}; + + (function (module) { + var runtime = function (exports) { + + var Op = Object.prototype; + var hasOwn = Op.hasOwnProperty; + var undefined$1; // More compressible than void 0. + + var $Symbol = typeof Symbol === "function" ? Symbol : {}; + var iteratorSymbol = $Symbol.iterator || "@@iterator"; + var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator"; + var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; + + function define(obj, key, value) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + return obj[key]; + } + + try { + // IE 8 has a broken Object.defineProperty that only works on DOM objects. + define({}, ""); + } catch (err) { + define = function define(obj, key, value) { + return obj[key] = value; + }; + } + + function wrap(innerFn, outerFn, self, tryLocsList) { + // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator. + var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator; + var generator = Object.create(protoGenerator.prototype); + var context = new Context(tryLocsList || []); // The ._invoke method unifies the implementations of the .next, + // .throw, and .return methods. + + generator._invoke = makeInvokeMethod(innerFn, self, context); + return generator; + } + + exports.wrap = wrap; // Try/catch helper to minimize deoptimizations. Returns a completion + // record like context.tryEntries[i].completion. This interface could + // have been (and was previously) designed to take a closure to be + // invoked without arguments, but in all the cases we care about we + // already have an existing method we want to call, so there's no need + // to create a new function object. We can even get away with assuming + // the method takes exactly one argument, since that happens to be true + // in every case, so we don't have to touch the arguments object. The + // only additional allocation required is the completion record, which + // has a stable shape and so hopefully should be cheap to allocate. + + function tryCatch(fn, obj, arg) { + try { + return { + type: "normal", + arg: fn.call(obj, arg) + }; + } catch (err) { + return { + type: "throw", + arg: err + }; + } + } + + var GenStateSuspendedStart = "suspendedStart"; + var GenStateSuspendedYield = "suspendedYield"; + var GenStateExecuting = "executing"; + var GenStateCompleted = "completed"; // Returning this object from the innerFn has the same effect as + // breaking out of the dispatch switch statement. + + var ContinueSentinel = {}; // Dummy constructor functions that we use as the .constructor and + // .constructor.prototype properties for functions that return Generator + // objects. For full spec compliance, you may wish to configure your + // minifier not to mangle the names of these two functions. + + function Generator() {} + + function GeneratorFunction() {} + + function GeneratorFunctionPrototype() {} // This is a polyfill for %IteratorPrototype% for environments that + // don't natively support it. + + + var IteratorPrototype = {}; + + IteratorPrototype[iteratorSymbol] = function () { + return this; + }; + + var getProto = Object.getPrototypeOf; + var NativeIteratorPrototype = getProto && getProto(getProto(values([]))); + + if (NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) { + // This environment has a native %IteratorPrototype%; use it instead + // of the polyfill. + IteratorPrototype = NativeIteratorPrototype; + } + + var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); + GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype; + GeneratorFunctionPrototype.constructor = GeneratorFunction; + GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"); // Helper for defining the .next, .throw, and .return methods of the + // Iterator interface in terms of a single ._invoke method. + + function defineIteratorMethods(prototype) { + ["next", "throw", "return"].forEach(function (method) { + define(prototype, method, function (arg) { + return this._invoke(method, arg); + }); + }); + } + + exports.isGeneratorFunction = function (genFun) { + var ctor = typeof genFun === "function" && genFun.constructor; + return ctor ? ctor === GeneratorFunction || // For the native GeneratorFunction constructor, the best we can + // do is to check its .name property. + (ctor.displayName || ctor.name) === "GeneratorFunction" : false; + }; + + exports.mark = function (genFun) { + if (Object.setPrototypeOf) { + Object.setPrototypeOf(genFun, GeneratorFunctionPrototype); + } else { + genFun.__proto__ = GeneratorFunctionPrototype; + define(genFun, toStringTagSymbol, "GeneratorFunction"); + } + + genFun.prototype = Object.create(Gp); + return genFun; + }; // Within the body of any async function, `await x` is transformed to + // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test + // `hasOwn.call(value, "__await")` to determine if the yielded value is + // meant to be awaited. + + + exports.awrap = function (arg) { + return { + __await: arg + }; + }; + + function AsyncIterator(generator, PromiseImpl) { + function invoke(method, arg, resolve, reject) { + var record = tryCatch(generator[method], generator, arg); + + if (record.type === "throw") { + reject(record.arg); + } else { + var result = record.arg; + var value = result.value; + + if (value && _typeof(value) === "object" && hasOwn.call(value, "__await")) { + return PromiseImpl.resolve(value.__await).then(function (value) { + invoke("next", value, resolve, reject); + }, function (err) { + invoke("throw", err, resolve, reject); + }); + } + + return PromiseImpl.resolve(value).then(function (unwrapped) { + // When a yielded Promise is resolved, its final value becomes + // the .value of the Promise<{value,done}> result for the + // current iteration. + result.value = unwrapped; + resolve(result); + }, function (error) { + // If a rejected Promise was yielded, throw the rejection back + // into the async generator function so it can be handled there. + return invoke("throw", error, resolve, reject); + }); + } + } + + var previousPromise; + + function enqueue(method, arg) { + function callInvokeWithMethodAndArg() { + return new PromiseImpl(function (resolve, reject) { + invoke(method, arg, resolve, reject); + }); + } + + return previousPromise = // If enqueue has been called before, then we want to wait until + // all previous Promises have been resolved before calling invoke, + // so that results are always delivered in the correct order. If + // enqueue has not been called before, then it is important to + // call invoke immediately, without waiting on a callback to fire, + // so that the async generator function has the opportunity to do + // any necessary setup in a predictable way. This predictability + // is why the Promise constructor synchronously invokes its + // executor callback, and why async functions synchronously + // execute code before the first await. Since we implement simple + // async functions in terms of async generators, it is especially + // important to get this right, even though it requires care. + previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, // Avoid propagating failures to Promises returned by later + // invocations of the iterator. + callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); + } // Define the unified helper method that is used to implement .next, + // .throw, and .return (see defineIteratorMethods). + + + this._invoke = enqueue; + } + + defineIteratorMethods(AsyncIterator.prototype); + + AsyncIterator.prototype[asyncIteratorSymbol] = function () { + return this; + }; + + exports.AsyncIterator = AsyncIterator; // Note that simple async functions are implemented on top of + // AsyncIterator objects; they just return a Promise for the value of + // the final result produced by the iterator. + + exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) { + if (PromiseImpl === void 0) PromiseImpl = Promise; + var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl); + return exports.isGeneratorFunction(outerFn) ? iter // If outerFn is a generator, return the full iterator. + : iter.next().then(function (result) { + return result.done ? result.value : iter.next(); + }); + }; + + function makeInvokeMethod(innerFn, self, context) { + var state = GenStateSuspendedStart; + return function invoke(method, arg) { + if (state === GenStateExecuting) { + throw new Error("Generator is already running"); + } + + if (state === GenStateCompleted) { + if (method === "throw") { + throw arg; + } // Be forgiving, per 25.3.3.3.3 of the spec: + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume + + + return doneResult(); + } + + context.method = method; + context.arg = arg; + + while (true) { + var delegate = context.delegate; + + if (delegate) { + var delegateResult = maybeInvokeDelegate(delegate, context); + + if (delegateResult) { + if (delegateResult === ContinueSentinel) continue; + return delegateResult; + } + } + + if (context.method === "next") { + // Setting context._sent for legacy support of Babel's + // function.sent implementation. + context.sent = context._sent = context.arg; + } else if (context.method === "throw") { + if (state === GenStateSuspendedStart) { + state = GenStateCompleted; + throw context.arg; + } + + context.dispatchException(context.arg); + } else if (context.method === "return") { + context.abrupt("return", context.arg); + } + + state = GenStateExecuting; + var record = tryCatch(innerFn, self, context); + + if (record.type === "normal") { + // If an exception is thrown from innerFn, we leave state === + // GenStateExecuting and loop back for another invocation. + state = context.done ? GenStateCompleted : GenStateSuspendedYield; + + if (record.arg === ContinueSentinel) { + continue; + } + + return { + value: record.arg, + done: context.done + }; + } else if (record.type === "throw") { + state = GenStateCompleted; // Dispatch the exception by looping back around to the + // context.dispatchException(context.arg) call above. + + context.method = "throw"; + context.arg = record.arg; + } + } + }; + } // Call delegate.iterator[context.method](context.arg) and handle the + // result, either by returning a { value, done } result from the + // delegate iterator, or by modifying context.method and context.arg, + // setting context.delegate to null, and returning the ContinueSentinel. + + + function maybeInvokeDelegate(delegate, context) { + var method = delegate.iterator[context.method]; + + if (method === undefined$1) { + // A .throw or .return when the delegate iterator has no .throw + // method always terminates the yield* loop. + context.delegate = null; + + if (context.method === "throw") { + // Note: ["return"] must be used for ES3 parsing compatibility. + if (delegate.iterator["return"]) { + // If the delegate iterator has a return method, give it a + // chance to clean up. + context.method = "return"; + context.arg = undefined$1; + maybeInvokeDelegate(delegate, context); + + if (context.method === "throw") { + // If maybeInvokeDelegate(context) changed context.method from + // "return" to "throw", let that override the TypeError below. + return ContinueSentinel; + } + } + + context.method = "throw"; + context.arg = new TypeError("The iterator does not provide a 'throw' method"); + } + + return ContinueSentinel; + } + + var record = tryCatch(method, delegate.iterator, context.arg); + + if (record.type === "throw") { + context.method = "throw"; + context.arg = record.arg; + context.delegate = null; + return ContinueSentinel; + } + + var info = record.arg; + + if (!info) { + context.method = "throw"; + context.arg = new TypeError("iterator result is not an object"); + context.delegate = null; + return ContinueSentinel; + } + + if (info.done) { + // Assign the result of the finished delegate to the temporary + // variable specified by delegate.resultName (see delegateYield). + context[delegate.resultName] = info.value; // Resume execution at the desired location (see delegateYield). + + context.next = delegate.nextLoc; // If context.method was "throw" but the delegate handled the + // exception, let the outer generator proceed normally. If + // context.method was "next", forget context.arg since it has been + // "consumed" by the delegate iterator. If context.method was + // "return", allow the original .return call to continue in the + // outer generator. + + if (context.method !== "return") { + context.method = "next"; + context.arg = undefined$1; + } + } else { + // Re-yield the result returned by the delegate method. + return info; + } // The delegate iterator is finished, so forget it and continue with + // the outer generator. + + + context.delegate = null; + return ContinueSentinel; + } // Define Generator.prototype.{next,throw,return} in terms of the + // unified ._invoke helper method. + + + defineIteratorMethods(Gp); + define(Gp, toStringTagSymbol, "Generator"); // A Generator should always return itself as the iterator object when the + // @@iterator function is called on it. Some browsers' implementations of the + // iterator prototype chain incorrectly implement this, causing the Generator + // object to not be returned from this call. This ensures that doesn't happen. + // See https://github.com/facebook/regenerator/issues/274 for more details. + + Gp[iteratorSymbol] = function () { + return this; + }; + + Gp.toString = function () { + return "[object Generator]"; + }; + + function pushTryEntry(locs) { + var entry = { + tryLoc: locs[0] + }; + + if (1 in locs) { + entry.catchLoc = locs[1]; + } + + if (2 in locs) { + entry.finallyLoc = locs[2]; + entry.afterLoc = locs[3]; + } + + this.tryEntries.push(entry); + } + + function resetTryEntry(entry) { + var record = entry.completion || {}; + record.type = "normal"; + delete record.arg; + entry.completion = record; + } + + function Context(tryLocsList) { + // The root entry object (effectively a try statement without a catch + // or a finally block) gives us a place to store values thrown from + // locations where there is no enclosing try statement. + this.tryEntries = [{ + tryLoc: "root" + }]; + tryLocsList.forEach(pushTryEntry, this); + this.reset(true); + } + + exports.keys = function (object) { + var keys = []; + + for (var key in object) { + keys.push(key); + } + + keys.reverse(); // Rather than returning an object with a next method, we keep + // things simple and return the next function itself. + + return function next() { + while (keys.length) { + var key = keys.pop(); + + if (key in object) { + next.value = key; + next.done = false; + return next; + } + } // To avoid creating an additional object, we just hang the .value + // and .done properties off the next function object itself. This + // also ensures that the minifier will not anonymize the function. + + + next.done = true; + return next; + }; + }; + + function values(iterable) { + if (iterable) { + var iteratorMethod = iterable[iteratorSymbol]; + + if (iteratorMethod) { + return iteratorMethod.call(iterable); + } + + if (typeof iterable.next === "function") { + return iterable; + } + + if (!isNaN(iterable.length)) { + var i = -1, + next = function next() { + while (++i < iterable.length) { + if (hasOwn.call(iterable, i)) { + next.value = iterable[i]; + next.done = false; + return next; + } + } + + next.value = undefined$1; + next.done = true; + return next; + }; + + return next.next = next; + } + } // Return an iterator with no values. + + + return { + next: doneResult + }; + } + + exports.values = values; + + function doneResult() { + return { + value: undefined$1, + done: true + }; + } + + Context.prototype = { + constructor: Context, + reset: function reset(skipTempReset) { + this.prev = 0; + this.next = 0; // Resetting context._sent for legacy support of Babel's + // function.sent implementation. + + this.sent = this._sent = undefined$1; + this.done = false; + this.delegate = null; + this.method = "next"; + this.arg = undefined$1; + this.tryEntries.forEach(resetTryEntry); + + if (!skipTempReset) { + for (var name in this) { + // Not sure about the optimal order of these conditions: + if (name.charAt(0) === "t" && hasOwn.call(this, name) && !isNaN(+name.slice(1))) { + this[name] = undefined$1; + } + } + } + }, + stop: function stop() { + this.done = true; + var rootEntry = this.tryEntries[0]; + var rootRecord = rootEntry.completion; + + if (rootRecord.type === "throw") { + throw rootRecord.arg; + } + + return this.rval; + }, + dispatchException: function dispatchException(exception) { + if (this.done) { + throw exception; + } + + var context = this; + + function handle(loc, caught) { + record.type = "throw"; + record.arg = exception; + context.next = loc; + + if (caught) { + // If the dispatched exception was caught by a catch block, + // then let that catch block handle the exception normally. + context.method = "next"; + context.arg = undefined$1; + } + + return !!caught; + } + + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + var record = entry.completion; + + if (entry.tryLoc === "root") { + // Exception thrown outside of any try block that could handle + // it, so set the completion value of the entire function to + // throw the exception. + return handle("end"); + } + + if (entry.tryLoc <= this.prev) { + var hasCatch = hasOwn.call(entry, "catchLoc"); + var hasFinally = hasOwn.call(entry, "finallyLoc"); + + if (hasCatch && hasFinally) { + if (this.prev < entry.catchLoc) { + return handle(entry.catchLoc, true); + } else if (this.prev < entry.finallyLoc) { + return handle(entry.finallyLoc); + } + } else if (hasCatch) { + if (this.prev < entry.catchLoc) { + return handle(entry.catchLoc, true); + } + } else if (hasFinally) { + if (this.prev < entry.finallyLoc) { + return handle(entry.finallyLoc); + } + } else { + throw new Error("try statement without catch or finally"); + } + } + } + }, + abrupt: function abrupt(type, arg) { + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + + if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) { + var finallyEntry = entry; + break; + } + } + + if (finallyEntry && (type === "break" || type === "continue") && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc) { + // Ignore the finally entry if control is not jumping to a + // location outside the try/catch block. + finallyEntry = null; + } + + var record = finallyEntry ? finallyEntry.completion : {}; + record.type = type; + record.arg = arg; + + if (finallyEntry) { + this.method = "next"; + this.next = finallyEntry.finallyLoc; + return ContinueSentinel; + } + + return this.complete(record); + }, + complete: function complete(record, afterLoc) { + if (record.type === "throw") { + throw record.arg; + } + + if (record.type === "break" || record.type === "continue") { + this.next = record.arg; + } else if (record.type === "return") { + this.rval = this.arg = record.arg; + this.method = "return"; + this.next = "end"; + } else if (record.type === "normal" && afterLoc) { + this.next = afterLoc; + } + + return ContinueSentinel; + }, + finish: function finish(finallyLoc) { + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + + if (entry.finallyLoc === finallyLoc) { + this.complete(entry.completion, entry.afterLoc); + resetTryEntry(entry); + return ContinueSentinel; + } + } + }, + "catch": function _catch(tryLoc) { + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + + if (entry.tryLoc === tryLoc) { + var record = entry.completion; + + if (record.type === "throw") { + var thrown = record.arg; + resetTryEntry(entry); + } + + return thrown; + } + } // The context.catch method must only be called with a location + // argument that corresponds to a known catch block. + + + throw new Error("illegal catch attempt"); + }, + delegateYield: function delegateYield(iterable, resultName, nextLoc) { + this.delegate = { + iterator: values(iterable), + resultName: resultName, + nextLoc: nextLoc + }; + + if (this.method === "next") { + // Deliberately forget the last sent value so that we don't + // accidentally pass it on to the delegate. + this.arg = undefined$1; + } + + return ContinueSentinel; + } + }; // Regardless of whether this script is executing as a CommonJS module + // or not, return the runtime object so that we can declare the variable + // regeneratorRuntime in the outer scope, which allows this module to be + // injected easily by `bin/regenerator --include-runtime script.js`. + + return exports; + }( // If this script is executing as a CommonJS module, use module.exports + // as the regeneratorRuntime namespace. Otherwise create a new empty + // object. Either way, the resulting object will be used to initialize + // the regeneratorRuntime variable at the top of this file. + module.exports ); + + try { + regeneratorRuntime = runtime; + } catch (accidentalStrictMode) { + // This module should not be running in strict mode, so the above + // assignment should always work unless something is misconfigured. Just + // in case runtime.js accidentally runs in strict mode, we can escape + // strict mode using a global Function call. This could conceivably fail + // if a Content Security Policy forbids using Function, but in that case + // the proper solution is to fix the accidental strict mode problem. If + // you've misconfigured your bundler to force strict mode and applied a + // CSP to forbid Function, and you're not willing to fix either of those + // problems, please detail your unique predicament in a GitHub issue. + Function("r", "regeneratorRuntime = r")(runtime); + } + })(runtime); + + var _marked$3 = /*#__PURE__*/regeneratorRuntime.mark(numbers); + + function number$1 (x) { + return x === null ? NaN : +x; + } + function numbers(values, valueof) { + var _iterator, _step, value, index, _iterator2, _step2, _value; + + return regeneratorRuntime.wrap(function numbers$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + if (!(valueof === undefined)) { + _context.next = 21; + break; + } + + _iterator = _createForOfIteratorHelper(values); + _context.prev = 2; + + _iterator.s(); + + case 4: + if ((_step = _iterator.n()).done) { + _context.next = 11; + break; + } + + value = _step.value; + + if (!(value != null && (value = +value) >= value)) { + _context.next = 9; + break; + } + + _context.next = 9; + return value; + + case 9: + _context.next = 4; + break; + + case 11: + _context.next = 16; + break; + + case 13: + _context.prev = 13; + _context.t0 = _context["catch"](2); + + _iterator.e(_context.t0); + + case 16: + _context.prev = 16; + + _iterator.f(); + + return _context.finish(16); + + case 19: + _context.next = 40; + break; + + case 21: + index = -1; + _iterator2 = _createForOfIteratorHelper(values); + _context.prev = 23; + + _iterator2.s(); + + case 25: + if ((_step2 = _iterator2.n()).done) { + _context.next = 32; + break; + } + + _value = _step2.value; + + if (!((_value = valueof(_value, ++index, values)) != null && (_value = +_value) >= _value)) { + _context.next = 30; + break; + } + + _context.next = 30; + return _value; + + case 30: + _context.next = 25; + break; + + case 32: + _context.next = 37; + break; + + case 34: + _context.prev = 34; + _context.t1 = _context["catch"](23); + + _iterator2.e(_context.t1); + + case 37: + _context.prev = 37; + + _iterator2.f(); + + return _context.finish(37); + + case 40: + case "end": + return _context.stop(); + } + } + }, _marked$3, null, [[2, 13, 16, 19], [23, 34, 37, 40]]); + } + + var ascendingBisect = d3_bisector(d3_ascending); + var bisectRight = ascendingBisect.right; + d3_bisector(number$1).center; + + var $$w = _export; + var from = arrayFrom$1; + var checkCorrectnessOfIteration = checkCorrectnessOfIteration$4; + + var INCORRECT_ITERATION = !checkCorrectnessOfIteration(function (iterable) { + // eslint-disable-next-line es/no-array-from -- required for testing + Array.from(iterable); + }); + + // `Array.from` method + // https://tc39.es/ecma262/#sec-array.from + $$w({ target: 'Array', stat: true, forced: INCORRECT_ITERATION }, { + from: from + }); + + var $$v = _export; + var fill = arrayFill$1; + var addToUnscopables$3 = addToUnscopables$5; + + // `Array.prototype.fill` method + // https://tc39.es/ecma262/#sec-array.prototype.fill + $$v({ target: 'Array', proto: true }, { + fill: fill + }); + + // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables + addToUnscopables$3('fill'); + + var $$u = _export; + var $some = arrayIteration.some; + var arrayMethodIsStrict$3 = arrayMethodIsStrict$8; + + var STRICT_METHOD$3 = arrayMethodIsStrict$3('some'); + + // `Array.prototype.some` method + // https://tc39.es/ecma262/#sec-array.prototype.some + $$u({ target: 'Array', proto: true, forced: !STRICT_METHOD$3 }, { + some: function some(callbackfn /* , thisArg */) { + return $some(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); + } + }); + + var TYPED_ARRAYS_CONSTRUCTORS_REQUIRES_WRAPPERS = typedArrayConstructorsRequireWrappers; + var exportTypedArrayStaticMethod = arrayBufferViewCore.exportTypedArrayStaticMethod; + var typedArrayFrom = typedArrayFrom$2; + + // `%TypedArray%.from` method + // https://tc39.es/ecma262/#sec-%typedarray%.from + exportTypedArrayStaticMethod('from', typedArrayFrom, TYPED_ARRAYS_CONSTRUCTORS_REQUIRES_WRAPPERS); + + var createTypedArrayConstructor = typedArrayConstructor.exports; + + // `Float64Array` constructor + // https://tc39.es/ecma262/#sec-typedarray-objects + createTypedArrayConstructor('Float64', function (init) { + return function Float64Array(data, byteOffset, length) { + return init(this, data, byteOffset, length); + }; + }); + + function d3_descending (a, b) { + return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; + } + + // https://github.com/python/cpython/blob/a74eea238f5baba15797e2e8b570d153bc8690a7/Modules/mathmodule.c#L1423 + var Adder = /*#__PURE__*/function () { + function Adder() { + _classCallCheck$1(this, Adder); + + this._partials = new Float64Array(32); + this._n = 0; + } + + _createClass$1(Adder, [{ + key: "add", + value: function add(x) { + var p = this._partials; + var i = 0; + + for (var j = 0; j < this._n && j < 32; j++) { + var y = p[j], + hi = x + y, + lo = Math.abs(x) < Math.abs(y) ? x - (hi - y) : y - (hi - x); + if (lo) p[i++] = lo; + x = hi; + } + + p[i] = x; + this._n = i + 1; + return this; + } + }, { + key: "valueOf", + value: function valueOf() { + var p = this._partials; + var n = this._n, + x, + y, + lo, + hi = 0; + + if (n > 0) { + hi = p[--n]; + + while (n > 0) { + x = hi; + y = p[--n]; + hi = x + y; + lo = y - (hi - x); + if (lo) break; + } + + if (n > 0 && (lo < 0 && p[n - 1] < 0 || lo > 0 && p[n - 1] > 0)) { + y = lo * 2; + x = hi + y; + if (y == x - hi) hi = x; + } + } + + return hi; + } + }]); + + return Adder; + }(); + + var $$t = _export; + var DESCRIPTORS$4 = descriptors; + var defineProperties = objectDefineProperties; + + // `Object.defineProperties` method + // https://tc39.es/ecma262/#sec-object.defineproperties + $$t({ target: 'Object', stat: true, forced: !DESCRIPTORS$4, sham: !DESCRIPTORS$4 }, { + defineProperties: defineProperties + }); + + var collection = collection$2; + var collectionStrong = collectionStrong$2; + + // `Map` constructor + // https://tc39.es/ecma262/#sec-map-objects + collection('Map', function (init) { + return function Map() { return init(this, arguments.length ? arguments[0] : undefined); }; + }, collectionStrong); + + var $$s = _export; + var aFunction = aFunction$9; + var toObject$1 = toObject$i; + var toLength$4 = toLength$q; + var fails$b = fails$N; + var internalSort = arraySort; + var arrayMethodIsStrict$2 = arrayMethodIsStrict$8; + var FF = engineFfVersion; + var IE_OR_EDGE = engineIsIeOrEdge; + var V8 = engineV8Version; + var WEBKIT = engineWebkitVersion; + + var test = []; + var nativeSort = test.sort; + + // IE8- + var FAILS_ON_UNDEFINED = fails$b(function () { + test.sort(undefined); + }); + // V8 bug + var FAILS_ON_NULL = fails$b(function () { + test.sort(null); + }); + // Old WebKit + var STRICT_METHOD$2 = arrayMethodIsStrict$2('sort'); + + var STABLE_SORT = !fails$b(function () { + // feature detection can be too slow, so check engines versions + if (V8) return V8 < 70; + if (FF && FF > 3) return; + if (IE_OR_EDGE) return true; + if (WEBKIT) return WEBKIT < 603; + + var result = ''; + var code, chr, value, index; + + // generate an array with more 512 elements (Chakra and old V8 fails only in this case) + for (code = 65; code < 76; code++) { + chr = String.fromCharCode(code); + + switch (code) { + case 66: case 69: case 70: case 72: value = 3; break; + case 68: case 71: value = 4; break; + default: value = 2; + } + + for (index = 0; index < 47; index++) { + test.push({ k: chr + index, v: value }); + } + } + + test.sort(function (a, b) { return b.v - a.v; }); + + for (index = 0; index < test.length; index++) { + chr = test[index].k.charAt(0); + if (result.charAt(result.length - 1) !== chr) result += chr; + } + + return result !== 'DGBEFHACIJK'; + }); + + var FORCED$5 = FAILS_ON_UNDEFINED || !FAILS_ON_NULL || !STRICT_METHOD$2 || !STABLE_SORT; + + var getSortCompare = function (comparefn) { + return function (x, y) { + if (y === undefined) return -1; + if (x === undefined) return 1; + if (comparefn !== undefined) return +comparefn(x, y) || 0; + return String(x) > String(y) ? 1 : -1; + }; + }; + + // `Array.prototype.sort` method + // https://tc39.es/ecma262/#sec-array.prototype.sort + $$s({ target: 'Array', proto: true, forced: FORCED$5 }, { + sort: function sort(comparefn) { + if (comparefn !== undefined) aFunction(comparefn); + + var array = toObject$1(this); + + if (STABLE_SORT) return comparefn === undefined ? nativeSort.call(array) : nativeSort.call(array, comparefn); + + var items = []; + var arrayLength = toLength$4(array.length); + var itemsLength, index; + + for (index = 0; index < arrayLength; index++) { + if (index in array) items.push(array[index]); + } + + items = internalSort(items, getSortCompare(comparefn)); + itemsLength = items.length; + index = 0; + + while (index < itemsLength) array[index] = items[index++]; + while (index < arrayLength) delete array[index++]; + + return array; + } + }); + + var e10 = Math.sqrt(50), + e5 = Math.sqrt(10), + e2 = Math.sqrt(2); + function ticks (start, stop, count) { + var reverse, + i = -1, + n, + ticks, + step; + stop = +stop, start = +start, count = +count; + if (start === stop && count > 0) return [start]; + if (reverse = stop < start) n = start, start = stop, stop = n; + if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return []; + + if (step > 0) { + var r0 = Math.round(start / step), + r1 = Math.round(stop / step); + if (r0 * step < start) ++r0; + if (r1 * step > stop) --r1; + ticks = new Array(n = r1 - r0 + 1); + + while (++i < n) { + ticks[i] = (r0 + i) * step; + } + } else { + step = -step; + + var _r = Math.round(start * step), + _r2 = Math.round(stop * step); + + if (_r / step < start) ++_r; + if (_r2 / step > stop) --_r2; + ticks = new Array(n = _r2 - _r + 1); + + while (++i < n) { + ticks[i] = (_r + i) / step; + } + } + + if (reverse) ticks.reverse(); + return ticks; + } + function tickIncrement(start, stop, count) { + var step = (stop - start) / Math.max(0, count), + power = Math.floor(Math.log(step) / Math.LN10), + error = step / Math.pow(10, power); + return power >= 0 ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power) : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1); + } + function tickStep(start, stop, count) { + var step0 = Math.abs(stop - start) / Math.max(0, count), + step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)), + error = step0 / step1; + if (error >= e10) step1 *= 10;else if (error >= e5) step1 *= 5;else if (error >= e2) step1 *= 2; + return stop < start ? -step1 : step1; + } + + function max(values, valueof) { + var max; + + if (valueof === undefined) { + var _iterator = _createForOfIteratorHelper(values), + _step; + + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var value = _step.value; + + if (value != null && (max < value || max === undefined && value >= value)) { + max = value; + } + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + } else { + var index = -1; + + var _iterator2 = _createForOfIteratorHelper(values), + _step2; + + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + var _value = _step2.value; + + if ((_value = valueof(_value, ++index, values)) != null && (max < _value || max === undefined && _value >= _value)) { + max = _value; + } + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } + } + + return max; + } + + function min$2(values, valueof) { + var min; + + if (valueof === undefined) { + var _iterator = _createForOfIteratorHelper(values), + _step; + + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var value = _step.value; + + if (value != null && (min > value || min === undefined && value >= value)) { + min = value; + } + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + } else { + var index = -1; + + var _iterator2 = _createForOfIteratorHelper(values), + _step2; + + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + var _value = _step2.value; + + if ((_value = valueof(_value, ++index, values)) != null && (min > _value || min === undefined && _value >= _value)) { + min = _value; + } + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } + } + + return min; + } + + // ISC license, Copyright 2018 Vladimir Agafonkin. + + function quickselect$3(array, k) { + var left = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; + var right = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : array.length - 1; + var compare = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : d3_ascending; + + while (right > left) { + if (right - left > 600) { + var n = right - left + 1; + var m = k - left + 1; + var z = Math.log(n); + var s = 0.5 * Math.exp(2 * z / 3); + var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); + var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); + var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); + quickselect$3(array, k, newLeft, newRight, compare); + } + + var t = array[k]; + var i = left; + var j = right; + swap$1(array, left, k); + if (compare(array[right], t) > 0) swap$1(array, left, right); + + while (i < j) { + swap$1(array, i, j), ++i, --j; + + while (compare(array[i], t) < 0) { + ++i; + } + + while (compare(array[j], t) > 0) { + --j; + } + } + + if (compare(array[left], t) === 0) swap$1(array, left, j);else ++j, swap$1(array, j, right); + if (j <= k) left = j + 1; + if (k <= j) right = j - 1; + } + + return array; + } + + function swap$1(array, i, j) { + var t = array[i]; + array[i] = array[j]; + array[j] = t; + } + + function quantile(values, p, valueof) { + values = Float64Array.from(numbers(values, valueof)); + if (!(n = values.length)) return; + if ((p = +p) <= 0 || n < 2) return min$2(values); + if (p >= 1) return max(values); + var n, + i = (n - 1) * p, + i0 = Math.floor(i), + value0 = max(quickselect$3(values, i0).subarray(0, i0 + 1)), + value1 = min$2(values.subarray(i0 + 1)); + return value0 + (value1 - value0) * (i - i0); + } + + function d3_median (values, valueof) { + return quantile(values, 0.5, valueof); + } + + var _marked$2 = /*#__PURE__*/regeneratorRuntime.mark(flatten); + + function flatten(arrays) { + var _iterator, _step, array; + + return regeneratorRuntime.wrap(function flatten$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + _iterator = _createForOfIteratorHelper(arrays); + _context.prev = 1; + + _iterator.s(); + + case 3: + if ((_step = _iterator.n()).done) { + _context.next = 8; + break; + } + + array = _step.value; + return _context.delegateYield(array, "t0", 6); + + case 6: + _context.next = 3; + break; + + case 8: + _context.next = 13; + break; + + case 10: + _context.prev = 10; + _context.t1 = _context["catch"](1); + + _iterator.e(_context.t1); + + case 13: + _context.prev = 13; + + _iterator.f(); + + return _context.finish(13); + + case 16: + case "end": + return _context.stop(); + } + } + }, _marked$2, null, [[1, 10, 13, 16]]); + } + + function merge$4(arrays) { + return Array.from(flatten(arrays)); + } + + function range$1 (start, stop, step) { + start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step; + var i = -1, + n = Math.max(0, Math.ceil((stop - start) / step)) | 0, + range = new Array(n); + + while (++i < n) { + range[i] = start + i * step; + } + + return range; + } + + // `SameValue` abstract operation + // https://tc39.es/ecma262/#sec-samevalue + // eslint-disable-next-line es/no-object-is -- safe + var sameValue$1 = Object.is || function is(x, y) { + // eslint-disable-next-line no-self-compare -- NaN check + return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y; + }; + + var $$r = _export; + + // eslint-disable-next-line es/no-math-hypot -- required for testing + var $hypot = Math.hypot; + var abs$3 = Math.abs; + var sqrt$1 = Math.sqrt; + + // Chrome 77 bug + // https://bugs.chromium.org/p/v8/issues/detail?id=9546 + var BUGGY = !!$hypot && $hypot(Infinity, NaN) !== Infinity; + + // `Math.hypot` method + // https://tc39.es/ecma262/#sec-math.hypot + $$r({ target: 'Math', stat: true, forced: BUGGY }, { + // eslint-disable-next-line no-unused-vars -- required for `.length` + hypot: function hypot(value1, value2) { + var sum = 0; + var i = 0; + var aLen = arguments.length; + var larg = 0; + var arg, div; + while (i < aLen) { + arg = abs$3(arguments[i++]); + if (larg < arg) { + div = larg / arg; + sum = sum * div * div + 1; + larg = arg; + } else if (arg > 0) { + div = arg / larg; + sum += div * div; + } else sum += arg; + } + return larg === Infinity ? Infinity : larg * sqrt$1(sum); + } + }); + + // `Math.sign` method implementation + // https://tc39.es/ecma262/#sec-math.sign + // eslint-disable-next-line es/no-math-sign -- safe + var mathSign = Math.sign || function sign(x) { + // eslint-disable-next-line no-self-compare -- NaN check + return (x = +x) == 0 || x != x ? x : x < 0 ? -1 : 1; + }; + + var $$q = _export; + var sign$1 = mathSign; + + // `Math.sign` method + // https://tc39.es/ecma262/#sec-math.sign + $$q({ target: 'Math', stat: true }, { + sign: sign$1 + }); + + var epsilon$1 = 1e-6; + var epsilon2$1 = 1e-12; + var pi = Math.PI; + var halfPi = pi / 2; + var quarterPi = pi / 4; + var tau = pi * 2; + var degrees$1 = 180 / pi; + var radians = pi / 180; + var abs$2 = Math.abs; + var atan = Math.atan; + var atan2 = Math.atan2; + var cos = Math.cos; + var exp$2 = Math.exp; + var log$1 = Math.log; + var sin = Math.sin; + var sign = Math.sign || function (x) { + return x > 0 ? 1 : x < 0 ? -1 : 0; + }; + var sqrt = Math.sqrt; + var tan = Math.tan; + function acos(x) { + return x > 1 ? 0 : x < -1 ? pi : Math.acos(x); + } + function asin(x) { + return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x); + } + + function noop$1() {} + + function streamGeometry(geometry, stream) { + if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) { + streamGeometryType[geometry.type](geometry, stream); + } + } + + var streamObjectType = { + Feature: function Feature(object, stream) { + streamGeometry(object.geometry, stream); + }, + FeatureCollection: function FeatureCollection(object, stream) { + var features = object.features, + i = -1, + n = features.length; + + while (++i < n) { + streamGeometry(features[i].geometry, stream); + } + } + }; + var streamGeometryType = { + Sphere: function Sphere(object, stream) { + stream.sphere(); + }, + Point: function Point(object, stream) { + object = object.coordinates; + stream.point(object[0], object[1], object[2]); + }, + MultiPoint: function MultiPoint(object, stream) { + var coordinates = object.coordinates, + i = -1, + n = coordinates.length; + + while (++i < n) { + object = coordinates[i], stream.point(object[0], object[1], object[2]); + } + }, + LineString: function LineString(object, stream) { + streamLine(object.coordinates, stream, 0); + }, + MultiLineString: function MultiLineString(object, stream) { + var coordinates = object.coordinates, + i = -1, + n = coordinates.length; + + while (++i < n) { + streamLine(coordinates[i], stream, 0); + } + }, + Polygon: function Polygon(object, stream) { + streamPolygon(object.coordinates, stream); + }, + MultiPolygon: function MultiPolygon(object, stream) { + var coordinates = object.coordinates, + i = -1, + n = coordinates.length; + + while (++i < n) { + streamPolygon(coordinates[i], stream); + } + }, + GeometryCollection: function GeometryCollection(object, stream) { + var geometries = object.geometries, + i = -1, + n = geometries.length; + + while (++i < n) { + streamGeometry(geometries[i], stream); + } + } + }; + + function streamLine(coordinates, stream, closed) { + var i = -1, + n = coordinates.length - closed, + coordinate; + stream.lineStart(); + + while (++i < n) { + coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]); + } + + stream.lineEnd(); + } + + function streamPolygon(coordinates, stream) { + var i = -1, + n = coordinates.length; + stream.polygonStart(); + + while (++i < n) { + streamLine(coordinates[i], stream, 1); + } + + stream.polygonEnd(); + } + + function d3_geoStream (object, stream) { + if (object && streamObjectType.hasOwnProperty(object.type)) { + streamObjectType[object.type](object, stream); + } else { + streamGeometry(object, stream); + } + } + + var areaRingSum$1 = new Adder(); // hello? + + var areaSum$1 = new Adder(), + lambda00$1, + phi00$1, + lambda0$2, + cosPhi0$1, + sinPhi0$1; + var areaStream$1 = { + point: noop$1, + lineStart: noop$1, + lineEnd: noop$1, + polygonStart: function polygonStart() { + areaRingSum$1 = new Adder(); + areaStream$1.lineStart = areaRingStart$1; + areaStream$1.lineEnd = areaRingEnd$1; + }, + polygonEnd: function polygonEnd() { + var areaRing = +areaRingSum$1; + areaSum$1.add(areaRing < 0 ? tau + areaRing : areaRing); + this.lineStart = this.lineEnd = this.point = noop$1; + }, + sphere: function sphere() { + areaSum$1.add(tau); + } + }; + + function areaRingStart$1() { + areaStream$1.point = areaPointFirst$1; + } + + function areaRingEnd$1() { + areaPoint$1(lambda00$1, phi00$1); + } + + function areaPointFirst$1(lambda, phi) { + areaStream$1.point = areaPoint$1; + lambda00$1 = lambda, phi00$1 = phi; + lambda *= radians, phi *= radians; + lambda0$2 = lambda, cosPhi0$1 = cos(phi = phi / 2 + quarterPi), sinPhi0$1 = sin(phi); + } + + function areaPoint$1(lambda, phi) { + lambda *= radians, phi *= radians; + phi = phi / 2 + quarterPi; // half the angular distance from south pole + // Spherical excess E for a spherical triangle with vertices: south pole, + // previous point, current point. Uses a formula derived from Cagnoli’s + // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2). + + var dLambda = lambda - lambda0$2, + sdLambda = dLambda >= 0 ? 1 : -1, + adLambda = sdLambda * dLambda, + cosPhi = cos(phi), + sinPhi = sin(phi), + k = sinPhi0$1 * sinPhi, + u = cosPhi0$1 * cosPhi + k * cos(adLambda), + v = k * sdLambda * sin(adLambda); + areaRingSum$1.add(atan2(v, u)); // Advance the previous points. + + lambda0$2 = lambda, cosPhi0$1 = cosPhi, sinPhi0$1 = sinPhi; + } + + function d3_geoArea (object) { + areaSum$1 = new Adder(); + d3_geoStream(object, areaStream$1); + return areaSum$1 * 2; + } + + function spherical(cartesian) { + return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])]; + } + function cartesian(spherical) { + var lambda = spherical[0], + phi = spherical[1], + cosPhi = cos(phi); + return [cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi)]; + } + function cartesianDot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + function cartesianCross(a, b) { + return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]; + } // TODO return a + + function cartesianAddInPlace(a, b) { + a[0] += b[0], a[1] += b[1], a[2] += b[2]; + } + function cartesianScale(vector, k) { + return [vector[0] * k, vector[1] * k, vector[2] * k]; + } // TODO return d + + function cartesianNormalizeInPlace(d) { + var l = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); + d[0] /= l, d[1] /= l, d[2] /= l; + } + + var lambda0$1, phi0, lambda1, phi1, // bounds + lambda2, // previous lambda-coordinate + lambda00, phi00, // first point + p0, // previous 3D point + deltaSum, ranges, range; + var boundsStream$1 = { + point: boundsPoint$1, + lineStart: boundsLineStart, + lineEnd: boundsLineEnd, + polygonStart: function polygonStart() { + boundsStream$1.point = boundsRingPoint; + boundsStream$1.lineStart = boundsRingStart; + boundsStream$1.lineEnd = boundsRingEnd; + deltaSum = new Adder(); + areaStream$1.polygonStart(); + }, + polygonEnd: function polygonEnd() { + areaStream$1.polygonEnd(); + boundsStream$1.point = boundsPoint$1; + boundsStream$1.lineStart = boundsLineStart; + boundsStream$1.lineEnd = boundsLineEnd; + 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; + range[0] = lambda0$1, range[1] = lambda1; + }, + sphere: function sphere() { + lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90); + } + }; + + function boundsPoint$1(lambda, phi) { + ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]); + if (phi < phi0) phi0 = phi; + if (phi > phi1) phi1 = phi; + } + + function linePoint(lambda, phi) { + var p = cartesian([lambda * radians, phi * radians]); + + if (p0) { + var normal = cartesianCross(p0, p), + equatorial = [normal[1], -normal[0], 0], + inflection = cartesianCross(equatorial, normal); + cartesianNormalizeInPlace(inflection); + inflection = spherical(inflection); + var delta = lambda - lambda2, + sign = delta > 0 ? 1 : -1, + lambdai = inflection[0] * degrees$1 * sign, + phii, + antimeridian = abs$2(delta) > 180; + + if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) { + phii = inflection[1] * degrees$1; + if (phii > phi1) phi1 = phii; + } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) { + phii = -inflection[1] * degrees$1; + if (phii < phi0) phi0 = phii; + } else { + if (phi < phi0) phi0 = phi; + if (phi > phi1) phi1 = phi; + } + + if (antimeridian) { + if (lambda < lambda2) { + if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda; + } else { + if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda; + } + } else { + if (lambda1 >= lambda0$1) { + if (lambda < lambda0$1) lambda0$1 = lambda; + if (lambda > lambda1) lambda1 = lambda; + } else { + if (lambda > lambda2) { + if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda; + } else { + if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda; + } + } + } + } else { + ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]); + } + + if (phi < phi0) phi0 = phi; + if (phi > phi1) phi1 = phi; + p0 = p, lambda2 = lambda; + } + + function boundsLineStart() { + boundsStream$1.point = linePoint; + } + + function boundsLineEnd() { + range[0] = lambda0$1, range[1] = lambda1; + boundsStream$1.point = boundsPoint$1; + p0 = null; + } + + function boundsRingPoint(lambda, phi) { + if (p0) { + var delta = lambda - lambda2; + deltaSum.add(abs$2(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta); + } else { + lambda00 = lambda, phi00 = phi; + } + + areaStream$1.point(lambda, phi); + linePoint(lambda, phi); + } + + function boundsRingStart() { + areaStream$1.lineStart(); + } + + function boundsRingEnd() { + boundsRingPoint(lambda00, phi00); + areaStream$1.lineEnd(); + if (abs$2(deltaSum) > epsilon$1) lambda0$1 = -(lambda1 = 180); + range[0] = lambda0$1, range[1] = lambda1; + p0 = null; + } // Finds the left-right distance between two longitudes. + // This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want + // the distance between ±180° to be 360°. + + + function angle(lambda0, lambda1) { + return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1; + } + + function rangeCompare(a, b) { + return a[0] - b[0]; + } + + function rangeContains(range, x) { + return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; + } + + function d3_geoBounds (feature) { + var i, n, a, b, merged, deltaMax, delta; + phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity); + ranges = []; + d3_geoStream(feature, boundsStream$1); // First, sort ranges by their minimum longitudes. + + if (n = ranges.length) { + ranges.sort(rangeCompare); // Then, merge any ranges that overlap. + + for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) { + b = ranges[i]; + + if (rangeContains(a, b[0]) || rangeContains(a, b[1])) { + if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; + if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; + } else { + merged.push(a = b); + } + } // Finally, find the largest gap between the merged ranges. + // The final bounding box will be the inverse of this gap. + + + for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) { + b = merged[i]; + if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1]; + } + } + + ranges = range = null; + return lambda0$1 === Infinity || phi0 === Infinity ? [[NaN, NaN], [NaN, NaN]] : [[lambda0$1, phi0], [lambda1, phi1]]; + } + + function compose (a, b) { + function compose(x, y) { + return x = a(x, y), b(x[0], x[1]); + } + + if (a.invert && b.invert) compose.invert = function (x, y) { + return x = b.invert(x, y), x && a.invert(x[0], x[1]); + }; + return compose; + } + + function rotationIdentity(lambda, phi) { + return [abs$2(lambda) > pi ? lambda + Math.round(-lambda / tau) * tau : lambda, phi]; + } + + rotationIdentity.invert = rotationIdentity; + function rotateRadians(deltaLambda, deltaPhi, deltaGamma) { + return (deltaLambda %= tau) ? deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma)) : rotationLambda(deltaLambda) : deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma) : rotationIdentity; + } + + function forwardRotationLambda(deltaLambda) { + return function (lambda, phi) { + return lambda += deltaLambda, [lambda > pi ? lambda - tau : lambda < -pi ? lambda + tau : lambda, phi]; + }; + } + + function rotationLambda(deltaLambda) { + var rotation = forwardRotationLambda(deltaLambda); + rotation.invert = forwardRotationLambda(-deltaLambda); + return rotation; + } + + function rotationPhiGamma(deltaPhi, deltaGamma) { + var cosDeltaPhi = cos(deltaPhi), + sinDeltaPhi = sin(deltaPhi), + cosDeltaGamma = cos(deltaGamma), + sinDeltaGamma = sin(deltaGamma); + + function rotation(lambda, phi) { + var cosPhi = cos(phi), + x = cos(lambda) * cosPhi, + y = sin(lambda) * cosPhi, + z = sin(phi), + k = z * cosDeltaPhi + x * sinDeltaPhi; + return [atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi), asin(k * cosDeltaGamma + y * sinDeltaGamma)]; + } + + rotation.invert = function (lambda, phi) { + var cosPhi = cos(phi), + x = cos(lambda) * cosPhi, + y = sin(lambda) * cosPhi, + z = sin(phi), + k = z * cosDeltaGamma - y * sinDeltaGamma; + return [atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi), asin(k * cosDeltaPhi - x * sinDeltaPhi)]; + }; + + return rotation; + } + + function rotation (rotate) { + rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0); + + function forward(coordinates) { + coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians); + return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates; + } + + forward.invert = function (coordinates) { + coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians); + return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates; + }; + + return forward; + } + + function circleStream(stream, radius, delta, direction, t0, t1) { + if (!delta) return; + var cosRadius = cos(radius), + sinRadius = sin(radius), + step = direction * delta; + + if (t0 == null) { + t0 = radius + direction * tau; + t1 = radius - step / 2; + } else { + t0 = circleRadius(cosRadius, t0); + t1 = circleRadius(cosRadius, t1); + if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau; + } + + for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) { + point = spherical([cosRadius, -sinRadius * cos(t), -sinRadius * sin(t)]); + stream.point(point[0], point[1]); + } + } // Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0]. + + function circleRadius(cosRadius, point) { + point = cartesian(point), point[0] -= cosRadius; + cartesianNormalizeInPlace(point); + var radius = acos(-point[1]); + return ((-point[2] < 0 ? -radius : radius) + tau - epsilon$1) % tau; + } + + function clipBuffer () { + var lines = [], + line; + return { + point: function point(x, y, m) { + line.push([x, y, m]); + }, + lineStart: function lineStart() { + lines.push(line = []); + }, + lineEnd: noop$1, + rejoin: function rejoin() { + if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); + }, + result: function result() { + var result = lines; + lines = []; + line = null; + return result; + } + }; + } + + function pointEqual (a, b) { + return abs$2(a[0] - b[0]) < epsilon$1 && abs$2(a[1] - b[1]) < epsilon$1; + } + + function Intersection(point, points, other, entry) { + this.x = point; + this.z = points; + this.o = other; // another intersection + + this.e = entry; // is an entry? + + this.v = false; // visited + + this.n = this.p = null; // next & previous + } // A generalized polygon clipping algorithm: given a polygon that has been cut + // into its visible line segments, and rejoins the segments by interpolating + // along the clip edge. + + + function clipRejoin (segments, compareIntersection, startInside, interpolate, stream) { + var subject = [], + clip = [], + i, + n; + segments.forEach(function (segment) { + if ((n = segment.length - 1) <= 0) return; + var n, + p0 = segment[0], + p1 = segment[n], + x; + + if (pointEqual(p0, p1)) { + if (!p0[2] && !p1[2]) { + stream.lineStart(); + + for (i = 0; i < n; ++i) { + stream.point((p0 = segment[i])[0], p0[1]); + } + + stream.lineEnd(); + return; + } // handle degenerate cases by moving the point + + + p1[0] += 2 * epsilon$1; + } + + subject.push(x = new Intersection(p0, segment, null, true)); + clip.push(x.o = new Intersection(p0, null, x, false)); + subject.push(x = new Intersection(p1, segment, null, false)); + clip.push(x.o = new Intersection(p1, null, x, true)); + }); + if (!subject.length) return; + clip.sort(compareIntersection); + link(subject); + link(clip); + + for (i = 0, n = clip.length; i < n; ++i) { + clip[i].e = startInside = !startInside; + } + + var start = subject[0], + points, + point; + + while (1) { + // Find first unvisited intersection. + var current = start, + isSubject = true; + + while (current.v) { + if ((current = current.n) === start) return; + } + + points = current.z; + stream.lineStart(); + + do { + current.v = current.o.v = true; + + if (current.e) { + if (isSubject) { + for (i = 0, n = points.length; i < n; ++i) { + stream.point((point = points[i])[0], point[1]); + } + } else { + interpolate(current.x, current.n.x, 1, stream); + } + + current = current.n; + } else { + if (isSubject) { + points = current.p.z; + + for (i = points.length - 1; i >= 0; --i) { + stream.point((point = points[i])[0], point[1]); + } + } else { + interpolate(current.x, current.p.x, -1, stream); + } + + current = current.p; + } + + current = current.o; + points = current.z; + isSubject = !isSubject; + } while (!current.v); + + stream.lineEnd(); + } + } + + function link(array) { + if (!(n = array.length)) return; + var n, + i = 0, + a = array[0], + b; + + while (++i < n) { + a.n = b = array[i]; + b.p = a; + a = b; + } + + a.n = b = array[0]; + b.p = a; + } + + function longitude(point) { + if (abs$2(point[0]) <= pi) return point[0];else return sign(point[0]) * ((abs$2(point[0]) + pi) % tau - pi); + } + + function polygonContains (polygon, point) { + var lambda = longitude(point), + phi = point[1], + sinPhi = sin(phi), + normal = [sin(lambda), -cos(lambda), 0], + angle = 0, + winding = 0; + var sum = new Adder(); + if (sinPhi === 1) phi = halfPi + epsilon$1;else if (sinPhi === -1) phi = -halfPi - epsilon$1; + + for (var i = 0, n = polygon.length; i < n; ++i) { + if (!(m = (ring = polygon[i]).length)) continue; + var ring, + m, + point0 = ring[m - 1], + lambda0 = longitude(point0), + phi0 = point0[1] / 2 + quarterPi, + sinPhi0 = sin(phi0), + cosPhi0 = cos(phi0); + + for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) { + var point1 = ring[j], + lambda1 = longitude(point1), + phi1 = point1[1] / 2 + quarterPi, + sinPhi1 = sin(phi1), + cosPhi1 = cos(phi1), + delta = lambda1 - lambda0, + sign = delta >= 0 ? 1 : -1, + absDelta = sign * delta, + antimeridian = absDelta > pi, + k = sinPhi0 * sinPhi1; + sum.add(atan2(k * sign * sin(absDelta), cosPhi0 * cosPhi1 + k * cos(absDelta))); + angle += antimeridian ? delta + sign * tau : delta; // Are the longitudes either side of the point’s meridian (lambda), + // and are the latitudes smaller than the parallel (phi)? + + if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) { + var arc = cartesianCross(cartesian(point0), cartesian(point1)); + cartesianNormalizeInPlace(arc); + var intersection = cartesianCross(normal, arc); + cartesianNormalizeInPlace(intersection); + var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]); + + if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) { + winding += antimeridian ^ delta >= 0 ? 1 : -1; + } + } + } + } // First, determine whether the South pole is inside or outside: + // + // It is inside if: + // * the polygon winds around it in a clockwise direction. + // * the polygon does not (cumulatively) wind around it, but has a negative + // (counter-clockwise) area. + // + // Second, count the (signed) number of times a segment crosses a lambda + // from the point to the South pole. If it is zero, then the point is the + // same side as the South pole. + + + return (angle < -epsilon$1 || angle < epsilon$1 && sum < -epsilon2$1) ^ winding & 1; + } + + function clip (pointVisible, clipLine, interpolate, start) { + return function (sink) { + var line = clipLine(sink), + ringBuffer = clipBuffer(), + ringSink = clipLine(ringBuffer), + polygonStarted = false, + polygon, + segments, + ring; + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function polygonStart() { + clip.point = pointRing; + clip.lineStart = ringStart; + clip.lineEnd = ringEnd; + segments = []; + polygon = []; + }, + polygonEnd: function polygonEnd() { + clip.point = point; + clip.lineStart = lineStart; + clip.lineEnd = lineEnd; + segments = merge$4(segments); + var startInside = polygonContains(polygon, start); + + if (segments.length) { + if (!polygonStarted) sink.polygonStart(), polygonStarted = true; + clipRejoin(segments, compareIntersection, startInside, interpolate, sink); + } else if (startInside) { + if (!polygonStarted) sink.polygonStart(), polygonStarted = true; + sink.lineStart(); + interpolate(null, null, 1, sink); + sink.lineEnd(); + } + + if (polygonStarted) sink.polygonEnd(), polygonStarted = false; + segments = polygon = null; + }, + sphere: function sphere() { + sink.polygonStart(); + sink.lineStart(); + interpolate(null, null, 1, sink); + sink.lineEnd(); + sink.polygonEnd(); + } + }; + + function point(lambda, phi) { + if (pointVisible(lambda, phi)) sink.point(lambda, phi); + } + + function pointLine(lambda, phi) { + line.point(lambda, phi); + } + + function lineStart() { + clip.point = pointLine; + line.lineStart(); + } + + function lineEnd() { + clip.point = point; + line.lineEnd(); + } + + function pointRing(lambda, phi) { + ring.push([lambda, phi]); + ringSink.point(lambda, phi); + } + + function ringStart() { + ringSink.lineStart(); + ring = []; + } + + function ringEnd() { + pointRing(ring[0][0], ring[0][1]); + ringSink.lineEnd(); + var clean = ringSink.clean(), + ringSegments = ringBuffer.result(), + i, + n = ringSegments.length, + m, + segment, + point; + ring.pop(); + polygon.push(ring); + ring = null; + if (!n) return; // No intersections. + + if (clean & 1) { + segment = ringSegments[0]; + + if ((m = segment.length - 1) > 0) { + if (!polygonStarted) sink.polygonStart(), polygonStarted = true; + sink.lineStart(); + + for (i = 0; i < m; ++i) { + sink.point((point = segment[i])[0], point[1]); + } + + sink.lineEnd(); + } + + return; + } // Rejoin connected segments. + // TODO reuse ringBuffer.rejoin()? + + + if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); + segments.push(ringSegments.filter(validSegment)); + } + + return clip; + }; + } + + function validSegment(segment) { + return segment.length > 1; + } // Intersections are sorted along the clip edge. For both antimeridian cutting + // and circle clipping, the same comparison is used. + + + function compareIntersection(a, b) { + return ((a = a.x)[0] < 0 ? a[1] - halfPi - epsilon$1 : halfPi - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfPi - epsilon$1 : halfPi - b[1]); + } + + var clipAntimeridian = clip(function () { + return true; + }, clipAntimeridianLine, clipAntimeridianInterpolate, [-pi, -halfPi]); // Takes a line and cuts into visible segments. Return values: 0 - there were + // intersections or the line was empty; 1 - no intersections; 2 - there were + // intersections, and the first and last segments should be rejoined. + + function clipAntimeridianLine(stream) { + var lambda0 = NaN, + phi0 = NaN, + sign0 = NaN, + _clean; // no intersections + + + return { + lineStart: function lineStart() { + stream.lineStart(); + _clean = 1; + }, + point: function point(lambda1, phi1) { + var sign1 = lambda1 > 0 ? pi : -pi, + delta = abs$2(lambda1 - lambda0); + + if (abs$2(delta - pi) < epsilon$1) { + // line crosses a pole + stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi : -halfPi); + stream.point(sign0, phi0); + stream.lineEnd(); + stream.lineStart(); + stream.point(sign1, phi0); + stream.point(lambda1, phi0); + _clean = 0; + } else if (sign0 !== sign1 && delta >= pi) { + // line crosses antimeridian + if (abs$2(lambda0 - sign0) < epsilon$1) lambda0 -= sign0 * epsilon$1; // handle degeneracies + + if (abs$2(lambda1 - sign1) < epsilon$1) lambda1 -= sign1 * epsilon$1; + phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1); + stream.point(sign0, phi0); + stream.lineEnd(); + stream.lineStart(); + stream.point(sign1, phi0); + _clean = 0; + } + + stream.point(lambda0 = lambda1, phi0 = phi1); + sign0 = sign1; + }, + lineEnd: function lineEnd() { + stream.lineEnd(); + lambda0 = phi0 = NaN; + }, + clean: function clean() { + return 2 - _clean; // if intersections, rejoin first and last segments + } + }; + } + + function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) { + var cosPhi0, + cosPhi1, + sinLambda0Lambda1 = sin(lambda0 - lambda1); + return abs$2(sinLambda0Lambda1) > epsilon$1 ? atan((sin(phi0) * (cosPhi1 = cos(phi1)) * sin(lambda1) - sin(phi1) * (cosPhi0 = cos(phi0)) * sin(lambda0)) / (cosPhi0 * cosPhi1 * sinLambda0Lambda1)) : (phi0 + phi1) / 2; + } + + function clipAntimeridianInterpolate(from, to, direction, stream) { + var phi; + + if (from == null) { + phi = direction * halfPi; + stream.point(-pi, phi); + stream.point(0, phi); + stream.point(pi, phi); + stream.point(pi, 0); + stream.point(pi, -phi); + stream.point(0, -phi); + stream.point(-pi, -phi); + stream.point(-pi, 0); + stream.point(-pi, phi); + } else if (abs$2(from[0] - to[0]) > epsilon$1) { + var lambda = from[0] < to[0] ? pi : -pi; + phi = direction * lambda / 2; + stream.point(-lambda, phi); + stream.point(0, phi); + stream.point(lambda, phi); + } else { + stream.point(to[0], to[1]); + } + } + + function clipCircle (radius) { + var cr = cos(radius), + delta = 6 * radians, + smallRadius = cr > 0, + notHemisphere = abs$2(cr) > epsilon$1; // TODO optimise for this common case + + function interpolate(from, to, direction, stream) { + circleStream(stream, radius, delta, direction, from, to); + } + + function visible(lambda, phi) { + return cos(lambda) * cos(phi) > cr; + } // Takes a line and cuts into visible segments. Return values used for polygon + // clipping: 0 - there were intersections or the line was empty; 1 - no + // intersections 2 - there were intersections, and the first and last segments + // should be rejoined. + + + function clipLine(stream) { + var point0, // previous point + c0, // code for previous point + v0, // visibility of previous point + v00, // visibility of first point + _clean; // no intersections + + + return { + lineStart: function lineStart() { + v00 = v0 = false; + _clean = 1; + }, + point: function point(lambda, phi) { + var point1 = [lambda, phi], + point2, + v = visible(lambda, phi), + c = smallRadius ? v ? 0 : code(lambda, phi) : v ? code(lambda + (lambda < 0 ? pi : -pi), phi) : 0; + if (!point0 && (v00 = v0 = v)) stream.lineStart(); + + if (v !== v0) { + point2 = intersect(point0, point1); + if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) point1[2] = 1; + } + + if (v !== v0) { + _clean = 0; + + if (v) { + // outside going in + stream.lineStart(); + point2 = intersect(point1, point0); + stream.point(point2[0], point2[1]); + } else { + // inside going out + point2 = intersect(point0, point1); + stream.point(point2[0], point2[1], 2); + stream.lineEnd(); + } + + point0 = point2; + } else if (notHemisphere && point0 && smallRadius ^ v) { + var t; // If the codes for two points are different, or are both zero, + // and there this segment intersects with the small circle. + + if (!(c & c0) && (t = intersect(point1, point0, true))) { + _clean = 0; + + if (smallRadius) { + stream.lineStart(); + stream.point(t[0][0], t[0][1]); + stream.point(t[1][0], t[1][1]); + stream.lineEnd(); + } else { + stream.point(t[1][0], t[1][1]); + stream.lineEnd(); + stream.lineStart(); + stream.point(t[0][0], t[0][1], 3); + } + } + } + + if (v && (!point0 || !pointEqual(point0, point1))) { + stream.point(point1[0], point1[1]); + } + + point0 = point1, v0 = v, c0 = c; + }, + lineEnd: function lineEnd() { + if (v0) stream.lineEnd(); + point0 = null; + }, + // Rejoin first and last segments if there were intersections and the first + // and last points were visible. + clean: function clean() { + return _clean | (v00 && v0) << 1; + } + }; + } // Intersects the great circle between a and b with the clip circle. + + + function intersect(a, b, two) { + var pa = cartesian(a), + pb = cartesian(b); // We have two planes, n1.p = d1 and n2.p = d2. + // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2). + + var n1 = [1, 0, 0], + // normal + n2 = cartesianCross(pa, pb), + n2n2 = cartesianDot(n2, n2), + n1n2 = n2[0], + // cartesianDot(n1, n2), + determinant = n2n2 - n1n2 * n1n2; // Two polar points. + + if (!determinant) return !two && a; + var c1 = cr * n2n2 / determinant, + c2 = -cr * n1n2 / determinant, + n1xn2 = cartesianCross(n1, n2), + A = cartesianScale(n1, c1), + B = cartesianScale(n2, c2); + cartesianAddInPlace(A, B); // Solve |p(t)|^2 = 1. + + var u = n1xn2, + w = cartesianDot(A, u), + uu = cartesianDot(u, u), + t2 = w * w - uu * (cartesianDot(A, A) - 1); + if (t2 < 0) return; + var t = sqrt(t2), + q = cartesianScale(u, (-w - t) / uu); + cartesianAddInPlace(q, A); + q = spherical(q); + if (!two) return q; // Two intersection points. + + var lambda0 = a[0], + lambda1 = b[0], + phi0 = a[1], + phi1 = b[1], + z; + if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z; + var delta = lambda1 - lambda0, + polar = abs$2(delta - pi) < epsilon$1, + meridian = polar || delta < epsilon$1; + if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z; // Check that the first point is between a and b. + + if (meridian ? polar ? phi0 + phi1 > 0 ^ q[1] < (abs$2(q[0] - lambda0) < epsilon$1 ? phi0 : phi1) : phi0 <= q[1] && q[1] <= phi1 : delta > pi ^ (lambda0 <= q[0] && q[0] <= lambda1)) { + var q1 = cartesianScale(u, (-w + t) / uu); + cartesianAddInPlace(q1, A); + return [q, spherical(q1)]; + } + } // Generates a 4-bit vector representing the location of a point relative to + // the small circle's bounding box. + + + function code(lambda, phi) { + var r = smallRadius ? radius : pi - radius, + code = 0; + if (lambda < -r) code |= 1; // left + else if (lambda > r) code |= 2; // right + + if (phi < -r) code |= 4; // below + else if (phi > r) code |= 8; // above + + return code; + } + + return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi, radius - pi]); + } + + function clipLine (a, b, x0, y0, x1, y1) { + var ax = a[0], + ay = a[1], + bx = b[0], + by = b[1], + t0 = 0, + t1 = 1, + dx = bx - ax, + dy = by - ay, + r; + r = x0 - ax; + if (!dx && r > 0) return; + r /= dx; + + if (dx < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dx > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + + r = x1 - ax; + if (!dx && r < 0) return; + r /= dx; + + if (dx < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dx > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + + r = y0 - ay; + if (!dy && r > 0) return; + r /= dy; + + if (dy < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dy > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + + r = y1 - ay; + if (!dy && r < 0) return; + r /= dy; + + if (dy < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dy > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + + if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy; + if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy; + return true; + } + + var clipMax = 1e9, + clipMin = -clipMax; // TODO Use d3-polygon’s polygonContains here for the ring check? + // TODO Eliminate duplicate buffering in clipBuffer and polygon.push? + + function clipRectangle(x0, y0, x1, y1) { + function visible(x, y) { + return x0 <= x && x <= x1 && y0 <= y && y <= y1; + } + + function interpolate(from, to, direction, stream) { + var a = 0, + a1 = 0; + + if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoint(from, to) < 0 ^ direction > 0) { + do { + stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); + } while ((a = (a + direction + 4) % 4) !== a1); + } else { + stream.point(to[0], to[1]); + } + } + + function corner(p, direction) { + return abs$2(p[0] - x0) < epsilon$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 + } + + function compareIntersection(a, b) { + return comparePoint(a.x, b.x); + } + + function comparePoint(a, b) { + var ca = corner(a, 1), + cb = corner(b, 1); + return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0]; + } + + return function (stream) { + var activeStream = stream, + bufferStream = clipBuffer(), + segments, + polygon, + ring, + x__, + y__, + v__, + // first point + x_, + y_, + v_, + // previous point + first, + clean; + var clipStream = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: polygonStart, + polygonEnd: polygonEnd + }; + + function point(x, y) { + if (visible(x, y)) activeStream.point(x, y); + } + + function polygonInside() { + var winding = 0; + + for (var i = 0, n = polygon.length; i < n; ++i) { + for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) { + a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1]; + + if (a1 <= y1) { + if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding; + } else { + if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding; + } + } + } + + return winding; + } // Buffer geometry within a polygon and then clip it en masse. + + + function polygonStart() { + activeStream = bufferStream, segments = [], polygon = [], clean = true; + } + + function polygonEnd() { + var startInside = polygonInside(), + cleanInside = clean && startInside, + visible = (segments = merge$4(segments)).length; + + if (cleanInside || visible) { + stream.polygonStart(); + + if (cleanInside) { + stream.lineStart(); + interpolate(null, null, 1, stream); + stream.lineEnd(); + } + + if (visible) { + clipRejoin(segments, compareIntersection, startInside, interpolate, stream); + } + + stream.polygonEnd(); + } + + activeStream = stream, segments = polygon = ring = null; + } + + function lineStart() { + clipStream.point = linePoint; + if (polygon) polygon.push(ring = []); + first = true; + v_ = false; + x_ = y_ = NaN; + } // TODO rather than special-case polygons, simply handle them separately. + // Ideally, coincident intersection points should be jittered to avoid + // clipping issues. + + + function lineEnd() { + if (segments) { + linePoint(x__, y__); + if (v__ && v_) bufferStream.rejoin(); + segments.push(bufferStream.result()); + } + + clipStream.point = point; + if (v_) activeStream.lineEnd(); + } + + function linePoint(x, y) { + var v = visible(x, y); + if (polygon) ring.push([x, y]); + + if (first) { + x__ = x, y__ = y, v__ = v; + first = false; + + if (v) { + activeStream.lineStart(); + activeStream.point(x, y); + } + } else { + if (v && v_) activeStream.point(x, y);else { + var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))], + b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))]; + + if (clipLine(a, b, x0, y0, x1, y1)) { + if (!v_) { + activeStream.lineStart(); + activeStream.point(a[0], a[1]); + } + + activeStream.point(b[0], b[1]); + if (!v) activeStream.lineEnd(); + clean = false; + } else if (v) { + activeStream.lineStart(); + activeStream.point(x, y); + clean = false; + } + } + } + + x_ = x, y_ = y, v_ = v; + } + + return clipStream; + }; + } + + var lengthSum$1, lambda0, sinPhi0, cosPhi0; + var lengthStream$1 = { + sphere: noop$1, + point: noop$1, + lineStart: lengthLineStart, + lineEnd: noop$1, + polygonStart: noop$1, + polygonEnd: noop$1 + }; + + function lengthLineStart() { + lengthStream$1.point = lengthPointFirst$1; + lengthStream$1.lineEnd = lengthLineEnd; + } + + function lengthLineEnd() { + lengthStream$1.point = lengthStream$1.lineEnd = noop$1; + } + + function lengthPointFirst$1(lambda, phi) { + lambda *= radians, phi *= radians; + lambda0 = lambda, sinPhi0 = sin(phi), cosPhi0 = cos(phi); + lengthStream$1.point = lengthPoint$1; + } + + function lengthPoint$1(lambda, phi) { + lambda *= radians, phi *= radians; + var sinPhi = sin(phi), + cosPhi = cos(phi), + delta = abs$2(lambda - lambda0), + cosDelta = cos(delta), + sinDelta = sin(delta), + x = cosPhi * sinDelta, + y = cosPhi0 * sinPhi - sinPhi0 * cosPhi * cosDelta, + z = sinPhi0 * sinPhi + cosPhi0 * cosPhi * cosDelta; + lengthSum$1.add(atan2(sqrt(x * x + y * y), z)); + lambda0 = lambda, sinPhi0 = sinPhi, cosPhi0 = cosPhi; + } + + function d3_geoLength (object) { + lengthSum$1 = new Adder(); + d3_geoStream(object, lengthStream$1); + return +lengthSum$1; + } + + var identity$4 = (function (x) { + return x; + }); + + var areaSum = new Adder(), + areaRingSum = new Adder(), + x00$2, + y00$2, + x0$3, + y0$3; + var areaStream = { + point: noop$1, + lineStart: noop$1, + lineEnd: noop$1, + polygonStart: function polygonStart() { + areaStream.lineStart = areaRingStart; + areaStream.lineEnd = areaRingEnd; + }, + polygonEnd: function polygonEnd() { + areaStream.lineStart = areaStream.lineEnd = areaStream.point = noop$1; + areaSum.add(abs$2(areaRingSum)); + areaRingSum = new Adder(); + }, + result: function result() { + var area = areaSum / 2; + areaSum = new Adder(); + return area; + } + }; + + function areaRingStart() { + areaStream.point = areaPointFirst; + } + + function areaPointFirst(x, y) { + areaStream.point = areaPoint; + x00$2 = x0$3 = x, y00$2 = y0$3 = y; + } + + function areaPoint(x, y) { + areaRingSum.add(y0$3 * x - x0$3 * y); + x0$3 = x, y0$3 = y; + } + + function areaRingEnd() { + areaPoint(x00$2, y00$2); + } + + var x0$2 = Infinity, + y0$2 = x0$2, + x1 = -x0$2, + y1 = x1; + var boundsStream = { + point: boundsPoint, + lineStart: noop$1, + lineEnd: noop$1, + polygonStart: noop$1, + polygonEnd: noop$1, + result: function result() { + var bounds = [[x0$2, y0$2], [x1, y1]]; + x1 = y1 = -(y0$2 = x0$2 = Infinity); + return bounds; + } + }; + + function boundsPoint(x, y) { + if (x < x0$2) x0$2 = x; + if (x > x1) x1 = x; + if (y < y0$2) y0$2 = y; + if (y > y1) y1 = y; + } + + var X0 = 0, + Y0 = 0, + Z0 = 0, + X1 = 0, + Y1 = 0, + Z1 = 0, + X2 = 0, + Y2 = 0, + Z2 = 0, + x00$1, + y00$1, + x0$1, + y0$1; + var centroidStream = { + point: centroidPoint, + lineStart: centroidLineStart, + lineEnd: centroidLineEnd, + polygonStart: function polygonStart() { + centroidStream.lineStart = centroidRingStart; + centroidStream.lineEnd = centroidRingEnd; + }, + polygonEnd: function polygonEnd() { + centroidStream.point = centroidPoint; + centroidStream.lineStart = centroidLineStart; + centroidStream.lineEnd = centroidLineEnd; + }, + result: function result() { + var centroid = Z2 ? [X2 / Z2, Y2 / Z2] : Z1 ? [X1 / Z1, Y1 / Z1] : Z0 ? [X0 / Z0, Y0 / Z0] : [NaN, NaN]; + X0 = Y0 = Z0 = X1 = Y1 = Z1 = X2 = Y2 = Z2 = 0; + return centroid; + } + }; + + function centroidPoint(x, y) { + X0 += x; + Y0 += y; + ++Z0; + } + + function centroidLineStart() { + centroidStream.point = centroidPointFirstLine; + } + + function centroidPointFirstLine(x, y) { + centroidStream.point = centroidPointLine; + centroidPoint(x0$1 = x, y0$1 = y); + } + + function centroidPointLine(x, y) { + var dx = x - x0$1, + dy = y - y0$1, + z = sqrt(dx * dx + dy * dy); + X1 += z * (x0$1 + x) / 2; + Y1 += z * (y0$1 + y) / 2; + Z1 += z; + centroidPoint(x0$1 = x, y0$1 = y); + } + + function centroidLineEnd() { + centroidStream.point = centroidPoint; + } + + function centroidRingStart() { + centroidStream.point = centroidPointFirstRing; + } + + function centroidRingEnd() { + centroidPointRing(x00$1, y00$1); + } + + function centroidPointFirstRing(x, y) { + centroidStream.point = centroidPointRing; + centroidPoint(x00$1 = x0$1 = x, y00$1 = y0$1 = y); + } + + function centroidPointRing(x, y) { + var dx = x - x0$1, + dy = y - y0$1, + z = sqrt(dx * dx + dy * dy); + X1 += z * (x0$1 + x) / 2; + Y1 += z * (y0$1 + y) / 2; + Z1 += z; + z = y0$1 * x - x0$1 * y; + X2 += z * (x0$1 + x); + Y2 += z * (y0$1 + y); + Z2 += z * 3; + centroidPoint(x0$1 = x, y0$1 = y); + } + + function PathContext(context) { + this._context = context; + } + PathContext.prototype = { + _radius: 4.5, + pointRadius: function pointRadius(_) { + return this._radius = _, this; + }, + polygonStart: function polygonStart() { + this._line = 0; + }, + polygonEnd: function polygonEnd() { + this._line = NaN; + }, + lineStart: function lineStart() { + this._point = 0; + }, + lineEnd: function lineEnd() { + if (this._line === 0) this._context.closePath(); + this._point = NaN; + }, + point: function point(x, y) { + switch (this._point) { + case 0: + { + this._context.moveTo(x, y); + + this._point = 1; + break; + } + + case 1: + { + this._context.lineTo(x, y); + + break; + } + + default: + { + this._context.moveTo(x + this._radius, y); + + this._context.arc(x, y, this._radius, 0, tau); + + break; + } + } + }, + result: noop$1 + }; + + var lengthSum = new Adder(), + lengthRing, + x00, + y00, + x0, + y0; + var lengthStream = { + point: noop$1, + lineStart: function lineStart() { + lengthStream.point = lengthPointFirst; + }, + lineEnd: function lineEnd() { + if (lengthRing) lengthPoint(x00, y00); + lengthStream.point = noop$1; + }, + polygonStart: function polygonStart() { + lengthRing = true; + }, + polygonEnd: function polygonEnd() { + lengthRing = null; + }, + result: function result() { + var length = +lengthSum; + lengthSum = new Adder(); + return length; + } + }; + + function lengthPointFirst(x, y) { + lengthStream.point = lengthPoint; + x00 = x0 = x, y00 = y0 = y; + } + + function lengthPoint(x, y) { + x0 -= x, y0 -= y; + lengthSum.add(sqrt(x0 * x0 + y0 * y0)); + x0 = x, y0 = y; + } + + function PathString() { + this._string = []; + } + PathString.prototype = { + _radius: 4.5, + _circle: circle(4.5), + pointRadius: function pointRadius(_) { + if ((_ = +_) !== this._radius) this._radius = _, this._circle = null; + return this; + }, + polygonStart: function polygonStart() { + this._line = 0; + }, + polygonEnd: function polygonEnd() { + this._line = NaN; + }, + lineStart: function lineStart() { + this._point = 0; + }, + lineEnd: function lineEnd() { + if (this._line === 0) this._string.push("Z"); + this._point = NaN; + }, + point: function point(x, y) { + switch (this._point) { + case 0: + { + this._string.push("M", x, ",", y); + + this._point = 1; + break; + } + + case 1: + { + this._string.push("L", x, ",", y); + + break; + } + + default: + { + if (this._circle == null) this._circle = circle(this._radius); + + this._string.push("M", x, ",", y, this._circle); + + break; + } + } + }, + result: function result() { + if (this._string.length) { + var result = this._string.join(""); + + this._string = []; + return result; + } else { + return null; + } + } + }; + + function circle(radius) { + return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z"; + } + + function d3_geoPath (projection, context) { + var pointRadius = 4.5, + projectionStream, + contextStream; + + function path(object) { + if (object) { + if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); + d3_geoStream(object, projectionStream(contextStream)); + } + + return contextStream.result(); + } + + path.area = function (object) { + d3_geoStream(object, projectionStream(areaStream)); + return areaStream.result(); + }; + + path.measure = function (object) { + d3_geoStream(object, projectionStream(lengthStream)); + return lengthStream.result(); + }; + + path.bounds = function (object) { + d3_geoStream(object, projectionStream(boundsStream)); + return boundsStream.result(); + }; + + path.centroid = function (object) { + d3_geoStream(object, projectionStream(centroidStream)); + return centroidStream.result(); + }; + + path.projection = function (_) { + return arguments.length ? (projectionStream = _ == null ? (projection = null, identity$4) : (projection = _).stream, path) : projection; + }; + + path.context = function (_) { + if (!arguments.length) return context; + contextStream = _ == null ? (context = null, new PathString()) : new PathContext(context = _); + if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); + return path; + }; + + path.pointRadius = function (_) { + if (!arguments.length) return pointRadius; + pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); + return path; + }; + + return path.projection(projection).context(context); + } + + function d3_geoTransform (methods) { + return { + stream: transformer$1(methods) + }; + } + function transformer$1(methods) { + return function (stream) { + var s = new TransformStream(); + + for (var key in methods) { + s[key] = methods[key]; + } + + s.stream = stream; + return s; + }; + } + + function TransformStream() {} + + TransformStream.prototype = { + constructor: TransformStream, + point: function point(x, y) { + this.stream.point(x, y); + }, + sphere: function sphere() { + this.stream.sphere(); + }, + lineStart: function lineStart() { + this.stream.lineStart(); + }, + lineEnd: function lineEnd() { + this.stream.lineEnd(); + }, + polygonStart: function polygonStart() { + this.stream.polygonStart(); + }, + polygonEnd: function polygonEnd() { + this.stream.polygonEnd(); + } + }; + + function fit(projection, fitBounds, object) { + var clip = projection.clipExtent && projection.clipExtent(); + projection.scale(150).translate([0, 0]); + if (clip != null) projection.clipExtent(null); + d3_geoStream(object, projection.stream(boundsStream)); + fitBounds(boundsStream.result()); + if (clip != null) projection.clipExtent(clip); + return projection; + } + + function fitExtent(projection, extent, object) { + return fit(projection, function (b) { + var w = extent[1][0] - extent[0][0], + h = extent[1][1] - extent[0][1], + k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])), + x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2, + y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2; + projection.scale(150 * k).translate([x, y]); + }, object); + } + function fitSize(projection, size, object) { + return fitExtent(projection, [[0, 0], size], object); + } + function fitWidth(projection, width, object) { + return fit(projection, function (b) { + var w = +width, + k = w / (b[1][0] - b[0][0]), + x = (w - k * (b[1][0] + b[0][0])) / 2, + y = -k * b[0][1]; + projection.scale(150 * k).translate([x, y]); + }, object); + } + function fitHeight(projection, height, object) { + return fit(projection, function (b) { + var h = +height, + k = h / (b[1][1] - b[0][1]), + x = -k * b[0][0], + y = (h - k * (b[1][1] + b[0][1])) / 2; + projection.scale(150 * k).translate([x, y]); + }, object); + } + + var maxDepth = 16, + // maximum depth of subdivision + cosMinDistance = cos(30 * radians); // cos(minimum angular distance) + + function resample (project, delta2) { + return +delta2 ? resample$1(project, delta2) : resampleNone(project); + } + + function resampleNone(project) { + return transformer$1({ + point: function point(x, y) { + x = project(x, y); + this.stream.point(x[0], x[1]); + } + }); + } + + function resample$1(project, delta2) { + function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) { + var dx = x1 - x0, + dy = y1 - y0, + d2 = dx * dx + dy * dy; + + if (d2 > 4 * delta2 && depth--) { + var a = a0 + a1, + b = b0 + b1, + c = c0 + c1, + m = sqrt(a * a + b * b + c * c), + phi2 = asin(c /= m), + lambda2 = abs$2(abs$2(c) - 1) < epsilon$1 || abs$2(lambda0 - lambda1) < epsilon$1 ? (lambda0 + lambda1) / 2 : atan2(b, a), + p = project(lambda2, phi2), + x2 = p[0], + y2 = p[1], + dx2 = x2 - x0, + dy2 = y2 - y0, + dz = dy * dx2 - dx * dy2; + + if (dz * dz / d2 > delta2 // perpendicular projected distance + || abs$2((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end + || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { + // angular distance + resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream); + stream.point(x2, y2); + resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream); + } + } + } + + return function (stream) { + var lambda00, x00, y00, a00, b00, c00, // first point + lambda0, x0, y0, a0, b0, c0; // previous point + + var resampleStream = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function polygonStart() { + stream.polygonStart(); + resampleStream.lineStart = ringStart; + }, + polygonEnd: function polygonEnd() { + stream.polygonEnd(); + resampleStream.lineStart = lineStart; + } + }; + + function point(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + } + + function lineStart() { + x0 = NaN; + resampleStream.point = linePoint; + stream.lineStart(); + } + + function linePoint(lambda, phi) { + var c = cartesian([lambda, phi]), + p = project(lambda, phi); + resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); + stream.point(x0, y0); + } + + function lineEnd() { + resampleStream.point = point; + stream.lineEnd(); + } + + function ringStart() { + lineStart(); + resampleStream.point = ringPoint; + resampleStream.lineEnd = ringEnd; + } + + function ringPoint(lambda, phi) { + linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; + resampleStream.point = linePoint; + } + + function ringEnd() { + resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream); + resampleStream.lineEnd = lineEnd; + lineEnd(); + } + + return resampleStream; + }; + } + + var transformRadians = transformer$1({ + point: function point(x, y) { + this.stream.point(x * radians, y * radians); + } + }); + + function transformRotate(rotate) { + return transformer$1({ + point: function point(x, y) { + var r = rotate(x, y); + return this.stream.point(r[0], r[1]); + } + }); + } + + function scaleTranslate(k, dx, dy, sx, sy) { + function transform(x, y) { + x *= sx; + y *= sy; + return [dx + k * x, dy - k * y]; + } + + transform.invert = function (x, y) { + return [(x - dx) / k * sx, (dy - y) / k * sy]; + }; + + return transform; + } + + function scaleTranslateRotate(k, dx, dy, sx, sy, alpha) { + if (!alpha) return scaleTranslate(k, dx, dy, sx, sy); + var cosAlpha = cos(alpha), + sinAlpha = sin(alpha), + a = cosAlpha * k, + b = sinAlpha * k, + ai = cosAlpha / k, + bi = sinAlpha / k, + ci = (sinAlpha * dy - cosAlpha * dx) / k, + fi = (sinAlpha * dx + cosAlpha * dy) / k; + + function transform(x, y) { + x *= sx; + y *= sy; + return [a * x - b * y + dx, dy - b * x - a * y]; + } + + transform.invert = function (x, y) { + return [sx * (ai * x - bi * y + ci), sy * (fi - bi * x - ai * y)]; + }; + + return transform; + } + + function projection(project) { + return projectionMutator(function () { + return project; + })(); + } + function projectionMutator(projectAt) { + var project, + k = 150, + // scale + x = 480, + y = 250, + // translate + lambda = 0, + phi = 0, + // center + deltaLambda = 0, + deltaPhi = 0, + deltaGamma = 0, + rotate, + // pre-rotate + alpha = 0, + // post-rotate angle + sx = 1, + // reflectX + sy = 1, + // reflectX + theta = null, + preclip = clipAntimeridian, + // pre-clip angle + x0 = null, + y0, + x1, + y1, + postclip = identity$4, + // post-clip extent + delta2 = 0.5, + // precision + projectResample, + projectTransform, + projectRotateTransform, + cache, + cacheStream; + + function projection(point) { + return projectRotateTransform(point[0] * radians, point[1] * radians); + } + + function invert(point) { + point = projectRotateTransform.invert(point[0], point[1]); + return point && [point[0] * degrees$1, point[1] * degrees$1]; + } + + projection.stream = function (stream) { + return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream))))); + }; + + projection.preclip = function (_) { + return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip; + }; + + projection.postclip = function (_) { + return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip; + }; + + projection.clipAngle = function (_) { + return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees$1; + }; + + projection.clipExtent = function (_) { + 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]]; + }; + + projection.scale = function (_) { + return arguments.length ? (k = +_, recenter()) : k; + }; + + projection.translate = function (_) { + return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y]; + }; + + projection.center = function (_) { + return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees$1, phi * degrees$1]; + }; + + projection.rotate = function (_) { + 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]; + }; + + projection.angle = function (_) { + return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees$1; + }; + + projection.reflectX = function (_) { + return arguments.length ? (sx = _ ? -1 : 1, recenter()) : sx < 0; + }; + + projection.reflectY = function (_) { + return arguments.length ? (sy = _ ? -1 : 1, recenter()) : sy < 0; + }; + + projection.precision = function (_) { + return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt(delta2); + }; + + projection.fitExtent = function (extent, object) { + return fitExtent(projection, extent, object); + }; + + projection.fitSize = function (size, object) { + return fitSize(projection, size, object); + }; + + projection.fitWidth = function (width, object) { + return fitWidth(projection, width, object); + }; + + projection.fitHeight = function (height, object) { + return fitHeight(projection, height, object); + }; + + function recenter() { + var center = scaleTranslateRotate(k, 0, 0, sx, sy, alpha).apply(null, project(lambda, phi)), + transform = scaleTranslateRotate(k, x - center[0], y - center[1], sx, sy, alpha); + rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma); + projectTransform = compose(project, transform); + projectRotateTransform = compose(rotate, projectTransform); + projectResample = resample(projectTransform, delta2); + return reset(); + } + + function reset() { + cache = cacheStream = null; + return projection; + } + + return function () { + project = projectAt.apply(this, arguments); + projection.invert = project.invert && invert; + return recenter(); + }; + } + + function mercatorRaw(lambda, phi) { + return [lambda, log$1(tan((halfPi + phi) / 2))]; + } + + mercatorRaw.invert = function (x, y) { + return [x, 2 * atan(exp$2(y)) - halfPi]; + }; + + function mercator () { + return mercatorProjection(mercatorRaw).scale(961 / tau); + } + function mercatorProjection(project) { + var m = projection(project), + center = m.center, + scale = m.scale, + translate = m.translate, + clipExtent = m.clipExtent, + x0 = null, + y0, + x1, + y1; // clip extent + + m.scale = function (_) { + return arguments.length ? (scale(_), reclip()) : scale(); + }; + + m.translate = function (_) { + return arguments.length ? (translate(_), reclip()) : translate(); + }; + + m.center = function (_) { + return arguments.length ? (center(_), reclip()) : center(); + }; + + m.clipExtent = function (_) { + return arguments.length ? (_ == null ? x0 = y0 = x1 = y1 = null : (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reclip()) : x0 == null ? null : [[x0, y0], [x1, y1]]; + }; + + function reclip() { + var k = pi * scale(), + t = m(rotation(m.rotate()).invert([0, 0])); + return clipExtent(x0 == null ? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw ? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]] : [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]); + } + + return reclip(); + } + + function d3_geoIdentity () { + var k = 1, + tx = 0, + ty = 0, + sx = 1, + sy = 1, + // scale, translate and reflect + alpha = 0, + ca, + sa, + // angle + x0 = null, + y0, + x1, + y1, + // clip extent + kx = 1, + ky = 1, + transform = transformer$1({ + point: function point(x, y) { + var p = projection([x, y]); + this.stream.point(p[0], p[1]); + } + }), + postclip = identity$4, + cache, + cacheStream; + + function reset() { + kx = k * sx; + ky = k * sy; + cache = cacheStream = null; + return projection; + } + + function projection(p) { + var x = p[0] * kx, + y = p[1] * ky; + + if (alpha) { + var t = y * ca - x * sa; + x = x * ca + y * sa; + y = t; + } + + return [x + tx, y + ty]; + } + + projection.invert = function (p) { + var x = p[0] - tx, + y = p[1] - ty; + + if (alpha) { + var t = y * ca + x * sa; + x = x * ca - y * sa; + y = t; + } + + return [x / kx, y / ky]; + }; + + projection.stream = function (stream) { + return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream)); + }; + + projection.postclip = function (_) { + return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip; + }; + + projection.clipExtent = function (_) { + return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]]; + }; + + projection.scale = function (_) { + return arguments.length ? (k = +_, reset()) : k; + }; + + projection.translate = function (_) { + return arguments.length ? (tx = +_[0], ty = +_[1], reset()) : [tx, ty]; + }; + + projection.angle = function (_) { + return arguments.length ? (alpha = _ % 360 * radians, sa = sin(alpha), ca = cos(alpha), reset()) : alpha * degrees$1; + }; + + projection.reflectX = function (_) { + return arguments.length ? (sx = _ ? -1 : 1, reset()) : sx < 0; + }; + + projection.reflectY = function (_) { + return arguments.length ? (sy = _ ? -1 : 1, reset()) : sy < 0; + }; + + projection.fitExtent = function (extent, object) { + return fitExtent(projection, extent, object); + }; + + projection.fitSize = function (size, object) { + return fitSize(projection, size, object); + }; + + projection.fitWidth = function (width, object) { + return fitWidth(projection, width, object); + }; + + projection.fitHeight = function (height, object) { + return fitHeight(projection, height, object); + }; + + return projection; + } + + // constants + var TAU = 2 * Math.PI; + var EQUATORIAL_RADIUS = 6356752.314245179; + var POLAR_RADIUS = 6378137.0; + function geoLatToMeters(dLat) { + return dLat * (TAU * POLAR_RADIUS / 360); + } + function geoLonToMeters(dLon, atLat) { + return Math.abs(atLat) >= 90 ? 0 : dLon * (TAU * EQUATORIAL_RADIUS / 360) * Math.abs(Math.cos(atLat * (Math.PI / 180))); + } + function geoMetersToLat(m) { + return m / (TAU * POLAR_RADIUS / 360); + } + function geoMetersToLon(m, atLat) { + return Math.abs(atLat) >= 90 ? 0 : m / (TAU * EQUATORIAL_RADIUS / 360) / Math.abs(Math.cos(atLat * (Math.PI / 180))); + } + function geoMetersToOffset(meters, tileSize) { + tileSize = tileSize || 256; + return [meters[0] * tileSize / (TAU * EQUATORIAL_RADIUS), -meters[1] * tileSize / (TAU * POLAR_RADIUS)]; + } + function geoOffsetToMeters(offset, tileSize) { + tileSize = tileSize || 256; + return [offset[0] * TAU * EQUATORIAL_RADIUS / tileSize, -offset[1] * TAU * POLAR_RADIUS / tileSize]; + } // Equirectangular approximation of spherical distances on Earth + + function geoSphericalDistance(a, b) { + var x = geoLonToMeters(a[0] - b[0], (a[1] + b[1]) / 2); + var y = geoLatToMeters(a[1] - b[1]); + return Math.sqrt(x * x + y * y); + } // scale to zoom + + function geoScaleToZoom(k, tileSize) { + tileSize = tileSize || 256; + var log2ts = Math.log(tileSize) * Math.LOG2E; + return Math.log(k * TAU) / Math.LN2 - log2ts; + } // zoom to scale + + function geoZoomToScale(z, tileSize) { + tileSize = tileSize || 256; + return tileSize * Math.pow(2, z) / TAU; + } // returns info about the node from `nodes` closest to the given `point` + + function geoSphericalClosestNode(nodes, point) { + var minDistance = Infinity, + distance; + var indexOfMin; + + for (var i in nodes) { + distance = geoSphericalDistance(nodes[i].loc, point); + + if (distance < minDistance) { + minDistance = distance; + indexOfMin = i; + } + } + + if (indexOfMin !== undefined) { + return { + index: indexOfMin, + distance: minDistance, + node: nodes[indexOfMin] + }; + } else { + return null; + } + } + + function geoExtent(min, max) { + if (!(this instanceof geoExtent)) { + return new geoExtent(min, max); + } else if (min instanceof geoExtent) { + return min; + } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) { + this[0] = min[0]; + this[1] = min[1]; + } else { + this[0] = min || [Infinity, Infinity]; + this[1] = max || min || [-Infinity, -Infinity]; + } + } + geoExtent.prototype = new Array(2); + Object.assign(geoExtent.prototype, { + equals: function equals(obj) { + return this[0][0] === obj[0][0] && this[0][1] === obj[0][1] && this[1][0] === obj[1][0] && this[1][1] === obj[1][1]; + }, + extend: function extend(obj) { + if (!(obj instanceof geoExtent)) obj = new geoExtent(obj); + return geoExtent([Math.min(obj[0][0], this[0][0]), Math.min(obj[0][1], this[0][1])], [Math.max(obj[1][0], this[1][0]), Math.max(obj[1][1], this[1][1])]); + }, + _extend: function _extend(extent) { + this[0][0] = Math.min(extent[0][0], this[0][0]); + this[0][1] = Math.min(extent[0][1], this[0][1]); + this[1][0] = Math.max(extent[1][0], this[1][0]); + this[1][1] = Math.max(extent[1][1], this[1][1]); + }, + area: function area() { + return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1])); + }, + center: function center() { + return [(this[0][0] + this[1][0]) / 2, (this[0][1] + this[1][1]) / 2]; + }, + rectangle: function rectangle() { + return [this[0][0], this[0][1], this[1][0], this[1][1]]; + }, + bbox: function bbox() { + return { + minX: this[0][0], + minY: this[0][1], + maxX: this[1][0], + maxY: this[1][1] + }; + }, + polygon: function polygon() { + return [[this[0][0], this[0][1]], [this[0][0], this[1][1]], [this[1][0], this[1][1]], [this[1][0], this[0][1]], [this[0][0], this[0][1]]]; + }, + contains: function contains(obj) { + if (!(obj instanceof geoExtent)) obj = new geoExtent(obj); + return obj[0][0] >= this[0][0] && obj[0][1] >= this[0][1] && obj[1][0] <= this[1][0] && obj[1][1] <= this[1][1]; + }, + intersects: function intersects(obj) { + if (!(obj instanceof geoExtent)) obj = new geoExtent(obj); + return obj[0][0] <= this[1][0] && obj[0][1] <= this[1][1] && obj[1][0] >= this[0][0] && obj[1][1] >= this[0][1]; + }, + intersection: function intersection(obj) { + if (!this.intersects(obj)) return new geoExtent(); + return new geoExtent([Math.max(obj[0][0], this[0][0]), Math.max(obj[0][1], this[0][1])], [Math.min(obj[1][0], this[1][0]), Math.min(obj[1][1], this[1][1])]); + }, + percentContainedIn: function percentContainedIn(obj) { + if (!(obj instanceof geoExtent)) obj = new geoExtent(obj); + var a1 = this.intersection(obj).area(); + var a2 = this.area(); + + if (a1 === Infinity || a2 === Infinity) { + return 0; + } else if (a1 === 0 || a2 === 0) { + if (obj.contains(this)) { + return 1; + } + + return 0; + } else { + return a1 / a2; + } + }, + padByMeters: function padByMeters(meters) { + var dLat = geoMetersToLat(meters); + var dLon = geoMetersToLon(meters, this.center()[1]); + return geoExtent([this[0][0] - dLon, this[0][1] - dLat], [this[1][0] + dLon, this[1][1] + dLat]); + }, + toParam: function toParam() { + return this.rectangle().join(','); + } + }); + + var $$p = _export; + var $every = arrayIteration.every; + var arrayMethodIsStrict$1 = arrayMethodIsStrict$8; + + var STRICT_METHOD$1 = arrayMethodIsStrict$1('every'); + + // `Array.prototype.every` method + // https://tc39.es/ecma262/#sec-array.prototype.every + $$p({ target: 'Array', proto: true, forced: !STRICT_METHOD$1 }, { + every: function every(callbackfn /* , thisArg */) { + return $every(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); + } + }); + + var $$o = _export; + var $reduce = arrayReduce.left; + var arrayMethodIsStrict = arrayMethodIsStrict$8; + var CHROME_VERSION = engineV8Version; + var IS_NODE = engineIsNode; + + var STRICT_METHOD = arrayMethodIsStrict('reduce'); + // Chrome 80-82 has a critical bug + // https://bugs.chromium.org/p/chromium/issues/detail?id=1049982 + var CHROME_BUG = !IS_NODE && CHROME_VERSION > 79 && CHROME_VERSION < 83; + + // `Array.prototype.reduce` method + // https://tc39.es/ecma262/#sec-array.prototype.reduce + $$o({ target: 'Array', proto: true, forced: !STRICT_METHOD || CHROME_BUG }, { + reduce: function reduce(callbackfn /* , initialValue */) { + return $reduce(this, callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined); + } + }); + + function d3_polygonArea (polygon) { + var i = -1, + n = polygon.length, + a, + b = polygon[n - 1], + area = 0; + + while (++i < n) { + a = b; + b = polygon[i]; + area += a[1] * b[0] - a[0] * b[1]; + } + + return area / 2; + } + + function d3_polygonCentroid (polygon) { + var i = -1, + n = polygon.length, + x = 0, + y = 0, + a, + b = polygon[n - 1], + c, + k = 0; + + while (++i < n) { + a = b; + b = polygon[i]; + k += c = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * c; + y += (a[1] + b[1]) * c; + } + + return k *= 3, [x / k, y / k]; + } + + // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of + // the 3D cross product in a quadrant I Cartesian coordinate system (+x is + // right, +y is up). Returns a positive value if ABC is counter-clockwise, + // negative if clockwise, and zero if the points are collinear. + function cross (a, b, c) { + return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); + } + + function lexicographicOrder(a, b) { + return a[0] - b[0] || a[1] - b[1]; + } // Computes the upper convex hull per the monotone chain algorithm. + // Assumes points.length >= 3, is sorted by x, unique in y. + // Returns an array of indices into points in left-to-right order. + + + function computeUpperHullIndexes(points) { + var n = points.length, + indexes = [0, 1]; + var size = 2, + i; + + for (i = 2; i < n; ++i) { + while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) { + --size; + } + + indexes[size++] = i; + } + + return indexes.slice(0, size); // remove popped points + } + + function d3_polygonHull (points) { + if ((n = points.length) < 3) return null; + var i, + n, + sortedPoints = new Array(n), + flippedPoints = new Array(n); + + for (i = 0; i < n; ++i) { + sortedPoints[i] = [+points[i][0], +points[i][1], i]; + } + + sortedPoints.sort(lexicographicOrder); + + for (i = 0; i < n; ++i) { + flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]]; + } + + var upperIndexes = computeUpperHullIndexes(sortedPoints), + lowerIndexes = computeUpperHullIndexes(flippedPoints); // Construct the hull polygon, removing possible duplicate endpoints. + + var skipLeft = lowerIndexes[0] === upperIndexes[0], + skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1], + hull = []; // Add upper hull in right-to-l order. + // Then add lower hull in left-to-right order. + + for (i = upperIndexes.length - 1; i >= 0; --i) { + hull.push(points[sortedPoints[upperIndexes[i]][2]]); + } + + for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) { + hull.push(points[sortedPoints[lowerIndexes[i]][2]]); + } + + return hull; + } + + // vector equals + function geoVecEqual(a, b, epsilon) { + if (epsilon) { + return Math.abs(a[0] - b[0]) <= epsilon && Math.abs(a[1] - b[1]) <= epsilon; + } else { + return a[0] === b[0] && a[1] === b[1]; + } + } // vector addition + + function geoVecAdd(a, b) { + return [a[0] + b[0], a[1] + b[1]]; + } // vector subtraction + + function geoVecSubtract(a, b) { + return [a[0] - b[0], a[1] - b[1]]; + } // vector scaling + + function geoVecScale(a, mag) { + return [a[0] * mag, a[1] * mag]; + } // vector rounding (was: geoRoundCoordinates) + + function geoVecFloor(a) { + return [Math.floor(a[0]), Math.floor(a[1])]; + } // linear interpolation + + function geoVecInterp(a, b, t) { + return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]; + } // http://jsperf.com/id-dist-optimization + + function geoVecLength(a, b) { + return Math.sqrt(geoVecLengthSquare(a, b)); + } // length of vector raised to the power two + + function geoVecLengthSquare(a, b) { + b = b || [0, 0]; + var x = a[0] - b[0]; + var y = a[1] - b[1]; + return x * x + y * y; + } // get a unit vector + + function geoVecNormalize(a) { + var length = Math.sqrt(a[0] * a[0] + a[1] * a[1]); + + if (length !== 0) { + return geoVecScale(a, 1 / length); + } + + return [0, 0]; + } // Return the counterclockwise angle in the range (-pi, pi) + // between the positive X axis and the line intersecting a and b. + + function geoVecAngle(a, b) { + return Math.atan2(b[1] - a[1], b[0] - a[0]); + } // dot product + + function geoVecDot(a, b, origin) { + origin = origin || [0, 0]; + var p = geoVecSubtract(a, origin); + var q = geoVecSubtract(b, origin); + return p[0] * q[0] + p[1] * q[1]; + } // normalized dot product + + function geoVecNormalizedDot(a, b, origin) { + origin = origin || [0, 0]; + var p = geoVecNormalize(geoVecSubtract(a, origin)); + var q = geoVecNormalize(geoVecSubtract(b, origin)); + return geoVecDot(p, q); + } // 2D cross product of OA and OB vectors, returns magnitude of Z vector + // Returns a positive value, if OAB makes a counter-clockwise turn, + // negative for clockwise turn, and zero if the points are collinear. + + function geoVecCross(a, b, origin) { + origin = origin || [0, 0]; + var p = geoVecSubtract(a, origin); + var q = geoVecSubtract(b, origin); + return p[0] * q[1] - p[1] * q[0]; + } // find closest orthogonal projection of point onto points array + + function geoVecProject(a, points) { + var min = Infinity; + var idx; + var target; + + for (var i = 0; i < points.length - 1; i++) { + var o = points[i]; + var s = geoVecSubtract(points[i + 1], o); + var v = geoVecSubtract(a, o); + var proj = geoVecDot(v, s) / geoVecDot(s, s); + var p; + + if (proj < 0) { + p = o; + } else if (proj > 1) { + p = points[i + 1]; + } else { + p = [o[0] + proj * s[0], o[1] + proj * s[1]]; + } + + var dist = geoVecLength(p, a); + + if (dist < min) { + min = dist; + idx = i + 1; + target = p; + } + } + + if (idx !== undefined) { + return { + index: idx, + distance: min, + target: target + }; + } else { + return null; + } + } + + // between the positive X axis and the line intersecting a and b. + + function geoAngle(a, b, projection) { + return geoVecAngle(projection(a.loc), projection(b.loc)); + } + function geoEdgeEqual(a, b) { + return a[0] === b[0] && a[1] === b[1] || a[0] === b[1] && a[1] === b[0]; + } // Rotate all points counterclockwise around a pivot point by given angle + + function geoRotate(points, angle, around) { + return points.map(function (point) { + var radial = geoVecSubtract(point, around); + return [radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + around[0], radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + around[1]]; + }); + } // Choose the edge with the minimal distance from `point` to its orthogonal + // projection onto that edge, if such a projection exists, or the distance to + // the closest vertex on that edge. Returns an object with the `index` of the + // chosen edge, the chosen `loc` on that edge, and the `distance` to to it. + + function geoChooseEdge(nodes, point, projection, activeID) { + var dist = geoVecLength; + var points = nodes.map(function (n) { + return projection(n.loc); + }); + var ids = nodes.map(function (n) { + return n.id; + }); + var min = Infinity; + var idx; + var loc; + + for (var i = 0; i < points.length - 1; i++) { + if (ids[i] === activeID || ids[i + 1] === activeID) continue; + var o = points[i]; + var s = geoVecSubtract(points[i + 1], o); + var v = geoVecSubtract(point, o); + var proj = geoVecDot(v, s) / geoVecDot(s, s); + var p; + + if (proj < 0) { + p = o; + } else if (proj > 1) { + p = points[i + 1]; + } else { + p = [o[0] + proj * s[0], o[1] + proj * s[1]]; + } + + var d = dist(p, point); + + if (d < min) { + min = d; + idx = i + 1; + loc = projection.invert(p); + } + } + + if (idx !== undefined) { + return { + index: idx, + distance: min, + loc: loc + }; + } else { + return null; + } + } // Test active (dragged or drawing) segments against inactive segments + // This is used to test e.g. multipolygon rings that cross + // `activeNodes` is the ring containing the activeID being dragged. + // `inactiveNodes` is the other ring to test against + + function geoHasLineIntersections(activeNodes, inactiveNodes, activeID) { + var actives = []; + var inactives = []; + var j, k, n1, n2, segment; // gather active segments (only segments in activeNodes that contain the activeID) + + for (j = 0; j < activeNodes.length - 1; j++) { + n1 = activeNodes[j]; + n2 = activeNodes[j + 1]; + segment = [n1.loc, n2.loc]; + + if (n1.id === activeID || n2.id === activeID) { + actives.push(segment); + } + } // gather inactive segments + + + for (j = 0; j < inactiveNodes.length - 1; j++) { + n1 = inactiveNodes[j]; + n2 = inactiveNodes[j + 1]; + segment = [n1.loc, n2.loc]; + inactives.push(segment); + } // test + + + for (j = 0; j < actives.length; j++) { + for (k = 0; k < inactives.length; k++) { + var p = actives[j]; + var q = inactives[k]; + var hit = geoLineIntersection(p, q); + + if (hit) { + return true; + } + } + } + + return false; + } // Test active (dragged or drawing) segments against inactive segments + // This is used to test whether a way intersects with itself. + + function geoHasSelfIntersections(nodes, activeID) { + var actives = []; + var inactives = []; + var j, k; // group active and passive segments along the nodes + + for (j = 0; j < nodes.length - 1; j++) { + var n1 = nodes[j]; + var n2 = nodes[j + 1]; + var segment = [n1.loc, n2.loc]; + + if (n1.id === activeID || n2.id === activeID) { + actives.push(segment); + } else { + inactives.push(segment); + } + } // test + + + for (j = 0; j < actives.length; j++) { + for (k = 0; k < inactives.length; k++) { + var p = actives[j]; + var q = inactives[k]; // skip if segments share an endpoint + + if (geoVecEqual(p[1], q[0]) || geoVecEqual(p[0], q[1]) || geoVecEqual(p[0], q[0]) || geoVecEqual(p[1], q[1])) { + continue; + } + + var hit = geoLineIntersection(p, q); + + if (hit) { + var epsilon = 1e-8; // skip if the hit is at the segment's endpoint + + if (geoVecEqual(p[1], hit, epsilon) || geoVecEqual(p[0], hit, epsilon) || geoVecEqual(q[1], hit, epsilon) || geoVecEqual(q[0], hit, epsilon)) { + continue; + } else { + return true; + } + } + } + } + + return false; + } // Return the intersection point of 2 line segments. + // From https://github.com/pgkelley4/line-segments-intersect + // This uses the vector cross product approach described below: + // http://stackoverflow.com/a/565282/786339 + + function geoLineIntersection(a, b) { + var p = [a[0][0], a[0][1]]; + var p2 = [a[1][0], a[1][1]]; + var q = [b[0][0], b[0][1]]; + var q2 = [b[1][0], b[1][1]]; + var r = geoVecSubtract(p2, p); + var s = geoVecSubtract(q2, q); + var uNumerator = geoVecCross(geoVecSubtract(q, p), r); + var denominator = geoVecCross(r, s); + + if (uNumerator && denominator) { + var u = uNumerator / denominator; + var t = geoVecCross(geoVecSubtract(q, p), s) / denominator; + + if (t >= 0 && t <= 1 && u >= 0 && u <= 1) { + return geoVecInterp(p, p2, t); + } + } + + return null; + } + function geoPathIntersections(path1, path2) { + var intersections = []; + + for (var i = 0; i < path1.length - 1; i++) { + for (var j = 0; j < path2.length - 1; j++) { + var a = [path1[i], path1[i + 1]]; + var b = [path2[j], path2[j + 1]]; + var hit = geoLineIntersection(a, b); + + if (hit) { + intersections.push(hit); + } + } + } + + return intersections; + } + function geoPathHasIntersections(path1, path2) { + for (var i = 0; i < path1.length - 1; i++) { + for (var j = 0; j < path2.length - 1; j++) { + var a = [path1[i], path1[i + 1]]; + var b = [path2[j], path2[j + 1]]; + var hit = geoLineIntersection(a, b); + + if (hit) { + return true; + } + } + } + + return false; + } // Return whether point is contained in polygon. + // + // `point` should be a 2-item array of coordinates. + // `polygon` should be an array of 2-item arrays of coordinates. + // + // From https://github.com/substack/point-in-polygon. + // ray-casting algorithm based on + // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html + // + + function geoPointInPolygon(point, polygon) { + var x = point[0]; + var y = point[1]; + var inside = false; + + for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { + var xi = polygon[i][0]; + var yi = polygon[i][1]; + var xj = polygon[j][0]; + var yj = polygon[j][1]; + var intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi; + if (intersect) inside = !inside; + } + + return inside; + } + function geoPolygonContainsPolygon(outer, inner) { + return inner.every(function (point) { + return geoPointInPolygon(point, outer); + }); + } + function geoPolygonIntersectsPolygon(outer, inner, checkSegments) { + function testPoints(outer, inner) { + return inner.some(function (point) { + return geoPointInPolygon(point, outer); + }); + } + + return testPoints(outer, inner) || !!checkSegments && geoPathHasIntersections(outer, inner); + } // http://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points + // http://gis.stackexchange.com/questions/3739/generalisation-strategies-for-building-outlines/3756#3756 + + function geoGetSmallestSurroundingRectangle(points) { + var hull = d3_polygonHull(points); + var centroid = d3_polygonCentroid(hull); + var minArea = Infinity; + var ssrExtent = []; + var ssrAngle = 0; + var c1 = hull[0]; + + for (var i = 0; i <= hull.length - 1; i++) { + var c2 = i === hull.length - 1 ? hull[0] : hull[i + 1]; + var angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]); + var poly = geoRotate(hull, -angle, centroid); + var extent = poly.reduce(function (extent, point) { + return extent.extend(geoExtent(point)); + }, geoExtent()); + var area = extent.area(); + + if (area < minArea) { + minArea = area; + ssrExtent = extent; + ssrAngle = angle; + } + + c1 = c2; + } + + return { + poly: geoRotate(ssrExtent.polygon(), ssrAngle, centroid), + angle: ssrAngle + }; + } + function geoPathLength(path) { + var length = 0; + + for (var i = 0; i < path.length - 1; i++) { + length += geoVecLength(path[i], path[i + 1]); + } + + return length; + } // If the given point is at the edge of the padded viewport, + // return a vector that will nudge the viewport in that direction + + function geoViewportEdge(point, dimensions) { + var pad = [80, 20, 50, 20]; // top, right, bottom, left + + var x = 0; + var y = 0; + + if (point[0] > dimensions[0] - pad[1]) { + x = -10; + } + + if (point[0] < pad[3]) { + x = 10; + } + + if (point[1] > dimensions[1] - pad[2]) { + y = -10; + } + + if (point[1] < pad[0]) { + y = 10; + } + + if (x || y) { + return [x, y]; + } else { + return null; + } + } + + var noop = { + value: function value() {} + }; + + function dispatch$8() { + for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) { + if (!(t = arguments[i] + "") || t in _ || /[\s.]/.test(t)) throw new Error("illegal type: " + t); + _[t] = []; + } + + return new Dispatch(_); + } + + function Dispatch(_) { + this._ = _; + } + + function parseTypenames$1(typenames, types) { + return typenames.trim().split(/^|\s+/).map(function (t) { + var name = "", + i = t.indexOf("."); + if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); + if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t); + return { + type: t, + name: name + }; + }); + } + + Dispatch.prototype = dispatch$8.prototype = { + constructor: Dispatch, + on: function on(typename, callback) { + var _ = this._, + T = parseTypenames$1(typename + "", _), + t, + i = -1, + n = T.length; // If no callback was specified, return the callback of the given type and name. + + if (arguments.length < 2) { + while (++i < n) { + if ((t = (typename = T[i]).type) && (t = get$2(_[t], typename.name))) return t; + } + + return; + } // If a type was specified, set the callback for the given type and name. + // Otherwise, if a null callback was specified, remove callbacks of the given name. + + + if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback); + + while (++i < n) { + if (t = (typename = T[i]).type) _[t] = set$1(_[t], typename.name, callback);else if (callback == null) for (t in _) { + _[t] = set$1(_[t], typename.name, null); + } + } + + return this; + }, + copy: function copy() { + var copy = {}, + _ = this._; + + for (var t in _) { + copy[t] = _[t].slice(); + } + + return new Dispatch(copy); + }, + call: function call(type, that) { + if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) { + args[i] = arguments[i + 2]; + } + if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); + + for (t = this._[type], i = 0, n = t.length; i < n; ++i) { + t[i].value.apply(that, args); + } + }, + apply: function apply(type, that, args) { + if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); + + for (var t = this._[type], i = 0, n = t.length; i < n; ++i) { + t[i].value.apply(that, args); + } + } + }; + + function get$2(type, name) { + for (var i = 0, n = type.length, c; i < n; ++i) { + if ((c = type[i]).name === name) { + return c.value; + } + } + } + + function set$1(type, name, callback) { + for (var i = 0, n = type.length; i < n; ++i) { + if (type[i].name === name) { + type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1)); + break; + } + } + + if (callback != null) type.push({ + name: name, + value: callback + }); + return type; + } + + var xhtml = "http://www.w3.org/1999/xhtml"; + var namespaces = { + svg: "http://www.w3.org/2000/svg", + xhtml: xhtml, + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" + }; + + function namespace (name) { + var prefix = name += "", + i = prefix.indexOf(":"); + if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); + return namespaces.hasOwnProperty(prefix) ? { + space: namespaces[prefix], + local: name + } : name; // eslint-disable-line no-prototype-builtins + } + + function creatorInherit(name) { + return function () { + var document = this.ownerDocument, + uri = this.namespaceURI; + return uri === xhtml && document.documentElement.namespaceURI === xhtml ? document.createElement(name) : document.createElementNS(uri, name); + }; + } + + function creatorFixed(fullname) { + return function () { + return this.ownerDocument.createElementNS(fullname.space, fullname.local); + }; + } + + function creator (name) { + var fullname = namespace(name); + return (fullname.local ? creatorFixed : creatorInherit)(fullname); + } + + function none() {} + + function selector (selector) { + return selector == null ? none : function () { + return this.querySelector(selector); + }; + } + + function selection_select (select) { + if (typeof select !== "function") select = selector(select); + + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { + if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + subgroup[i] = subnode; + } + } + } + + return new Selection$1(subgroups, this._parents); + } + + function array (x) { + return _typeof(x) === "object" && "length" in x ? x // Array, TypedArray, NodeList, array-like + : Array.from(x); // Map, Set, iterable, string, or anything else + } + + function empty() { + return []; + } + + function selectorAll (selector) { + return selector == null ? empty : function () { + return this.querySelectorAll(selector); + }; + } + + function arrayAll(select) { + return function () { + var group = select.apply(this, arguments); + return group == null ? [] : array(group); + }; + } + + function selection_selectAll (select) { + if (typeof select === "function") select = arrayAll(select);else select = selectorAll(select); + + for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { + if (node = group[i]) { + subgroups.push(select.call(node, node.__data__, i, group)); + parents.push(node); + } + } + } + + return new Selection$1(subgroups, parents); + } + + var $$n = _export; + var $find = arrayIteration.find; + var addToUnscopables$2 = addToUnscopables$5; + + var FIND = 'find'; + var SKIPS_HOLES$1 = true; + + // Shouldn't skip holes + if (FIND in []) Array(1)[FIND](function () { SKIPS_HOLES$1 = false; }); + + // `Array.prototype.find` method + // https://tc39.es/ecma262/#sec-array.prototype.find + $$n({ target: 'Array', proto: true, forced: SKIPS_HOLES$1 }, { + find: function find(callbackfn /* , that = undefined */) { + return $find(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); + } + }); + + // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables + addToUnscopables$2(FIND); + + function matcher (selector) { + return function () { + return this.matches(selector); + }; + } + function childMatcher(selector) { + return function (node) { + return node.matches(selector); + }; + } + + var find = Array.prototype.find; + + function childFind(match) { + return function () { + return find.call(this.children, match); + }; + } + + function childFirst() { + return this.firstElementChild; + } + + function selection_selectChild (match) { + return this.select(match == null ? childFirst : childFind(typeof match === "function" ? match : childMatcher(match))); + } + + var filter = Array.prototype.filter; + + function children() { + return this.children; + } + + function childrenFilter(match) { + return function () { + return filter.call(this.children, match); + }; + } + + function selection_selectChildren (match) { + return this.selectAll(match == null ? children : childrenFilter(typeof match === "function" ? match : childMatcher(match))); + } + + function selection_filter (match) { + if (typeof match !== "function") match = matcher(match); + + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { + if ((node = group[i]) && match.call(node, node.__data__, i, group)) { + subgroup.push(node); + } + } + } + + return new Selection$1(subgroups, this._parents); + } + + function sparse (update) { + return new Array(update.length); + } + + function selection_enter () { + return new Selection$1(this._enter || this._groups.map(sparse), this._parents); + } + function EnterNode(parent, datum) { + this.ownerDocument = parent.ownerDocument; + this.namespaceURI = parent.namespaceURI; + this._next = null; + this._parent = parent; + this.__data__ = datum; + } + EnterNode.prototype = { + constructor: EnterNode, + appendChild: function appendChild(child) { + return this._parent.insertBefore(child, this._next); + }, + insertBefore: function insertBefore(child, next) { + return this._parent.insertBefore(child, next); + }, + querySelector: function querySelector(selector) { + return this._parent.querySelector(selector); + }, + querySelectorAll: function querySelectorAll(selector) { + return this._parent.querySelectorAll(selector); + } + }; + + function constant$3 (x) { + return function () { + return x; + }; + } + + function bindIndex(parent, group, enter, update, exit, data) { + var i = 0, + node, + groupLength = group.length, + dataLength = data.length; // Put any non-null nodes that fit into update. + // Put any null nodes into enter. + // Put any remaining data into enter. + + for (; i < dataLength; ++i) { + if (node = group[i]) { + node.__data__ = data[i]; + update[i] = node; + } else { + enter[i] = new EnterNode(parent, data[i]); + } + } // Put any non-null nodes that don’t fit into exit. + + + for (; i < groupLength; ++i) { + if (node = group[i]) { + exit[i] = node; + } + } + } + + function bindKey(parent, group, enter, update, exit, data, key) { + var i, + node, + nodeByKeyValue = new Map(), + groupLength = group.length, + dataLength = data.length, + keyValues = new Array(groupLength), + keyValue; // Compute the key for each node. + // If multiple nodes have the same key, the duplicates are added to exit. + + for (i = 0; i < groupLength; ++i) { + if (node = group[i]) { + keyValues[i] = keyValue = key.call(node, node.__data__, i, group) + ""; + + if (nodeByKeyValue.has(keyValue)) { + exit[i] = node; + } else { + nodeByKeyValue.set(keyValue, node); + } + } + } // Compute the key for each datum. + // If there a node associated with this key, join and add it to update. + // If there is not (or the key is a duplicate), add it to enter. + + + for (i = 0; i < dataLength; ++i) { + keyValue = key.call(parent, data[i], i, data) + ""; + + if (node = nodeByKeyValue.get(keyValue)) { + update[i] = node; + node.__data__ = data[i]; + nodeByKeyValue["delete"](keyValue); + } else { + enter[i] = new EnterNode(parent, data[i]); + } + } // Add any remaining nodes that were not bound to data to exit. + + + for (i = 0; i < groupLength; ++i) { + if ((node = group[i]) && nodeByKeyValue.get(keyValues[i]) === node) { + exit[i] = node; + } + } + } + + function datum(node) { + return node.__data__; + } + + function selection_data (value, key) { + if (!arguments.length) return Array.from(this, datum); + var bind = key ? bindKey : bindIndex, + parents = this._parents, + groups = this._groups; + if (typeof value !== "function") value = constant$3(value); + + for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) { + var parent = parents[j], + group = groups[j], + groupLength = group.length, + data = array(value.call(parent, parent && parent.__data__, j, parents)), + dataLength = data.length, + enterGroup = enter[j] = new Array(dataLength), + updateGroup = update[j] = new Array(dataLength), + exitGroup = exit[j] = new Array(groupLength); + bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); // Now connect the enter nodes to their following update node, such that + // appendChild can insert the materialized enter node before this node, + // rather than at the end of the parent node. + + for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) { + if (previous = enterGroup[i0]) { + if (i0 >= i1) i1 = i0 + 1; + + while (!(next = updateGroup[i1]) && ++i1 < dataLength) { + } + + previous._next = next || null; + } + } + } + + update = new Selection$1(update, parents); + update._enter = enter; + update._exit = exit; + return update; + } + + function selection_exit () { + return new Selection$1(this._exit || this._groups.map(sparse), this._parents); + } + + function selection_join (onenter, onupdate, onexit) { + var enter = this.enter(), + update = this, + exit = this.exit(); + enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + ""); + if (onupdate != null) update = onupdate(update); + if (onexit == null) exit.remove();else onexit(exit); + return enter && update ? enter.merge(update).order() : update; + } + + function selection_merge (selection) { + if (!(selection instanceof Selection$1)) throw new Error("invalid merge"); + + for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { + for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { + if (node = group0[i] || group1[i]) { + merge[i] = node; + } + } + } + + for (; j < m0; ++j) { + merges[j] = groups0[j]; + } + + return new Selection$1(merges, this._parents); + } + + function selection_order () { + for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) { + for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) { + if (node = group[i]) { + if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next); + next = node; + } + } + } + + return this; + } + + function selection_sort (compare) { + if (!compare) compare = ascending; + + function compareNode(a, b) { + return a && b ? compare(a.__data__, b.__data__) : !a - !b; + } + + for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) { + if (node = group[i]) { + sortgroup[i] = node; + } + } + + sortgroup.sort(compareNode); + } + + return new Selection$1(sortgroups, this._parents).order(); + } + + function ascending(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; + } + + function selection_call () { + var callback = arguments[0]; + arguments[0] = this; + callback.apply(null, arguments); + return this; + } + + function selection_nodes () { + return Array.from(this); + } + + function selection_node () { + for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { + for (var group = groups[j], i = 0, n = group.length; i < n; ++i) { + var node = group[i]; + if (node) return node; + } + } + + return null; + } + + function selection_size () { + var size = 0; + + var _iterator = _createForOfIteratorHelper(this), + _step; + + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var node = _step.value; + ++size; + } // eslint-disable-line no-unused-vars + + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + + return size; + } + + function selection_empty () { + return !this.node(); + } + + function selection_each (callback) { + for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { + for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { + if (node = group[i]) callback.call(node, node.__data__, i, group); + } + } + + return this; + } + + function attrRemove$1(name) { + return function () { + this.removeAttribute(name); + }; + } + + function attrRemoveNS$1(fullname) { + return function () { + this.removeAttributeNS(fullname.space, fullname.local); + }; + } + + function attrConstant$1(name, value) { + return function () { + this.setAttribute(name, value); + }; + } + + function attrConstantNS$1(fullname, value) { + return function () { + this.setAttributeNS(fullname.space, fullname.local, value); + }; + } + + function attrFunction$1(name, value) { + return function () { + var v = value.apply(this, arguments); + if (v == null) this.removeAttribute(name);else this.setAttribute(name, v); + }; + } + + function attrFunctionNS$1(fullname, value) { + return function () { + var v = value.apply(this, arguments); + if (v == null) this.removeAttributeNS(fullname.space, fullname.local);else this.setAttributeNS(fullname.space, fullname.local, v); + }; + } + + function selection_attr (name, value) { + var fullname = namespace(name); + + if (arguments.length < 2) { + var node = this.node(); + return fullname.local ? node.getAttributeNS(fullname.space, fullname.local) : node.getAttribute(fullname); + } + + return this.each((value == null ? fullname.local ? attrRemoveNS$1 : attrRemove$1 : typeof value === "function" ? fullname.local ? attrFunctionNS$1 : attrFunction$1 : fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, value)); + } + + function defaultView (node) { + return node.ownerDocument && node.ownerDocument.defaultView || // node is a Node + node.document && node // node is a Window + || node.defaultView; // node is a Document + } + + function styleRemove$1(name) { + return function () { + this.style.removeProperty(name); + }; + } + + function styleConstant$1(name, value, priority) { + return function () { + this.style.setProperty(name, value, priority); + }; + } + + function styleFunction$1(name, value, priority) { + return function () { + var v = value.apply(this, arguments); + if (v == null) this.style.removeProperty(name);else this.style.setProperty(name, v, priority); + }; + } + + function selection_style (name, value, priority) { + return arguments.length > 1 ? this.each((value == null ? styleRemove$1 : typeof value === "function" ? styleFunction$1 : styleConstant$1)(name, value, priority == null ? "" : priority)) : styleValue(this.node(), name); + } + function styleValue(node, name) { + return node.style.getPropertyValue(name) || defaultView(node).getComputedStyle(node, null).getPropertyValue(name); + } + + function propertyRemove(name) { + return function () { + delete this[name]; + }; + } + + function propertyConstant(name, value) { + return function () { + this[name] = value; + }; + } + + function propertyFunction(name, value) { + return function () { + var v = value.apply(this, arguments); + if (v == null) delete this[name];else this[name] = v; + }; + } + + function selection_property (name, value) { + return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name, value)) : this.node()[name]; + } + + function classArray(string) { + return string.trim().split(/^|\s+/); + } + + function classList(node) { + return node.classList || new ClassList(node); + } + + function ClassList(node) { + this._node = node; + this._names = classArray(node.getAttribute("class") || ""); + } + + ClassList.prototype = { + add: function add(name) { + var i = this._names.indexOf(name); + + if (i < 0) { + this._names.push(name); + + this._node.setAttribute("class", this._names.join(" ")); + } + }, + remove: function remove(name) { + var i = this._names.indexOf(name); + + if (i >= 0) { + this._names.splice(i, 1); + + this._node.setAttribute("class", this._names.join(" ")); + } + }, + contains: function contains(name) { + return this._names.indexOf(name) >= 0; + } + }; + + function classedAdd(node, names) { + var list = classList(node), + i = -1, + n = names.length; + + while (++i < n) { + list.add(names[i]); + } + } + + function classedRemove(node, names) { + var list = classList(node), + i = -1, + n = names.length; + + while (++i < n) { + list.remove(names[i]); + } + } + + function classedTrue(names) { + return function () { + classedAdd(this, names); + }; + } + + function classedFalse(names) { + return function () { + classedRemove(this, names); + }; + } + + function classedFunction(names, value) { + return function () { + (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names); + }; + } + + function selection_classed (name, value) { + var names = classArray(name + ""); + + if (arguments.length < 2) { + var list = classList(this.node()), + i = -1, + n = names.length; + + while (++i < n) { + if (!list.contains(names[i])) return false; + } + + return true; + } + + return this.each((typeof value === "function" ? classedFunction : value ? classedTrue : classedFalse)(names, value)); + } + + function textRemove() { + this.textContent = ""; + } + + function textConstant$1(value) { + return function () { + this.textContent = value; + }; + } + + function textFunction$1(value) { + return function () { + var v = value.apply(this, arguments); + this.textContent = v == null ? "" : v; + }; + } + + function selection_text (value) { + return arguments.length ? this.each(value == null ? textRemove : (typeof value === "function" ? textFunction$1 : textConstant$1)(value)) : this.node().textContent; + } + + function htmlRemove() { + this.innerHTML = ""; + } + + function htmlConstant(value) { + return function () { + this.innerHTML = value; + }; + } + + function htmlFunction(value) { + return function () { + var v = value.apply(this, arguments); + this.innerHTML = v == null ? "" : v; + }; + } + + function selection_html (value) { + return arguments.length ? this.each(value == null ? htmlRemove : (typeof value === "function" ? htmlFunction : htmlConstant)(value)) : this.node().innerHTML; + } + + function raise() { + if (this.nextSibling) this.parentNode.appendChild(this); + } + + function selection_raise () { + return this.each(raise); + } + + function lower() { + if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild); + } + + function selection_lower () { + return this.each(lower); + } + + function selection_append (name) { + var create = typeof name === "function" ? name : creator(name); + return this.select(function () { + return this.appendChild(create.apply(this, arguments)); + }); + } + + function constantNull() { + return null; + } + + function selection_insert (name, before) { + var create = typeof name === "function" ? name : creator(name), + select = before == null ? constantNull : typeof before === "function" ? before : selector(before); + return this.select(function () { + return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null); + }); + } + + function remove$7() { + var parent = this.parentNode; + if (parent) parent.removeChild(this); + } + + function selection_remove () { + return this.each(remove$7); + } + + function selection_cloneShallow() { + var clone = this.cloneNode(false), + parent = this.parentNode; + return parent ? parent.insertBefore(clone, this.nextSibling) : clone; + } + + function selection_cloneDeep() { + var clone = this.cloneNode(true), + parent = this.parentNode; + return parent ? parent.insertBefore(clone, this.nextSibling) : clone; + } + + function selection_clone (deep) { + return this.select(deep ? selection_cloneDeep : selection_cloneShallow); + } + + function selection_datum (value) { + return arguments.length ? this.property("__data__", value) : this.node().__data__; + } + + function contextListener(listener) { + return function (event) { + listener.call(this, event, this.__data__); + }; + } + + function parseTypenames(typenames) { + return typenames.trim().split(/^|\s+/).map(function (t) { + var name = "", + i = t.indexOf("."); + if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); + return { + type: t, + name: name + }; + }); + } + + function onRemove(typename) { + return function () { + var on = this.__on; + if (!on) return; + + for (var j = 0, i = -1, m = on.length, o; j < m; ++j) { + if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) { + this.removeEventListener(o.type, o.listener, o.options); + } else { + on[++i] = o; + } + } + + if (++i) on.length = i;else delete this.__on; + }; + } + + function onAdd(typename, value, options) { + return function () { + var on = this.__on, + o, + listener = contextListener(value); + if (on) for (var j = 0, m = on.length; j < m; ++j) { + if ((o = on[j]).type === typename.type && o.name === typename.name) { + this.removeEventListener(o.type, o.listener, o.options); + this.addEventListener(o.type, o.listener = listener, o.options = options); + o.value = value; + return; + } + } + this.addEventListener(typename.type, listener, options); + o = { + type: typename.type, + name: typename.name, + value: value, + listener: listener, + options: options + }; + if (!on) this.__on = [o];else on.push(o); + }; + } + + function selection_on (typename, value, options) { + var typenames = parseTypenames(typename + ""), + i, + n = typenames.length, + t; + + if (arguments.length < 2) { + var on = this.node().__on; + + if (on) for (var j = 0, m = on.length, o; j < m; ++j) { + for (i = 0, o = on[j]; i < n; ++i) { + if ((t = typenames[i]).type === o.type && t.name === o.name) { + return o.value; + } + } + } + return; + } + + on = value ? onAdd : onRemove; + + for (i = 0; i < n; ++i) { + this.each(on(typenames[i], value, options)); + } + + return this; + } + + function dispatchEvent(node, type, params) { + var window = defaultView(node), + event = window.CustomEvent; + + if (typeof event === "function") { + event = new event(type, params); + } else { + event = window.document.createEvent("Event"); + if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;else event.initEvent(type, false, false); + } + + node.dispatchEvent(event); + } + + function dispatchConstant(type, params) { + return function () { + return dispatchEvent(this, type, params); + }; + } + + function dispatchFunction(type, params) { + return function () { + return dispatchEvent(this, type, params.apply(this, arguments)); + }; + } + + function selection_dispatch (type, params) { + return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type, params)); + } + + var _marked$1 = /*#__PURE__*/regeneratorRuntime.mark(_callee); + + function _callee() { + var groups, j, m, group, i, n, node; + return regeneratorRuntime.wrap(function _callee$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + groups = this._groups, j = 0, m = groups.length; + + case 1: + if (!(j < m)) { + _context.next = 13; + break; + } + + group = groups[j], i = 0, n = group.length; + + case 3: + if (!(i < n)) { + _context.next = 10; + break; + } + + if (!(node = group[i])) { + _context.next = 7; + break; + } + + _context.next = 7; + return node; + + case 7: + ++i; + _context.next = 3; + break; + + case 10: + ++j; + _context.next = 1; + break; + + case 13: + case "end": + return _context.stop(); + } + } + }, _marked$1, this); + } + + var root$1 = [null]; + function Selection$1(groups, parents) { + this._groups = groups; + this._parents = parents; + } + + function selection() { + return new Selection$1([[document.documentElement]], root$1); + } + + function selection_selection() { + return this; + } + + Selection$1.prototype = selection.prototype = _defineProperty({ + constructor: Selection$1, + select: selection_select, + selectAll: selection_selectAll, + selectChild: selection_selectChild, + selectChildren: selection_selectChildren, + filter: selection_filter, + data: selection_data, + enter: selection_enter, + exit: selection_exit, + join: selection_join, + merge: selection_merge, + selection: selection_selection, + order: selection_order, + sort: selection_sort, + call: selection_call, + nodes: selection_nodes, + node: selection_node, + size: selection_size, + empty: selection_empty, + each: selection_each, + attr: selection_attr, + style: selection_style, + property: selection_property, + classed: selection_classed, + text: selection_text, + html: selection_html, + raise: selection_raise, + lower: selection_lower, + append: selection_append, + insert: selection_insert, + remove: selection_remove, + clone: selection_clone, + datum: selection_datum, + on: selection_on, + dispatch: selection_dispatch + }, Symbol.iterator, _callee); + + function select (selector) { + return typeof selector === "string" ? new Selection$1([[document.querySelector(selector)]], [document.documentElement]) : new Selection$1([[selector]], root$1); + } + + function sourceEvent (event) { + var sourceEvent; + + while (sourceEvent = event.sourceEvent) { + event = sourceEvent; + } + + return event; + } + + function pointer (event, node) { + event = sourceEvent(event); + if (node === undefined) node = event.currentTarget; + + if (node) { + var svg = node.ownerSVGElement || node; + + if (svg.createSVGPoint) { + var point = svg.createSVGPoint(); + point.x = event.clientX, point.y = event.clientY; + point = point.matrixTransform(node.getScreenCTM().inverse()); + return [point.x, point.y]; + } + + if (node.getBoundingClientRect) { + var rect = node.getBoundingClientRect(); + return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop]; + } + } + + return [event.pageX, event.pageY]; + } + + function selectAll (selector) { + return typeof selector === "string" ? new Selection$1([document.querySelectorAll(selector)], [document.documentElement]) : new Selection$1([selector == null ? [] : array(selector)], root$1); + } + + function nopropagation$1(event) { + event.stopImmediatePropagation(); + } + function noevent$1 (event) { + event.preventDefault(); + event.stopImmediatePropagation(); + } + + function dragDisable (view) { + var root = view.document.documentElement, + selection = select(view).on("dragstart.drag", noevent$1, true); + + if ("onselectstart" in root) { + selection.on("selectstart.drag", noevent$1, true); + } else { + root.__noselect = root.style.MozUserSelect; + root.style.MozUserSelect = "none"; + } + } + function yesdrag(view, noclick) { + var root = view.document.documentElement, + selection = select(view).on("dragstart.drag", null); + + if (noclick) { + selection.on("click.drag", noevent$1, true); + setTimeout(function () { + selection.on("click.drag", null); + }, 0); + } + + if ("onselectstart" in root) { + selection.on("selectstart.drag", null); + } else { + root.style.MozUserSelect = root.__noselect; + delete root.__noselect; + } + } + + var constant$2 = (function (x) { + return function () { + return x; + }; + }); + + function DragEvent(type, _ref) { + var sourceEvent = _ref.sourceEvent, + subject = _ref.subject, + target = _ref.target, + identifier = _ref.identifier, + active = _ref.active, + x = _ref.x, + y = _ref.y, + dx = _ref.dx, + dy = _ref.dy, + dispatch = _ref.dispatch; + Object.defineProperties(this, { + type: { + value: type, + enumerable: true, + configurable: true + }, + sourceEvent: { + value: sourceEvent, + enumerable: true, + configurable: true + }, + subject: { + value: subject, + enumerable: true, + configurable: true + }, + target: { + value: target, + enumerable: true, + configurable: true + }, + identifier: { + value: identifier, + enumerable: true, + configurable: true + }, + active: { + value: active, + enumerable: true, + configurable: true + }, + x: { + value: x, + enumerable: true, + configurable: true + }, + y: { + value: y, + enumerable: true, + configurable: true + }, + dx: { + value: dx, + enumerable: true, + configurable: true + }, + dy: { + value: dy, + enumerable: true, + configurable: true + }, + _: { + value: dispatch + } + }); + } + + DragEvent.prototype.on = function () { + var value = this._.on.apply(this._, arguments); + + return value === this._ ? this : value; + }; + + function defaultFilter$2(event) { + return !event.ctrlKey && !event.button; + } + + function defaultContainer() { + return this.parentNode; + } + + function defaultSubject(event, d) { + return d == null ? { + x: event.x, + y: event.y + } : d; + } + + function defaultTouchable$1() { + return navigator.maxTouchPoints || "ontouchstart" in this; + } + + function d3_drag () { + var filter = defaultFilter$2, + container = defaultContainer, + subject = defaultSubject, + touchable = defaultTouchable$1, + gestures = {}, + listeners = dispatch$8("start", "drag", "end"), + active = 0, + mousedownx, + mousedowny, + mousemoving, + touchending, + clickDistance2 = 0; + + function drag(selection) { + selection.on("mousedown.drag", mousedowned).filter(touchable).on("touchstart.drag", touchstarted).on("touchmove.drag", touchmoved).on("touchend.drag touchcancel.drag", touchended).style("touch-action", "none").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); + } + + function mousedowned(event, d) { + if (touchending || !filter.call(this, event, d)) return; + var gesture = beforestart(this, container.call(this, event, d), event, d, "mouse"); + if (!gesture) return; + select(event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true); + dragDisable(event.view); + nopropagation$1(event); + mousemoving = false; + mousedownx = event.clientX; + mousedowny = event.clientY; + gesture("start", event); + } + + function mousemoved(event) { + noevent$1(event); + + if (!mousemoving) { + var dx = event.clientX - mousedownx, + dy = event.clientY - mousedowny; + mousemoving = dx * dx + dy * dy > clickDistance2; + } + + gestures.mouse("drag", event); + } + + function mouseupped(event) { + select(event.view).on("mousemove.drag mouseup.drag", null); + yesdrag(event.view, mousemoving); + noevent$1(event); + gestures.mouse("end", event); + } + + function touchstarted(event, d) { + if (!filter.call(this, event, d)) return; + var touches = event.changedTouches, + c = container.call(this, event, d), + n = touches.length, + i, + gesture; + + for (i = 0; i < n; ++i) { + if (gesture = beforestart(this, c, event, d, touches[i].identifier, touches[i])) { + nopropagation$1(event); + gesture("start", event, touches[i]); + } + } + } + + function touchmoved(event) { + var touches = event.changedTouches, + n = touches.length, + i, + gesture; + + for (i = 0; i < n; ++i) { + if (gesture = gestures[touches[i].identifier]) { + noevent$1(event); + gesture("drag", event, touches[i]); + } + } + } + + function touchended(event) { + var touches = event.changedTouches, + n = touches.length, + i, + gesture; + if (touchending) clearTimeout(touchending); + touchending = setTimeout(function () { + touchending = null; + }, 500); // Ghost clicks are delayed! + + for (i = 0; i < n; ++i) { + if (gesture = gestures[touches[i].identifier]) { + nopropagation$1(event); + gesture("end", event, touches[i]); + } + } + } + + function beforestart(that, container, event, d, identifier, touch) { + var dispatch = listeners.copy(), + p = pointer(touch || event, container), + dx, + dy, + s; + if ((s = subject.call(that, new DragEvent("beforestart", { + sourceEvent: event, + target: drag, + identifier: identifier, + active: active, + x: p[0], + y: p[1], + dx: 0, + dy: 0, + dispatch: dispatch + }), d)) == null) return; + dx = s.x - p[0] || 0; + dy = s.y - p[1] || 0; + return function gesture(type, event, touch) { + var p0 = p, + n; + + switch (type) { + case "start": + gestures[identifier] = gesture, n = active++; + break; + + case "end": + delete gestures[identifier], --active; + // nobreak + + case "drag": + p = pointer(touch || event, container), n = active; + break; + } + + dispatch.call(type, that, new DragEvent(type, { + sourceEvent: event, + subject: s, + target: drag, + identifier: identifier, + active: n, + x: p[0] + dx, + y: p[1] + dy, + dx: p[0] - p0[0], + dy: p[1] - p0[1], + dispatch: dispatch + }), d); + }; + } + + drag.filter = function (_) { + return arguments.length ? (filter = typeof _ === "function" ? _ : constant$2(!!_), drag) : filter; + }; + + drag.container = function (_) { + return arguments.length ? (container = typeof _ === "function" ? _ : constant$2(_), drag) : container; + }; + + drag.subject = function (_) { + return arguments.length ? (subject = typeof _ === "function" ? _ : constant$2(_), drag) : subject; + }; + + drag.touchable = function (_) { + return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$2(!!_), drag) : touchable; + }; + + drag.on = function () { + var value = listeners.on.apply(listeners, arguments); + return value === listeners ? drag : value; + }; + + drag.clickDistance = function (_) { + return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2); + }; + + return drag; + } + + var DESCRIPTORS$3 = descriptors; + var global$3 = global$F; + var isForced$1 = isForced_1; + var inheritIfRequired$1 = inheritIfRequired$4; + var createNonEnumerableProperty = createNonEnumerableProperty$e; + var defineProperty$1 = objectDefineProperty.f; + var getOwnPropertyNames$1 = objectGetOwnPropertyNames.f; + var isRegExp$1 = isRegexp; + var getFlags = regexpFlags$1; + var stickyHelpers = regexpStickyHelpers; + var redefine$2 = redefine$g.exports; + var fails$a = fails$N; + var has$1 = has$j; + var enforceInternalState = internalState.enforce; + var setSpecies = setSpecies$5; + var wellKnownSymbol$1 = wellKnownSymbol$s; + var UNSUPPORTED_DOT_ALL = regexpUnsupportedDotAll; + var UNSUPPORTED_NCG = regexpUnsupportedNcg; + + var MATCH$1 = wellKnownSymbol$1('match'); + var NativeRegExp = global$3.RegExp; + var RegExpPrototype = NativeRegExp.prototype; + // TODO: Use only propper RegExpIdentifierName + var IS_NCG = /^\?<[^\s\d!#%&*+<=>@^][^\s!#%&*+<=>@^]*>/; + var re1 = /a/g; + var re2 = /a/g; + + // "new" should create a new object, old webkit bug + var CORRECT_NEW = new NativeRegExp(re1) !== re1; + + var UNSUPPORTED_Y = stickyHelpers.UNSUPPORTED_Y; + + var BASE_FORCED = DESCRIPTORS$3 && + (!CORRECT_NEW || UNSUPPORTED_Y || UNSUPPORTED_DOT_ALL || UNSUPPORTED_NCG || fails$a(function () { + re2[MATCH$1] = false; + // RegExp constructor can alter flags and IsRegExp works correct with @@match + return NativeRegExp(re1) != re1 || NativeRegExp(re2) == re2 || NativeRegExp(re1, 'i') != '/a/i'; + })); + + var handleDotAll = function (string) { + var length = string.length; + var index = 0; + var result = ''; + var brackets = false; + var chr; + for (; index <= length; index++) { + chr = string.charAt(index); + if (chr === '\\') { + result += chr + string.charAt(++index); + continue; + } + if (!brackets && chr === '.') { + result += '[\\s\\S]'; + } else { + if (chr === '[') { + brackets = true; + } else if (chr === ']') { + brackets = false; + } result += chr; + } + } return result; + }; + + var handleNCG = function (string) { + var length = string.length; + var index = 0; + var result = ''; + var named = []; + var names = {}; + var brackets = false; + var ncg = false; + var groupid = 0; + var groupname = ''; + var chr; + for (; index <= length; index++) { + chr = string.charAt(index); + if (chr === '\\') { + chr = chr + string.charAt(++index); + } else if (chr === ']') { + brackets = false; + } else if (!brackets) switch (true) { + case chr === '[': + brackets = true; + break; + case chr === '(': + if (IS_NCG.test(string.slice(index + 1))) { + index += 2; + ncg = true; + } + result += chr; + groupid++; + continue; + case chr === '>' && ncg: + if (groupname === '' || has$1(names, groupname)) { + throw new SyntaxError('Invalid capture group name'); + } + names[groupname] = true; + named.push([groupname, groupid]); + ncg = false; + groupname = ''; + continue; + } + if (ncg) groupname += chr; + else result += chr; + } return [result, named]; + }; + + // `RegExp` constructor + // https://tc39.es/ecma262/#sec-regexp-constructor + if (isForced$1('RegExp', BASE_FORCED)) { + var RegExpWrapper = function RegExp(pattern, flags) { + var thisIsRegExp = this instanceof RegExpWrapper; + var patternIsRegExp = isRegExp$1(pattern); + var flagsAreUndefined = flags === undefined; + var groups = []; + var rawPattern, rawFlags, dotAll, sticky, handled, result, state; + + if (!thisIsRegExp && patternIsRegExp && pattern.constructor === RegExpWrapper && flagsAreUndefined) { + return pattern; + } + + if (CORRECT_NEW) { + if (patternIsRegExp && !flagsAreUndefined) pattern = pattern.source; + } else if (pattern instanceof RegExpWrapper) { + if (flagsAreUndefined) flags = getFlags.call(pattern); + pattern = pattern.source; + } + + pattern = pattern === undefined ? '' : String(pattern); + flags = flags === undefined ? '' : String(flags); + rawPattern = pattern; + + if (UNSUPPORTED_DOT_ALL && 'dotAll' in re1) { + dotAll = !!flags && flags.indexOf('s') > -1; + if (dotAll) flags = flags.replace(/s/g, ''); + } + + rawFlags = flags; + + if (UNSUPPORTED_Y && 'sticky' in re1) { + sticky = !!flags && flags.indexOf('y') > -1; + if (sticky) flags = flags.replace(/y/g, ''); + } + + if (UNSUPPORTED_NCG) { + handled = handleNCG(pattern); + pattern = handled[0]; + groups = handled[1]; + } + + result = inheritIfRequired$1( + CORRECT_NEW ? new NativeRegExp(pattern, flags) : NativeRegExp(pattern, flags), + thisIsRegExp ? this : RegExpPrototype, + RegExpWrapper + ); + + if (dotAll || sticky || groups.length) { + state = enforceInternalState(result); + if (dotAll) { + state.dotAll = true; + state.raw = RegExpWrapper(handleDotAll(pattern), rawFlags); + } + if (sticky) state.sticky = true; + if (groups.length) state.groups = groups; + } + + if (pattern !== rawPattern) try { + // fails in old engines, but we have no alternatives for unsupported regex syntax + createNonEnumerableProperty(result, 'source', rawPattern === '' ? '(?:)' : rawPattern); + } catch (error) { /* empty */ } + + return result; + }; + + var proxy = function (key) { + key in RegExpWrapper || defineProperty$1(RegExpWrapper, key, { + configurable: true, + get: function () { return NativeRegExp[key]; }, + set: function (it) { NativeRegExp[key] = it; } + }); + }; + + for (var keys$1 = getOwnPropertyNames$1(NativeRegExp), index$1 = 0; keys$1.length > index$1;) { + proxy(keys$1[index$1++]); + } + + RegExpPrototype.constructor = RegExpWrapper; + RegExpWrapper.prototype = RegExpPrototype; + redefine$2(global$3, 'RegExp', RegExpWrapper); + } + + // https://tc39.es/ecma262/#sec-get-regexp-@@species + setSpecies('RegExp'); + + function define (constructor, factory, prototype) { + constructor.prototype = factory.prototype = prototype; + prototype.constructor = constructor; + } + function extend$3(parent, definition) { + var prototype = Object.create(parent.prototype); + + for (var key in definition) { + prototype[key] = definition[key]; + } + + return prototype; + } + + function Color() {} + var _darker = 0.7; + + var _brighter = 1 / _darker; + var reI = "\\s*([+-]?\\d+)\\s*", + reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*", + reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*", + reHex = /^#([0-9a-f]{3,8})$/, + reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"), + reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"), + reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"), + reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"), + reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"), + reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$"); + var named = { + aliceblue: 0xf0f8ff, + antiquewhite: 0xfaebd7, + aqua: 0x00ffff, + aquamarine: 0x7fffd4, + azure: 0xf0ffff, + beige: 0xf5f5dc, + bisque: 0xffe4c4, + black: 0x000000, + blanchedalmond: 0xffebcd, + blue: 0x0000ff, + blueviolet: 0x8a2be2, + brown: 0xa52a2a, + burlywood: 0xdeb887, + cadetblue: 0x5f9ea0, + chartreuse: 0x7fff00, + chocolate: 0xd2691e, + coral: 0xff7f50, + cornflowerblue: 0x6495ed, + cornsilk: 0xfff8dc, + crimson: 0xdc143c, + cyan: 0x00ffff, + darkblue: 0x00008b, + darkcyan: 0x008b8b, + darkgoldenrod: 0xb8860b, + darkgray: 0xa9a9a9, + darkgreen: 0x006400, + darkgrey: 0xa9a9a9, + darkkhaki: 0xbdb76b, + darkmagenta: 0x8b008b, + darkolivegreen: 0x556b2f, + darkorange: 0xff8c00, + darkorchid: 0x9932cc, + darkred: 0x8b0000, + darksalmon: 0xe9967a, + darkseagreen: 0x8fbc8f, + darkslateblue: 0x483d8b, + darkslategray: 0x2f4f4f, + darkslategrey: 0x2f4f4f, + darkturquoise: 0x00ced1, + darkviolet: 0x9400d3, + deeppink: 0xff1493, + deepskyblue: 0x00bfff, + dimgray: 0x696969, + dimgrey: 0x696969, + dodgerblue: 0x1e90ff, + firebrick: 0xb22222, + floralwhite: 0xfffaf0, + forestgreen: 0x228b22, + fuchsia: 0xff00ff, + gainsboro: 0xdcdcdc, + ghostwhite: 0xf8f8ff, + gold: 0xffd700, + goldenrod: 0xdaa520, + gray: 0x808080, + green: 0x008000, + greenyellow: 0xadff2f, + grey: 0x808080, + honeydew: 0xf0fff0, + hotpink: 0xff69b4, + indianred: 0xcd5c5c, + indigo: 0x4b0082, + ivory: 0xfffff0, + khaki: 0xf0e68c, + lavender: 0xe6e6fa, + lavenderblush: 0xfff0f5, + lawngreen: 0x7cfc00, + lemonchiffon: 0xfffacd, + lightblue: 0xadd8e6, + lightcoral: 0xf08080, + lightcyan: 0xe0ffff, + lightgoldenrodyellow: 0xfafad2, + lightgray: 0xd3d3d3, + lightgreen: 0x90ee90, + lightgrey: 0xd3d3d3, + lightpink: 0xffb6c1, + lightsalmon: 0xffa07a, + lightseagreen: 0x20b2aa, + lightskyblue: 0x87cefa, + lightslategray: 0x778899, + lightslategrey: 0x778899, + lightsteelblue: 0xb0c4de, + lightyellow: 0xffffe0, + lime: 0x00ff00, + limegreen: 0x32cd32, + linen: 0xfaf0e6, + magenta: 0xff00ff, + maroon: 0x800000, + mediumaquamarine: 0x66cdaa, + mediumblue: 0x0000cd, + mediumorchid: 0xba55d3, + mediumpurple: 0x9370db, + mediumseagreen: 0x3cb371, + mediumslateblue: 0x7b68ee, + mediumspringgreen: 0x00fa9a, + mediumturquoise: 0x48d1cc, + mediumvioletred: 0xc71585, + midnightblue: 0x191970, + mintcream: 0xf5fffa, + mistyrose: 0xffe4e1, + moccasin: 0xffe4b5, + navajowhite: 0xffdead, + navy: 0x000080, + oldlace: 0xfdf5e6, + olive: 0x808000, + olivedrab: 0x6b8e23, + orange: 0xffa500, + orangered: 0xff4500, + orchid: 0xda70d6, + palegoldenrod: 0xeee8aa, + palegreen: 0x98fb98, + paleturquoise: 0xafeeee, + palevioletred: 0xdb7093, + papayawhip: 0xffefd5, + peachpuff: 0xffdab9, + peru: 0xcd853f, + pink: 0xffc0cb, + plum: 0xdda0dd, + powderblue: 0xb0e0e6, + purple: 0x800080, + rebeccapurple: 0x663399, + red: 0xff0000, + rosybrown: 0xbc8f8f, + royalblue: 0x4169e1, + saddlebrown: 0x8b4513, + salmon: 0xfa8072, + sandybrown: 0xf4a460, + seagreen: 0x2e8b57, + seashell: 0xfff5ee, + sienna: 0xa0522d, + silver: 0xc0c0c0, + skyblue: 0x87ceeb, + slateblue: 0x6a5acd, + slategray: 0x708090, + slategrey: 0x708090, + snow: 0xfffafa, + springgreen: 0x00ff7f, + steelblue: 0x4682b4, + tan: 0xd2b48c, + teal: 0x008080, + thistle: 0xd8bfd8, + tomato: 0xff6347, + turquoise: 0x40e0d0, + violet: 0xee82ee, + wheat: 0xf5deb3, + white: 0xffffff, + whitesmoke: 0xf5f5f5, + yellow: 0xffff00, + yellowgreen: 0x9acd32 + }; + define(Color, color, { + copy: function copy(channels) { + return Object.assign(new this.constructor(), this, channels); + }, + displayable: function displayable() { + return this.rgb().displayable(); + }, + hex: color_formatHex, + // Deprecated! Use color.formatHex. + formatHex: color_formatHex, + formatHsl: color_formatHsl, + formatRgb: color_formatRgb, + toString: color_formatRgb + }); + + function color_formatHex() { + return this.rgb().formatHex(); + } + + function color_formatHsl() { + return hslConvert(this).formatHsl(); + } + + function color_formatRgb() { + return this.rgb().formatRgb(); + } + + function color(format) { + var m, l; + format = (format + "").trim().toLowerCase(); + return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000 + : l === 3 ? new Rgb(m >> 8 & 0xf | m >> 4 & 0xf0, m >> 4 & 0xf | m & 0xf0, (m & 0xf) << 4 | m & 0xf, 1) // #f00 + : l === 8 ? rgba(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000 + : l === 4 ? rgba(m >> 12 & 0xf | m >> 8 & 0xf0, m >> 8 & 0xf | m >> 4 & 0xf0, m >> 4 & 0xf | m & 0xf0, ((m & 0xf) << 4 | m & 0xf) / 0xff) // #f000 + : null // invalid hex + ) : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0) + : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%) + : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1) + : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1) + : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%) + : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1) + : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins + : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) : null; + } + + function rgbn(n) { + return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1); + } + + function rgba(r, g, b, a) { + if (a <= 0) r = g = b = NaN; + return new Rgb(r, g, b, a); + } + + function rgbConvert(o) { + if (!(o instanceof Color)) o = color(o); + if (!o) return new Rgb(); + o = o.rgb(); + return new Rgb(o.r, o.g, o.b, o.opacity); + } + function rgb(r, g, b, opacity) { + return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity); + } + function Rgb(r, g, b, opacity) { + this.r = +r; + this.g = +g; + this.b = +b; + this.opacity = +opacity; + } + define(Rgb, rgb, extend$3(Color, { + brighter: function brighter(k) { + k = k == null ? _brighter : Math.pow(_brighter, k); + return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); + }, + darker: function darker(k) { + k = k == null ? _darker : Math.pow(_darker, k); + return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); + }, + rgb: function rgb() { + return this; + }, + displayable: function displayable() { + return -0.5 <= this.r && this.r < 255.5 && -0.5 <= this.g && this.g < 255.5 && -0.5 <= this.b && this.b < 255.5 && 0 <= this.opacity && this.opacity <= 1; + }, + hex: rgb_formatHex, + // Deprecated! Use color.formatHex. + formatHex: rgb_formatHex, + formatRgb: rgb_formatRgb, + toString: rgb_formatRgb + })); + + function rgb_formatHex() { + return "#" + hex$1(this.r) + hex$1(this.g) + hex$1(this.b); + } + + function rgb_formatRgb() { + var a = this.opacity; + a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a)); + return (a === 1 ? "rgb(" : "rgba(") + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", " + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", " + Math.max(0, Math.min(255, Math.round(this.b) || 0)) + (a === 1 ? ")" : ", " + a + ")"); + } + + function hex$1(value) { + value = Math.max(0, Math.min(255, Math.round(value) || 0)); + return (value < 16 ? "0" : "") + value.toString(16); + } + + function hsla(h, s, l, a) { + if (a <= 0) h = s = l = NaN;else if (l <= 0 || l >= 1) h = s = NaN;else if (s <= 0) h = NaN; + return new Hsl(h, s, l, a); + } + + function hslConvert(o) { + if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity); + if (!(o instanceof Color)) o = color(o); + if (!o) return new Hsl(); + if (o instanceof Hsl) return o; + o = o.rgb(); + var r = o.r / 255, + g = o.g / 255, + b = o.b / 255, + min = Math.min(r, g, b), + max = Math.max(r, g, b), + h = NaN, + s = max - min, + l = (max + min) / 2; + + if (s) { + if (r === max) h = (g - b) / s + (g < b) * 6;else if (g === max) h = (b - r) / s + 2;else h = (r - g) / s + 4; + s /= l < 0.5 ? max + min : 2 - max - min; + h *= 60; + } else { + s = l > 0 && l < 1 ? 0 : h; + } + + return new Hsl(h, s, l, o.opacity); + } + function hsl(h, s, l, opacity) { + return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity); + } + + function Hsl(h, s, l, opacity) { + this.h = +h; + this.s = +s; + this.l = +l; + this.opacity = +opacity; + } + + define(Hsl, hsl, extend$3(Color, { + brighter: function brighter(k) { + k = k == null ? _brighter : Math.pow(_brighter, k); + return new Hsl(this.h, this.s, this.l * k, this.opacity); + }, + darker: function darker(k) { + k = k == null ? _darker : Math.pow(_darker, k); + return new Hsl(this.h, this.s, this.l * k, this.opacity); + }, + rgb: function rgb() { + var h = this.h % 360 + (this.h < 0) * 360, + s = isNaN(h) || isNaN(this.s) ? 0 : this.s, + l = this.l, + m2 = l + (l < 0.5 ? l : 1 - l) * s, + m1 = 2 * l - m2; + return new Rgb(hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2), hsl2rgb(h, m1, m2), hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2), this.opacity); + }, + displayable: function displayable() { + return (0 <= this.s && this.s <= 1 || isNaN(this.s)) && 0 <= this.l && this.l <= 1 && 0 <= this.opacity && this.opacity <= 1; + }, + formatHsl: function formatHsl() { + var a = this.opacity; + a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a)); + return (a === 1 ? "hsl(" : "hsla(") + (this.h || 0) + ", " + (this.s || 0) * 100 + "%, " + (this.l || 0) * 100 + "%" + (a === 1 ? ")" : ", " + a + ")"); + } + })); + /* From FvD 13.37, CSS Color Module Level 3 */ + + function hsl2rgb(h, m1, m2) { + return (h < 60 ? m1 + (m2 - m1) * h / 60 : h < 180 ? m2 : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 : m1) * 255; + } + + var constant$1 = (function (x) { + return function () { + return x; + }; + }); + + function linear$2(a, d) { + return function (t) { + return a + t * d; + }; + } + + function exponential(a, b, y) { + return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function (t) { + return Math.pow(a + t * b, y); + }; + } + function gamma(y) { + return (y = +y) === 1 ? nogamma : function (a, b) { + return b - a ? exponential(a, b, y) : constant$1(isNaN(a) ? b : a); + }; + } + function nogamma(a, b) { + var d = b - a; + return d ? linear$2(a, d) : constant$1(isNaN(a) ? b : a); + } + + var d3_interpolateRgb = (function rgbGamma(y) { + var color = gamma(y); + + function rgb$1(start, end) { + var r = color((start = rgb(start)).r, (end = rgb(end)).r), + g = color(start.g, end.g), + b = color(start.b, end.b), + opacity = nogamma(start.opacity, end.opacity); + return function (t) { + start.r = r(t); + start.g = g(t); + start.b = b(t); + start.opacity = opacity(t); + return start + ""; + }; + } + + rgb$1.gamma = rgbGamma; + return rgb$1; + })(1); + + function numberArray (a, b) { + if (!b) b = []; + var n = a ? Math.min(b.length, a.length) : 0, + c = b.slice(), + i; + return function (t) { + for (i = 0; i < n; ++i) { + c[i] = a[i] * (1 - t) + b[i] * t; + } + + return c; + }; + } + function isNumberArray(x) { + return ArrayBuffer.isView(x) && !(x instanceof DataView); + } + + function genericArray(a, b) { + var nb = b ? b.length : 0, + na = a ? Math.min(nb, a.length) : 0, + x = new Array(na), + c = new Array(nb), + i; + + for (i = 0; i < na; ++i) { + x[i] = interpolate$1(a[i], b[i]); + } + + for (; i < nb; ++i) { + c[i] = b[i]; + } + + return function (t) { + for (i = 0; i < na; ++i) { + c[i] = x[i](t); + } + + return c; + }; + } + + function date (a, b) { + var d = new Date(); + return a = +a, b = +b, function (t) { + return d.setTime(a * (1 - t) + b * t), d; + }; + } + + function d3_interpolateNumber (a, b) { + return a = +a, b = +b, function (t) { + return a * (1 - t) + b * t; + }; + } + + function object (a, b) { + var i = {}, + c = {}, + k; + if (a === null || _typeof(a) !== "object") a = {}; + if (b === null || _typeof(b) !== "object") b = {}; + + for (k in b) { + if (k in a) { + i[k] = interpolate$1(a[k], b[k]); + } else { + c[k] = b[k]; + } + } + + return function (t) { + for (k in i) { + c[k] = i[k](t); + } + + return c; + }; + } + + var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, + reB = new RegExp(reA.source, "g"); + + function zero(b) { + return function () { + return b; + }; + } + + function one(b) { + return function (t) { + return b(t) + ""; + }; + } + + function interpolateString (a, b) { + var bi = reA.lastIndex = reB.lastIndex = 0, + // scan index for next number in b + am, + // current match in a + bm, + // current match in b + bs, + // string preceding current number in b, if any + i = -1, + // index in s + s = [], + // string constants and placeholders + q = []; // number interpolators + // Coerce inputs to strings. + + a = a + "", b = b + ""; // Interpolate pairs of numbers in a & b. + + while ((am = reA.exec(a)) && (bm = reB.exec(b))) { + if ((bs = bm.index) > bi) { + // a string precedes the next number in b + bs = b.slice(bi, bs); + if (s[i]) s[i] += bs; // coalesce with previous string + else s[++i] = bs; + } + + if ((am = am[0]) === (bm = bm[0])) { + // numbers in a & b match + if (s[i]) s[i] += bm; // coalesce with previous string + else s[++i] = bm; + } else { + // interpolate non-matching numbers + s[++i] = null; + q.push({ + i: i, + x: d3_interpolateNumber(am, bm) + }); + } + + bi = reB.lastIndex; + } // Add remains of b. + + + if (bi < b.length) { + bs = b.slice(bi); + if (s[i]) s[i] += bs; // coalesce with previous string + else s[++i] = bs; + } // Special optimization for only a single match. + // Otherwise, interpolate each of the numbers and rejoin the string. + + + return s.length < 2 ? q[0] ? one(q[0].x) : zero(b) : (b = q.length, function (t) { + for (var i = 0, o; i < b; ++i) { + s[(o = q[i]).i] = o.x(t); + } + + return s.join(""); + }); + } + + function interpolate$1 (a, b) { + var t = _typeof(b), + c; + + 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); + } + + function interpolateRound (a, b) { + return a = +a, b = +b, function (t) { + return Math.round(a * (1 - t) + b * t); + }; + } + + var degrees = 180 / Math.PI; + var identity$3 = { + translateX: 0, + translateY: 0, + rotate: 0, + skewX: 0, + scaleX: 1, + scaleY: 1 + }; + function decompose (a, b, c, d, e, f) { + var scaleX, scaleY, skewX; + if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX; + if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX; + if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY; + if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX; + return { + translateX: e, + translateY: f, + rotate: Math.atan2(b, a) * degrees, + skewX: Math.atan(skewX) * degrees, + scaleX: scaleX, + scaleY: scaleY + }; + } + + var svgNode; + /* eslint-disable no-undef */ + + function parseCss(value) { + var m = new (typeof DOMMatrix === "function" ? DOMMatrix : WebKitCSSMatrix)(value + ""); + return m.isIdentity ? identity$3 : decompose(m.a, m.b, m.c, m.d, m.e, m.f); + } + function parseSvg(value) { + if (value == null) return identity$3; + if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g"); + svgNode.setAttribute("transform", value); + if (!(value = svgNode.transform.baseVal.consolidate())) return identity$3; + value = value.matrix; + return decompose(value.a, value.b, value.c, value.d, value.e, value.f); + } + + function interpolateTransform(parse, pxComma, pxParen, degParen) { + function pop(s) { + return s.length ? s.pop() + " " : ""; + } + + function translate(xa, ya, xb, yb, s, q) { + if (xa !== xb || ya !== yb) { + var i = s.push("translate(", null, pxComma, null, pxParen); + q.push({ + i: i - 4, + x: d3_interpolateNumber(xa, xb) + }, { + i: i - 2, + x: d3_interpolateNumber(ya, yb) + }); + } else if (xb || yb) { + s.push("translate(" + xb + pxComma + yb + pxParen); + } + } + + function rotate(a, b, s, q) { + if (a !== b) { + if (a - b > 180) b += 360;else if (b - a > 180) a += 360; // shortest path + + q.push({ + i: s.push(pop(s) + "rotate(", null, degParen) - 2, + x: d3_interpolateNumber(a, b) + }); + } else if (b) { + s.push(pop(s) + "rotate(" + b + degParen); + } + } + + function skewX(a, b, s, q) { + if (a !== b) { + q.push({ + i: s.push(pop(s) + "skewX(", null, degParen) - 2, + x: d3_interpolateNumber(a, b) + }); + } else if (b) { + s.push(pop(s) + "skewX(" + b + degParen); + } + } + + function scale(xa, ya, xb, yb, s, q) { + if (xa !== xb || ya !== yb) { + var i = s.push(pop(s) + "scale(", null, ",", null, ")"); + q.push({ + i: i - 4, + x: d3_interpolateNumber(xa, xb) + }, { + i: i - 2, + x: d3_interpolateNumber(ya, yb) + }); + } else if (xb !== 1 || yb !== 1) { + s.push(pop(s) + "scale(" + xb + "," + yb + ")"); + } + } + + return function (a, b) { + var s = [], + // string constants and placeholders + q = []; // number interpolators + + a = parse(a), b = parse(b); + translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q); + rotate(a.rotate, b.rotate, s, q); + skewX(a.skewX, b.skewX, s, q); + scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q); + a = b = null; // gc + + return function (t) { + var i = -1, + n = q.length, + o; + + while (++i < n) { + s[(o = q[i]).i] = o.x(t); + } + + return s.join(""); + }; + }; + } + + var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)"); + var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")"); + + var epsilon2 = 1e-12; + + function cosh(x) { + return ((x = Math.exp(x)) + 1 / x) / 2; + } + + function sinh(x) { + return ((x = Math.exp(x)) - 1 / x) / 2; + } + + function tanh(x) { + return ((x = Math.exp(2 * x)) - 1) / (x + 1); + } + + var interpolateZoom = (function zoomRho(rho, rho2, rho4) { + // p0 = [ux0, uy0, w0] + // p1 = [ux1, uy1, w1] + function zoom(p0, p1) { + var ux0 = p0[0], + uy0 = p0[1], + w0 = p0[2], + ux1 = p1[0], + uy1 = p1[1], + w1 = p1[2], + dx = ux1 - ux0, + dy = uy1 - uy0, + d2 = dx * dx + dy * dy, + i, + S; // Special case for u0 ≅ u1. + + if (d2 < epsilon2) { + S = Math.log(w1 / w0) / rho; + + i = function i(t) { + return [ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(rho * t * S)]; + }; + } // General case. + else { + var d1 = Math.sqrt(d2), + b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1), + b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1), + r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), + r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1); + S = (r1 - r0) / rho; + + i = function i(t) { + var s = t * S, + coshr0 = cosh(r0), + u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0)); + return [ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / cosh(rho * s + r0)]; + }; + } + + i.duration = S * 1000 * rho / Math.SQRT2; + return i; + } + + zoom.rho = function (_) { + var _1 = Math.max(1e-3, +_), + _2 = _1 * _1, + _4 = _2 * _2; + + return zoomRho(_1, _2, _4); + }; + + return zoom; + })(Math.SQRT2, 2, 4); + + function d3_quantize (interpolator, n) { + var samples = new Array(n); + + for (var i = 0; i < n; ++i) { + samples[i] = interpolator(i / (n - 1)); + } + + return samples; + } + + var $$m = _export; + var bind$2 = functionBind; + + // `Function.prototype.bind` method + // https://tc39.es/ecma262/#sec-function.prototype.bind + $$m({ target: 'Function', proto: true }, { + bind: bind$2 + }); + + var frame = 0, + // is an animation frame pending? + timeout = 0, + // is a timeout pending? + interval = 0, + // are any timers active? + pokeDelay = 1000, + // how frequently we check for clock skew + taskHead, + taskTail, + clockLast = 0, + clockNow = 0, + clockSkew = 0, + clock = (typeof performance === "undefined" ? "undefined" : _typeof(performance)) === "object" && performance.now ? performance : Date, + setFrame = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function (f) { + setTimeout(f, 17); + }; + function now$1() { + return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew); + } + + function clearNow() { + clockNow = 0; + } + + function Timer() { + this._call = this._time = this._next = null; + } + Timer.prototype = timer.prototype = { + constructor: Timer, + restart: function restart(callback, delay, time) { + if (typeof callback !== "function") throw new TypeError("callback is not a function"); + time = (time == null ? now$1() : +time) + (delay == null ? 0 : +delay); + + if (!this._next && taskTail !== this) { + if (taskTail) taskTail._next = this;else taskHead = this; + taskTail = this; + } + + this._call = callback; + this._time = time; + sleep(); + }, + stop: function stop() { + if (this._call) { + this._call = null; + this._time = Infinity; + sleep(); + } + } + }; + function timer(callback, delay, time) { + var t = new Timer(); + t.restart(callback, delay, time); + return t; + } + function timerFlush() { + now$1(); // Get the current time, if not already set. + + ++frame; // Pretend we’ve set an alarm, if we haven’t already. + + var t = taskHead, + e; + + while (t) { + if ((e = clockNow - t._time) >= 0) t._call.call(null, e); + t = t._next; + } + + --frame; + } + + function wake() { + clockNow = (clockLast = clock.now()) + clockSkew; + frame = timeout = 0; + + try { + timerFlush(); + } finally { + frame = 0; + nap(); + clockNow = 0; + } + } + + function poke() { + var now = clock.now(), + delay = now - clockLast; + if (delay > pokeDelay) clockSkew -= delay, clockLast = now; + } + + function nap() { + var t0, + t1 = taskHead, + t2, + time = Infinity; + + while (t1) { + if (t1._call) { + if (time > t1._time) time = t1._time; + t0 = t1, t1 = t1._next; + } else { + t2 = t1._next, t1._next = null; + t1 = t0 ? t0._next = t2 : taskHead = t2; + } + } + + taskTail = t0; + sleep(time); + } + + function sleep(time) { + if (frame) return; // Soonest alarm already set, or will be. + + if (timeout) timeout = clearTimeout(timeout); + var delay = time - clockNow; // Strictly less than if we recomputed clockNow. + + if (delay > 24) { + if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew); + if (interval) interval = clearInterval(interval); + } else { + if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay); + frame = 1, setFrame(wake); + } + } + + function d3_timeout (callback, delay, time) { + var t = new Timer(); + delay = delay == null ? 0 : +delay; + t.restart(function (elapsed) { + t.stop(); + callback(elapsed + delay); + }, delay, time); + return t; + } + + var emptyOn = dispatch$8("start", "end", "cancel", "interrupt"); + var emptyTween = []; + var CREATED = 0; + var SCHEDULED = 1; + var STARTING = 2; + var STARTED = 3; + var RUNNING = 4; + var ENDING = 5; + var ENDED = 6; + function schedule (node, name, id, index, group, timing) { + var schedules = node.__transition; + if (!schedules) node.__transition = {};else if (id in schedules) return; + create$3(node, id, { + name: name, + index: index, + // For context during callback. + group: group, + // For context during callback. + on: emptyOn, + tween: emptyTween, + time: timing.time, + delay: timing.delay, + duration: timing.duration, + ease: timing.ease, + timer: null, + state: CREATED + }); + } + function init(node, id) { + var schedule = get$1(node, id); + if (schedule.state > CREATED) throw new Error("too late; already scheduled"); + return schedule; + } + function set(node, id) { + var schedule = get$1(node, id); + if (schedule.state > STARTED) throw new Error("too late; already running"); + return schedule; + } + function get$1(node, id) { + var schedule = node.__transition; + if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found"); + return schedule; + } + + function create$3(node, id, self) { + var schedules = node.__transition, + tween; // Initialize the self timer when the transition is created. + // Note the actual delay is not known until the first callback! + + schedules[id] = self; + self.timer = timer(schedule, 0, self.time); + + function schedule(elapsed) { + self.state = SCHEDULED; + self.timer.restart(start, self.delay, self.time); // If the elapsed delay is less than our first sleep, start immediately. + + if (self.delay <= elapsed) start(elapsed - self.delay); + } + + function start(elapsed) { + var i, j, n, o; // If the state is not SCHEDULED, then we previously errored on start. + + if (self.state !== SCHEDULED) return stop(); + + for (i in schedules) { + o = schedules[i]; + if (o.name !== self.name) continue; // While this element already has a starting transition during this frame, + // defer starting an interrupting transition until that transition has a + // chance to tick (and possibly end); see d3/d3-transition#54! + + if (o.state === STARTED) return d3_timeout(start); // Interrupt the active transition, if any. + + if (o.state === RUNNING) { + o.state = ENDED; + o.timer.stop(); + o.on.call("interrupt", node, node.__data__, o.index, o.group); + delete schedules[i]; + } // Cancel any pre-empted transitions. + else if (+i < id) { + o.state = ENDED; + o.timer.stop(); + o.on.call("cancel", node, node.__data__, o.index, o.group); + delete schedules[i]; + } + } // Defer the first tick to end of the current frame; see d3/d3#1576. + // Note the transition may be canceled after start and before the first tick! + // Note this must be scheduled before the start event; see d3/d3-transition#16! + // Assuming this is successful, subsequent callbacks go straight to tick. + + + d3_timeout(function () { + if (self.state === STARTED) { + self.state = RUNNING; + self.timer.restart(tick, self.delay, self.time); + tick(elapsed); + } + }); // Dispatch the start event. + // Note this must be done before the tween are initialized. + + self.state = STARTING; + self.on.call("start", node, node.__data__, self.index, self.group); + if (self.state !== STARTING) return; // interrupted + + self.state = STARTED; // Initialize the tween, deleting null tween. + + tween = new Array(n = self.tween.length); + + for (i = 0, j = -1; i < n; ++i) { + if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) { + tween[++j] = o; + } + } + + tween.length = j + 1; + } + + function tick(elapsed) { + var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1), + i = -1, + n = tween.length; + + while (++i < n) { + tween[i].call(node, t); + } // Dispatch the end event. + + + if (self.state === ENDING) { + self.on.call("end", node, node.__data__, self.index, self.group); + stop(); + } + } + + function stop() { + self.state = ENDED; + self.timer.stop(); + delete schedules[id]; + + for (var i in schedules) { + return; + } // eslint-disable-line no-unused-vars + + + delete node.__transition; + } + } + + function interrupt (node, name) { + var schedules = node.__transition, + schedule, + active, + empty = true, + i; + if (!schedules) return; + name = name == null ? null : name + ""; + + for (i in schedules) { + if ((schedule = schedules[i]).name !== name) { + empty = false; + continue; + } + + active = schedule.state > STARTING && schedule.state < ENDING; + schedule.state = ENDED; + schedule.timer.stop(); + schedule.on.call(active ? "interrupt" : "cancel", node, node.__data__, schedule.index, schedule.group); + delete schedules[i]; + } + + if (empty) delete node.__transition; + } + + function selection_interrupt (name) { + return this.each(function () { + interrupt(this, name); + }); + } + + function tweenRemove(id, name) { + var tween0, tween1; + return function () { + var schedule = set(this, id), + tween = schedule.tween; // If this node shared tween with the previous node, + // just assign the updated shared tween and we’re done! + // Otherwise, copy-on-write. + + if (tween !== tween0) { + tween1 = tween0 = tween; + + for (var i = 0, n = tween1.length; i < n; ++i) { + if (tween1[i].name === name) { + tween1 = tween1.slice(); + tween1.splice(i, 1); + break; + } + } + } + + schedule.tween = tween1; + }; + } + + function tweenFunction(id, name, value) { + var tween0, tween1; + if (typeof value !== "function") throw new Error(); + return function () { + var schedule = set(this, id), + tween = schedule.tween; // If this node shared tween with the previous node, + // just assign the updated shared tween and we’re done! + // Otherwise, copy-on-write. + + if (tween !== tween0) { + tween1 = (tween0 = tween).slice(); + + for (var t = { + name: name, + value: value + }, i = 0, n = tween1.length; i < n; ++i) { + if (tween1[i].name === name) { + tween1[i] = t; + break; + } + } + + if (i === n) tween1.push(t); + } + + schedule.tween = tween1; + }; + } + + function transition_tween (name, value) { + var id = this._id; + name += ""; + + if (arguments.length < 2) { + var tween = get$1(this.node(), id).tween; + + for (var i = 0, n = tween.length, t; i < n; ++i) { + if ((t = tween[i]).name === name) { + return t.value; + } + } + + return null; + } + + return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value)); + } + function tweenValue(transition, name, value) { + var id = transition._id; + transition.each(function () { + var schedule = set(this, id); + (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments); + }); + return function (node) { + return get$1(node, id).value[name]; + }; + } + + function interpolate (a, b) { + var c; + return (typeof b === "number" ? d3_interpolateNumber : b instanceof color ? d3_interpolateRgb : (c = color(b)) ? (b = c, d3_interpolateRgb) : interpolateString)(a, b); + } + + function attrRemove(name) { + return function () { + this.removeAttribute(name); + }; + } + + function attrRemoveNS(fullname) { + return function () { + this.removeAttributeNS(fullname.space, fullname.local); + }; + } + + function attrConstant(name, interpolate, value1) { + var string00, + string1 = value1 + "", + interpolate0; + return function () { + var string0 = this.getAttribute(name); + return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1); + }; + } + + function attrConstantNS(fullname, interpolate, value1) { + var string00, + string1 = value1 + "", + interpolate0; + return function () { + var string0 = this.getAttributeNS(fullname.space, fullname.local); + return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1); + }; + } + + function attrFunction(name, interpolate, value) { + var string00, string10, interpolate0; + return function () { + var string0, + value1 = value(this), + string1; + if (value1 == null) return void this.removeAttribute(name); + string0 = this.getAttribute(name); + string1 = value1 + ""; + return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1)); + }; + } + + function attrFunctionNS(fullname, interpolate, value) { + var string00, string10, interpolate0; + return function () { + var string0, + value1 = value(this), + string1; + if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local); + string0 = this.getAttributeNS(fullname.space, fullname.local); + string1 = value1 + ""; + return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1)); + }; + } + + function transition_attr (name, value) { + var fullname = namespace(name), + i = fullname === "transform" ? interpolateTransformSvg : interpolate; + 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)); + } + + function attrInterpolate(name, i) { + return function (t) { + this.setAttribute(name, i.call(this, t)); + }; + } + + function attrInterpolateNS(fullname, i) { + return function (t) { + this.setAttributeNS(fullname.space, fullname.local, i.call(this, t)); + }; + } + + function attrTweenNS(fullname, value) { + var t0, i0; + + function tween() { + var i = value.apply(this, arguments); + if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i); + return t0; + } + + tween._value = value; + return tween; + } + + function attrTween(name, value) { + var t0, i0; + + function tween() { + var i = value.apply(this, arguments); + if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i); + return t0; + } + + tween._value = value; + return tween; + } + + function transition_attrTween (name, value) { + var key = "attr." + name; + if (arguments.length < 2) return (key = this.tween(key)) && key._value; + if (value == null) return this.tween(key, null); + if (typeof value !== "function") throw new Error(); + var fullname = namespace(name); + return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value)); + } + + function delayFunction(id, value) { + return function () { + init(this, id).delay = +value.apply(this, arguments); + }; + } + + function delayConstant(id, value) { + return value = +value, function () { + init(this, id).delay = value; + }; + } + + function transition_delay (value) { + var id = this._id; + return arguments.length ? this.each((typeof value === "function" ? delayFunction : delayConstant)(id, value)) : get$1(this.node(), id).delay; + } + + function durationFunction(id, value) { + return function () { + set(this, id).duration = +value.apply(this, arguments); + }; + } + + function durationConstant(id, value) { + return value = +value, function () { + set(this, id).duration = value; + }; + } + + function transition_duration (value) { + var id = this._id; + return arguments.length ? this.each((typeof value === "function" ? durationFunction : durationConstant)(id, value)) : get$1(this.node(), id).duration; + } + + function easeConstant(id, value) { + if (typeof value !== "function") throw new Error(); + return function () { + set(this, id).ease = value; + }; + } + + function transition_ease (value) { + var id = this._id; + return arguments.length ? this.each(easeConstant(id, value)) : get$1(this.node(), id).ease; + } + + function easeVarying(id, value) { + return function () { + var v = value.apply(this, arguments); + if (typeof v !== "function") throw new Error(); + set(this, id).ease = v; + }; + } + + function transition_easeVarying (value) { + if (typeof value !== "function") throw new Error(); + return this.each(easeVarying(this._id, value)); + } + + function transition_filter (match) { + if (typeof match !== "function") match = matcher(match); + + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { + if ((node = group[i]) && match.call(node, node.__data__, i, group)) { + subgroup.push(node); + } + } + } + + return new Transition(subgroups, this._parents, this._name, this._id); + } + + function transition_merge (transition) { + if (transition._id !== this._id) throw new Error(); + + for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { + for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { + if (node = group0[i] || group1[i]) { + merge[i] = node; + } + } + } + + for (; j < m0; ++j) { + merges[j] = groups0[j]; + } + + return new Transition(merges, this._parents, this._name, this._id); + } + + function start(name) { + return (name + "").trim().split(/^|\s+/).every(function (t) { + var i = t.indexOf("."); + if (i >= 0) t = t.slice(0, i); + return !t || t === "start"; + }); + } + + function onFunction(id, name, listener) { + var on0, + on1, + sit = start(name) ? init : set; + return function () { + var schedule = sit(this, id), + on = schedule.on; // If this node shared a dispatch with the previous node, + // just assign the updated shared dispatch and we’re done! + // Otherwise, copy-on-write. + + if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener); + schedule.on = on1; + }; + } + + function transition_on (name, listener) { + var id = this._id; + return arguments.length < 2 ? get$1(this.node(), id).on.on(name) : this.each(onFunction(id, name, listener)); + } + + function removeFunction(id) { + return function () { + var parent = this.parentNode; + + for (var i in this.__transition) { + if (+i !== id) return; + } + + if (parent) parent.removeChild(this); + }; + } + + function transition_remove () { + return this.on("end.remove", removeFunction(this._id)); + } + + function transition_select (select) { + var name = this._name, + id = this._id; + if (typeof select !== "function") select = selector(select); + + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { + if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + subgroup[i] = subnode; + schedule(subgroup[i], name, id, i, subgroup, get$1(node, id)); + } + } + } + + return new Transition(subgroups, this._parents, name, id); + } + + function transition_selectAll (select) { + var name = this._name, + id = this._id; + if (typeof select !== "function") select = selectorAll(select); + + for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { + if (node = group[i]) { + for (var children = select.call(node, node.__data__, i, group), child, inherit = get$1(node, id), k = 0, l = children.length; k < l; ++k) { + if (child = children[k]) { + schedule(child, name, id, k, children, inherit); + } + } + + subgroups.push(children); + parents.push(node); + } + } + } + + return new Transition(subgroups, parents, name, id); + } + + var Selection = selection.prototype.constructor; + function transition_selection () { + return new Selection(this._groups, this._parents); + } + + function styleNull(name, interpolate) { + var string00, string10, interpolate0; + return function () { + var string0 = styleValue(this, name), + string1 = (this.style.removeProperty(name), styleValue(this, name)); + return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : interpolate0 = interpolate(string00 = string0, string10 = string1); + }; + } + + function styleRemove(name) { + return function () { + this.style.removeProperty(name); + }; + } + + function styleConstant(name, interpolate, value1) { + var string00, + string1 = value1 + "", + interpolate0; + return function () { + var string0 = styleValue(this, name); + return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1); + }; + } + + function styleFunction(name, interpolate, value) { + var string00, string10, interpolate0; + return function () { + var string0 = styleValue(this, name), + value1 = value(this), + string1 = value1 + ""; + if (value1 == null) string1 = value1 = (this.style.removeProperty(name), styleValue(this, name)); + return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1)); + }; + } + + function styleMaybeRemove(id, name) { + var on0, + on1, + listener0, + key = "style." + name, + event = "end." + key, + remove; + return function () { + var schedule = set(this, id), + on = schedule.on, + listener = schedule.value[key] == null ? remove || (remove = styleRemove(name)) : undefined; // If this node shared a dispatch with the previous node, + // just assign the updated shared dispatch and we’re done! + // Otherwise, copy-on-write. + + if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener); + schedule.on = on1; + }; + } + + function transition_style (name, value, priority) { + var i = (name += "") === "transform" ? interpolateTransformCss : interpolate; + 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); + } + + function styleInterpolate(name, i, priority) { + return function (t) { + this.style.setProperty(name, i.call(this, t), priority); + }; + } + + function styleTween(name, value, priority) { + var t, i0; + + function tween() { + var i = value.apply(this, arguments); + if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority); + return t; + } + + tween._value = value; + return tween; + } + + function transition_styleTween (name, value, priority) { + var key = "style." + (name += ""); + if (arguments.length < 2) return (key = this.tween(key)) && key._value; + if (value == null) return this.tween(key, null); + if (typeof value !== "function") throw new Error(); + return this.tween(key, styleTween(name, value, priority == null ? "" : priority)); + } + + function textConstant(value) { + return function () { + this.textContent = value; + }; + } + + function textFunction(value) { + return function () { + var value1 = value(this); + this.textContent = value1 == null ? "" : value1; + }; + } + + function transition_text (value) { + return this.tween("text", typeof value === "function" ? textFunction(tweenValue(this, "text", value)) : textConstant(value == null ? "" : value + "")); + } + + function textInterpolate(i) { + return function (t) { + this.textContent = i.call(this, t); + }; + } + + function textTween(value) { + var t0, i0; + + function tween() { + var i = value.apply(this, arguments); + if (i !== i0) t0 = (i0 = i) && textInterpolate(i); + return t0; + } + + tween._value = value; + return tween; + } + + function transition_textTween (value) { + var key = "text"; + if (arguments.length < 1) return (key = this.tween(key)) && key._value; + if (value == null) return this.tween(key, null); + if (typeof value !== "function") throw new Error(); + return this.tween(key, textTween(value)); + } + + function transition_transition () { + var name = this._name, + id0 = this._id, + id1 = newId(); + + for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { + if (node = group[i]) { + var inherit = get$1(node, id0); + schedule(node, name, id1, i, group, { + time: inherit.time + inherit.delay + inherit.duration, + delay: 0, + duration: inherit.duration, + ease: inherit.ease + }); + } + } + } + + return new Transition(groups, this._parents, name, id1); + } + + function transition_end () { + var on0, + on1, + that = this, + id = that._id, + size = that.size(); + return new Promise(function (resolve, reject) { + var cancel = { + value: reject + }, + end = { + value: function value() { + if (--size === 0) resolve(); + } + }; + that.each(function () { + var schedule = set(this, id), + on = schedule.on; // If this node shared a dispatch with the previous node, + // just assign the updated shared dispatch and we’re done! + // Otherwise, copy-on-write. + + if (on !== on0) { + on1 = (on0 = on).copy(); + + on1._.cancel.push(cancel); + + on1._.interrupt.push(cancel); + + on1._.end.push(end); + } + + schedule.on = on1; + }); // The selection was empty, resolve end immediately + + if (size === 0) resolve(); + }); + } + + var id = 0; + function Transition(groups, parents, name, id) { + this._groups = groups; + this._parents = parents; + this._name = name; + this._id = id; + } + function newId() { + return ++id; + } + var selection_prototype = selection.prototype; + Transition.prototype = _defineProperty({ + constructor: Transition, + select: transition_select, + selectAll: transition_selectAll, + filter: transition_filter, + merge: transition_merge, + selection: transition_selection, + transition: transition_transition, + call: selection_prototype.call, + nodes: selection_prototype.nodes, + node: selection_prototype.node, + size: selection_prototype.size, + empty: selection_prototype.empty, + each: selection_prototype.each, + on: transition_on, + attr: transition_attr, + attrTween: transition_attrTween, + style: transition_style, + styleTween: transition_styleTween, + text: transition_text, + textTween: transition_textTween, + remove: transition_remove, + tween: transition_tween, + delay: transition_delay, + duration: transition_duration, + ease: transition_ease, + easeVarying: transition_easeVarying, + end: transition_end + }, Symbol.iterator, selection_prototype[Symbol.iterator]); + + var linear$1 = function linear(t) { + return +t; + }; + + function cubicInOut(t) { + return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2; + } + + var defaultTiming = { + time: null, + // Set on use. + delay: 0, + duration: 250, + ease: cubicInOut + }; + + function inherit(node, id) { + var timing; + + while (!(timing = node.__transition) || !(timing = timing[id])) { + if (!(node = node.parentNode)) { + throw new Error("transition ".concat(id, " not found")); + } + } + + return timing; + } + + function selection_transition (name) { + var id, timing; + + if (name instanceof Transition) { + id = name._id, name = name._name; + } else { + id = newId(), (timing = defaultTiming).time = now$1(), name = name == null ? null : name + ""; + } + + for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { + if (node = group[i]) { + schedule(node, name, id, i, group, timing || inherit(node, id)); + } + } + } + + return new Transition(groups, this._parents, name, id); + } + + selection.prototype.interrupt = selection_interrupt; + selection.prototype.transition = selection_transition; + + var constant = (function (x) { + return function () { + return x; + }; + }); + + function ZoomEvent(type, _ref) { + var sourceEvent = _ref.sourceEvent, + target = _ref.target, + transform = _ref.transform, + dispatch = _ref.dispatch; + Object.defineProperties(this, { + type: { + value: type, + enumerable: true, + configurable: true + }, + sourceEvent: { + value: sourceEvent, + enumerable: true, + configurable: true + }, + target: { + value: target, + enumerable: true, + configurable: true + }, + transform: { + value: transform, + enumerable: true, + configurable: true + }, + _: { + value: dispatch + } + }); + } + + function Transform(k, x, y) { + this.k = k; + this.x = x; + this.y = y; + } + Transform.prototype = { + constructor: Transform, + scale: function scale(k) { + return k === 1 ? this : new Transform(this.k * k, this.x, this.y); + }, + translate: function translate(x, y) { + return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y); + }, + apply: function apply(point) { + return [point[0] * this.k + this.x, point[1] * this.k + this.y]; + }, + applyX: function applyX(x) { + return x * this.k + this.x; + }, + applyY: function applyY(y) { + return y * this.k + this.y; + }, + invert: function invert(location) { + return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k]; + }, + invertX: function invertX(x) { + return (x - this.x) / this.k; + }, + invertY: function invertY(y) { + return (y - this.y) / this.k; + }, + rescaleX: function rescaleX(x) { + return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x)); + }, + rescaleY: function rescaleY(y) { + return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y)); + }, + toString: function toString() { + return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")"; + } + }; + var identity$2 = new Transform(1, 0, 0); + + function nopropagation(event) { + event.stopImmediatePropagation(); + } + function noevent (event) { + event.preventDefault(); + event.stopImmediatePropagation(); + } + + // except for pinch-to-zoom, which is sent as a wheel+ctrlKey event + + function defaultFilter$1(event) { + return (!event.ctrlKey || event.type === 'wheel') && !event.button; + } + + function defaultExtent$1() { + var e = this; + + if (e instanceof SVGElement) { + e = e.ownerSVGElement || e; + + if (e.hasAttribute("viewBox")) { + e = e.viewBox.baseVal; + return [[e.x, e.y], [e.x + e.width, e.y + e.height]]; + } + + return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]]; + } + + return [[0, 0], [e.clientWidth, e.clientHeight]]; + } + + function defaultTransform() { + return this.__zoom || identity$2; + } + + function defaultWheelDelta$1(event) { + return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * (event.ctrlKey ? 10 : 1); + } + + function defaultTouchable() { + return navigator.maxTouchPoints || "ontouchstart" in this; + } + + function defaultConstrain$1(transform, extent, translateExtent) { + var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0], + dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0], + dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1], + dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1]; + return transform.translate(dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1), dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)); + } + + function d3_zoom () { + var filter = defaultFilter$1, + extent = defaultExtent$1, + constrain = defaultConstrain$1, + wheelDelta = defaultWheelDelta$1, + touchable = defaultTouchable, + scaleExtent = [0, Infinity], + translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], + duration = 250, + interpolate = interpolateZoom, + listeners = dispatch$8("start", "zoom", "end"), + touchstarting, + touchfirst, + touchending, + touchDelay = 500, + wheelDelay = 150, + clickDistance2 = 0, + tapDistance = 10; + + function zoom(selection) { + selection.property("__zoom", defaultTransform).on("wheel.zoom", wheeled).on("mousedown.zoom", mousedowned).on("dblclick.zoom", dblclicked).filter(touchable).on("touchstart.zoom", touchstarted).on("touchmove.zoom", touchmoved).on("touchend.zoom touchcancel.zoom", touchended).style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); + } + + zoom.transform = function (collection, transform, point, event) { + var selection = collection.selection ? collection.selection() : collection; + selection.property("__zoom", defaultTransform); + + if (collection !== selection) { + schedule(collection, transform, point, event); + } else { + selection.interrupt().each(function () { + gesture(this, arguments).event(event).start().zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform).end(); + }); + } + }; + + zoom.scaleBy = function (selection, k, p, event) { + zoom.scaleTo(selection, function () { + var k0 = this.__zoom.k, + k1 = typeof k === "function" ? k.apply(this, arguments) : k; + return k0 * k1; + }, p, event); + }; + + zoom.scaleTo = function (selection, k, p, event) { + zoom.transform(selection, function () { + var e = extent.apply(this, arguments), + t0 = this.__zoom, + p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p, + p1 = t0.invert(p0), + k1 = typeof k === "function" ? k.apply(this, arguments) : k; + return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent); + }, p, event); + }; + + zoom.translateBy = function (selection, x, y, event) { + zoom.transform(selection, function () { + return constrain(this.__zoom.translate(typeof x === "function" ? x.apply(this, arguments) : x, typeof y === "function" ? y.apply(this, arguments) : y), extent.apply(this, arguments), translateExtent); + }, null, event); + }; + + zoom.translateTo = function (selection, x, y, p, event) { + zoom.transform(selection, function () { + var e = extent.apply(this, arguments), + t = this.__zoom, + p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p; + return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate(typeof x === "function" ? -x.apply(this, arguments) : -x, typeof y === "function" ? -y.apply(this, arguments) : -y), e, translateExtent); + }, p, event); + }; + + function scale(transform, k) { + k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k)); + return k === transform.k ? transform : new Transform(k, transform.x, transform.y); + } + + function translate(transform, p0, p1) { + var x = p0[0] - p1[0] * transform.k, + y = p0[1] - p1[1] * transform.k; + return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y); + } + + function centroid(extent) { + return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2]; + } + + function schedule(transition, transform, point, event) { + transition.on("start.zoom", function () { + gesture(this, arguments).event(event).start(); + }).on("interrupt.zoom end.zoom", function () { + gesture(this, arguments).event(event).end(); + }).tween("zoom", function () { + var that = this, + args = arguments, + g = gesture(that, args).event(event), + e = extent.apply(that, args), + p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point, + w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), + a = that.__zoom, + b = typeof transform === "function" ? transform.apply(that, args) : transform, + i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k)); + return function (t) { + if (t === 1) t = b; // Avoid rounding error on end. + else { + var l = i(t), + k = w / l[2]; + t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); + } + g.zoom(null, t); + }; + }); + } + + function gesture(that, args, clean) { + return !clean && that.__zooming || new Gesture(that, args); + } + + function Gesture(that, args) { + this.that = that; + this.args = args; + this.active = 0; + this.sourceEvent = null; + this.extent = extent.apply(that, args); + this.taps = 0; + } + + Gesture.prototype = { + event: function event(_event) { + if (_event) this.sourceEvent = _event; + return this; + }, + start: function start() { + if (++this.active === 1) { + this.that.__zooming = this; + this.emit("start"); + } + + return this; + }, + zoom: function zoom(key, transform) { + if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]); + if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]); + if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]); + this.that.__zoom = transform; + this.emit("zoom"); + return this; + }, + end: function end() { + if (--this.active === 0) { + delete this.that.__zooming; + this.emit("end"); + } + + return this; + }, + emit: function emit(type) { + var d = select(this.that).datum(); + listeners.call(type, this.that, new ZoomEvent(type, { + sourceEvent: this.sourceEvent, + target: zoom, + type: type, + transform: this.that.__zoom, + dispatch: listeners + }), d); + } + }; + + function wheeled(event) { + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + if (!filter.apply(this, arguments)) return; + var g = gesture(this, args).event(event), + t = this.__zoom, + k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))), + p = pointer(event); // If the mouse is in the same location as before, reuse it. + // If there were recent wheel events, reset the wheel idle timeout. + + if (g.wheel) { + if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) { + g.mouse[1] = t.invert(g.mouse[0] = p); + } + + clearTimeout(g.wheel); + } // If this wheel event won’t trigger a transform change, ignore it. + else if (t.k === k) return; // Otherwise, capture the mouse point and location at the start. + else { + g.mouse = [p, t.invert(p)]; + interrupt(this); + g.start(); + } + + noevent(event); + g.wheel = setTimeout(wheelidled, wheelDelay); + g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent)); + + function wheelidled() { + g.wheel = null; + g.end(); + } + } + + function mousedowned(event) { + for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + args[_key2 - 1] = arguments[_key2]; + } + + if (touchending || !filter.apply(this, arguments)) return; + var g = gesture(this, args, true).event(event), + v = select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true), + p = pointer(event, currentTarget), + currentTarget = event.currentTarget, + x0 = event.clientX, + y0 = event.clientY; + dragDisable(event.view); + nopropagation(event); + g.mouse = [p, this.__zoom.invert(p)]; + interrupt(this); + g.start(); + + function mousemoved(event) { + noevent(event); + + if (!g.moved) { + var dx = event.clientX - x0, + dy = event.clientY - y0; + g.moved = dx * dx + dy * dy > clickDistance2; + } + + g.event(event).zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = pointer(event, currentTarget), g.mouse[1]), g.extent, translateExtent)); + } + + function mouseupped(event) { + v.on("mousemove.zoom mouseup.zoom", null); + yesdrag(event.view, g.moved); + noevent(event); + g.event(event).end(); + } + } + + function dblclicked(event) { + for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { + args[_key3 - 1] = arguments[_key3]; + } + + if (!filter.apply(this, arguments)) return; + var t0 = this.__zoom, + p0 = pointer(event.changedTouches ? event.changedTouches[0] : event, this), + p1 = t0.invert(p0), + k1 = t0.k * (event.shiftKey ? 0.5 : 2), + t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, args), translateExtent); + noevent(event); + if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0, event);else select(this).call(zoom.transform, t1, p0, event); + } + + function touchstarted(event) { + for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { + args[_key4 - 1] = arguments[_key4]; + } + + if (!filter.apply(this, arguments)) return; + var touches = event.touches, + n = touches.length, + g = gesture(this, args, event.changedTouches.length === n).event(event), + started, + i, + t, + p; + nopropagation(event); + + for (i = 0; i < n; ++i) { + t = touches[i], p = pointer(t, this); + p = [p, this.__zoom.invert(p), t.identifier]; + if (!g.touch0) g.touch0 = p, started = true, g.taps = 1 + !!touchstarting;else if (!g.touch1 && g.touch0[2] !== p[2]) g.touch1 = p, g.taps = 0; + } + + if (touchstarting) touchstarting = clearTimeout(touchstarting); + + if (started) { + if (g.taps < 2) touchfirst = p[0], touchstarting = setTimeout(function () { + touchstarting = null; + }, touchDelay); + interrupt(this); + g.start(); + } + } + + function touchmoved(event) { + if (!this.__zooming) return; + + for (var _len5 = arguments.length, args = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) { + args[_key5 - 1] = arguments[_key5]; + } + + var g = gesture(this, args).event(event), + touches = event.changedTouches, + n = touches.length, + i, + t, + p, + l; + noevent(event); + + for (i = 0; i < n; ++i) { + t = touches[i], p = pointer(t, this); + if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p;else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p; + } + + t = g.that.__zoom; + + if (g.touch1) { + var p0 = g.touch0[0], + l0 = g.touch0[1], + p1 = g.touch1[0], + l1 = g.touch1[1], + dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp, + dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl; + t = scale(t, Math.sqrt(dp / dl)); + p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2]; + l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2]; + } else if (g.touch0) p = g.touch0[0], l = g.touch0[1];else return; + + g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent)); + } + + function touchended(event) { + for (var _len6 = arguments.length, args = new Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) { + args[_key6 - 1] = arguments[_key6]; + } + + if (!this.__zooming) return; + var g = gesture(this, args).event(event), + touches = event.changedTouches, + n = touches.length, + i, + t; + nopropagation(event); + if (touchending) clearTimeout(touchending); + touchending = setTimeout(function () { + touchending = null; + }, touchDelay); + + for (i = 0; i < n; ++i) { + t = touches[i]; + if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1; + } + + if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1; + if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);else { + g.end(); // If this was a dbltap, reroute to the (optional) dblclick.zoom handler. + + if (g.taps === 2) { + t = pointer(t, this); + + if (Math.hypot(touchfirst[0] - t[0], touchfirst[1] - t[1]) < tapDistance) { + var p = select(this).on("dblclick.zoom"); + if (p) p.apply(this, arguments); + } + } + } + } + + zoom.wheelDelta = function (_) { + return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant(+_), zoom) : wheelDelta; + }; + + zoom.filter = function (_) { + return arguments.length ? (filter = typeof _ === "function" ? _ : constant(!!_), zoom) : filter; + }; + + zoom.touchable = function (_) { + return arguments.length ? (touchable = typeof _ === "function" ? _ : constant(!!_), zoom) : touchable; + }; + + zoom.extent = function (_) { + return arguments.length ? (extent = typeof _ === "function" ? _ : constant([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent; + }; + + zoom.scaleExtent = function (_) { + return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]]; + }; + + zoom.translateExtent = function (_) { + return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]]; + }; + + zoom.constrain = function (_) { + return arguments.length ? (constrain = _, zoom) : constrain; + }; + + zoom.duration = function (_) { + return arguments.length ? (duration = +_, zoom) : duration; + }; + + zoom.interpolate = function (_) { + return arguments.length ? (interpolate = _, zoom) : interpolate; + }; + + zoom.on = function () { + var value = listeners.on.apply(listeners, arguments); + return value === listeners ? zoom : value; + }; + + zoom.clickDistance = function (_) { + return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2); + }; + + zoom.tapDistance = function (_) { + return arguments.length ? (tapDistance = +_, zoom) : tapDistance; + }; + + return zoom; + } + + /* + Bypasses features of D3's default projection stream pipeline that are unnecessary: + * Antimeridian clipping + * Spherical rotation + * Resampling + */ + + function geoRawMercator() { + var project = mercatorRaw; + var k = 512 / Math.PI; // scale + + var x = 0; + var y = 0; // translate + + var clipExtent = [[0, 0], [0, 0]]; + + function projection(point) { + point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180); + return [point[0] * k + x, y - point[1] * k]; + } + + projection.invert = function (point) { + point = project.invert((point[0] - x) / k, (y - point[1]) / k); + return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI]; + }; + + projection.scale = function (_) { + if (!arguments.length) return k; + k = +_; + return projection; + }; + + projection.translate = function (_) { + if (!arguments.length) return [x, y]; + x = +_[0]; + y = +_[1]; + return projection; + }; + + projection.clipExtent = function (_) { + if (!arguments.length) return clipExtent; + clipExtent = _; + return projection; + }; + + projection.transform = function (obj) { + if (!arguments.length) return identity$2.translate(x, y).scale(k); + x = +obj.x; + y = +obj.y; + k = +obj.k; + return projection; + }; + + projection.stream = d3_geoTransform({ + point: function point(x, y) { + var vec = projection([x, y]); + this.stream.point(vec[0], vec[1]); + } + }).stream; + return projection; + } + + function geoOrthoNormalizedDotProduct(a, b, origin) { + if (geoVecEqual(origin, a) || geoVecEqual(origin, b)) { + return 1; // coincident points, treat as straight and try to remove + } + + return geoVecNormalizedDot(a, b, origin); + } + + function geoOrthoFilterDotProduct(dotp, epsilon, lowerThreshold, upperThreshold, allowStraightAngles) { + var val = Math.abs(dotp); + + if (val < epsilon) { + return 0; // already orthogonal + } else if (allowStraightAngles && Math.abs(val - 1) < epsilon) { + return 0; // straight angle, which is okay in this case + } else if (val < lowerThreshold || val > upperThreshold) { + return dotp; // can be adjusted + } else { + return null; // ignore vertex + } + } + + function geoOrthoCalcScore(points, isClosed, epsilon, threshold) { + var score = 0; + var first = isClosed ? 0 : 1; + var last = isClosed ? points.length : points.length - 1; + var coords = points.map(function (p) { + return p.coord; + }); + var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180); + var upperThreshold = Math.cos(threshold * Math.PI / 180); + + for (var i = first; i < last; i++) { + var a = coords[(i - 1 + coords.length) % coords.length]; + var origin = coords[i]; + var b = coords[(i + 1) % coords.length]; + var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold); + if (dotp === null) continue; // ignore vertex + + score = score + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1))); + } + + return score; + } // returns the maximum angle less than `lessThan` between the actual corner and a 0° or 90° corner + + function geoOrthoMaxOffsetAngle(coords, isClosed, lessThan) { + var max = -Infinity; + var first = isClosed ? 0 : 1; + var last = isClosed ? coords.length : coords.length - 1; + + for (var i = first; i < last; i++) { + var a = coords[(i - 1 + coords.length) % coords.length]; + var origin = coords[i]; + var b = coords[(i + 1) % coords.length]; + var normalizedDotP = geoOrthoNormalizedDotProduct(a, b, origin); + var angle = Math.acos(Math.abs(normalizedDotP)) * 180 / Math.PI; + if (angle > 45) angle = 90 - angle; + if (angle >= lessThan) continue; + if (angle > max) max = angle; + } + + if (max === -Infinity) return null; + return max; + } // similar to geoOrthoCalcScore, but returns quickly if there is something to do + + function geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles) { + var score = null; + var first = isClosed ? 0 : 1; + var last = isClosed ? coords.length : coords.length - 1; + var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180); + var upperThreshold = Math.cos(threshold * Math.PI / 180); + + for (var i = first; i < last; i++) { + var a = coords[(i - 1 + coords.length) % coords.length]; + var origin = coords[i]; + var b = coords[(i + 1) % coords.length]; + var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold, allowStraightAngles); + if (dotp === null) continue; // ignore vertex + + if (Math.abs(dotp) > 0) return 1; // something to do + + score = 0; // already square + } + + return score; + } + + var $$l = _export; + var FREEZING = freezing; + var fails$9 = fails$N; + var isObject$4 = isObject$r; + var onFreeze = internalMetadata.exports.onFreeze; + + // eslint-disable-next-line es/no-object-freeze -- safe + var $freeze = Object.freeze; + var FAILS_ON_PRIMITIVES = fails$9(function () { $freeze(1); }); + + // `Object.freeze` method + // https://tc39.es/ecma262/#sec-object.freeze + $$l({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES, sham: !FREEZING }, { + freeze: function freeze(it) { + return $freeze && isObject$4(it) ? $freeze(onFreeze(it)) : it; + } + }); + + // Returns true if a and b have the same elements at the same indices. + function utilArrayIdentical(a, b) { + // an array is always identical to itself + if (a === b) return true; + var i = a.length; + if (i !== b.length) return false; + + while (i--) { + if (a[i] !== b[i]) return false; + } + + return true; + } // http://2ality.com/2015/01/es6-set-operations.html + // Difference (a \ b): create a set that contains those elements of set a that are not in set b. + // This operation is also sometimes called minus (-). + // var a = [1,2,3]; + // var b = [4,3,2]; + // utilArrayDifference(a, b) + // [1] + // utilArrayDifference(b, a) + // [4] + + function utilArrayDifference(a, b) { + var other = new Set(b); + return Array.from(new Set(a)).filter(function (v) { + return !other.has(v); + }); + } // Intersection (a ∩ b): create a set that contains those elements of set a that are also in set b. + // var a = [1,2,3]; + // var b = [4,3,2]; + // utilArrayIntersection(a, b) + // [2,3] + + function utilArrayIntersection(a, b) { + var other = new Set(b); + return Array.from(new Set(a)).filter(function (v) { + return other.has(v); + }); + } // Union (a ∪ b): create a set that contains the elements of both set a and set b. + // var a = [1,2,3]; + // var b = [4,3,2]; + // utilArrayUnion(a, b) + // [1,2,3,4] + + function utilArrayUnion(a, b) { + var result = new Set(a); + b.forEach(function (v) { + result.add(v); + }); + return Array.from(result); + } // Returns an Array with all the duplicates removed + // var a = [1,1,2,3,3]; + // utilArrayUniq(a) + // [1,2,3] + + function utilArrayUniq(a) { + return Array.from(new Set(a)); + } // Splits array into chunks of given chunk size + // var a = [1,2,3,4,5,6,7]; + // utilArrayChunk(a, 3); + // [[1,2,3],[4,5,6],[7]]; + + function utilArrayChunk(a, chunkSize) { + if (!chunkSize || chunkSize < 0) return [a.slice()]; + var result = new Array(Math.ceil(a.length / chunkSize)); + return Array.from(result, function (item, i) { + return a.slice(i * chunkSize, i * chunkSize + chunkSize); + }); + } // Flattens two level array into a single level + // var a = [[1,2,3],[4,5,6],[7]]; + // utilArrayFlatten(a); + // [1,2,3,4,5,6,7]; + + function utilArrayFlatten(a) { + return a.reduce(function (acc, val) { + return acc.concat(val); + }, []); + } // Groups the items of the Array according to the given key + // `key` can be passed as a property or as a key function + // + // var pets = [ + // { type: 'Dog', name: 'Spot' }, + // { type: 'Cat', name: 'Tiger' }, + // { type: 'Dog', name: 'Rover' }, + // { type: 'Cat', name: 'Leo' } + // ]; + // + // utilArrayGroupBy(pets, 'type') + // { + // 'Dog': [{type: 'Dog', name: 'Spot'}, {type: 'Dog', name: 'Rover'}], + // 'Cat': [{type: 'Cat', name: 'Tiger'}, {type: 'Cat', name: 'Leo'}] + // } + // + // utilArrayGroupBy(pets, function(item) { return item.name.length; }) + // { + // 3: [{type: 'Cat', name: 'Leo'}], + // 4: [{type: 'Dog', name: 'Spot'}], + // 5: [{type: 'Cat', name: 'Tiger'}, {type: 'Dog', name: 'Rover'}] + // } + + function utilArrayGroupBy(a, key) { + return a.reduce(function (acc, item) { + var group = typeof key === 'function' ? key(item) : item[key]; + (acc[group] = acc[group] || []).push(item); + return acc; + }, {}); + } // Returns an Array with all the duplicates removed + // where uniqueness determined by the given key + // `key` can be passed as a property or as a key function + // + // var pets = [ + // { type: 'Dog', name: 'Spot' }, + // { type: 'Cat', name: 'Tiger' }, + // { type: 'Dog', name: 'Rover' }, + // { type: 'Cat', name: 'Leo' } + // ]; + // + // utilArrayUniqBy(pets, 'type') + // [ + // { type: 'Dog', name: 'Spot' }, + // { type: 'Cat', name: 'Tiger' } + // ] + // + // utilArrayUniqBy(pets, function(item) { return item.name.length; }) + // [ + // { type: 'Dog', name: 'Spot' }, + // { type: 'Cat', name: 'Tiger' }, + // { type: 'Cat', name: 'Leo' } + // } + + function utilArrayUniqBy(a, key) { + var seen = new Set(); + return a.reduce(function (acc, item) { + var val = typeof key === 'function' ? key(item) : item[key]; + + if (val && !seen.has(val)) { + seen.add(val); + acc.push(item); + } + + return acc; + }, []); + } + + var DESCRIPTORS$2 = descriptors; + var global$2 = global$F; + var isForced = isForced_1; + var redefine$1 = redefine$g.exports; + var has = has$j; + var classof$1 = classofRaw$1; + var inheritIfRequired = inheritIfRequired$4; + var toPrimitive$1 = toPrimitive$7; + var fails$8 = fails$N; + var create$2 = objectCreate; + var getOwnPropertyNames = objectGetOwnPropertyNames.f; + var getOwnPropertyDescriptor$2 = objectGetOwnPropertyDescriptor.f; + var defineProperty = objectDefineProperty.f; + var trim$2 = stringTrim.trim; + + var NUMBER = 'Number'; + var NativeNumber = global$2[NUMBER]; + var NumberPrototype = NativeNumber.prototype; + + // Opera ~12 has broken Object#toString + var BROKEN_CLASSOF = classof$1(create$2(NumberPrototype)) == NUMBER; + + // `ToNumber` abstract operation + // https://tc39.es/ecma262/#sec-tonumber + var toNumber$1 = function (argument) { + var it = toPrimitive$1(argument, false); + var first, third, radix, maxCode, digits, length, index, code; + if (typeof it == 'string' && it.length > 2) { + it = trim$2(it); + first = it.charCodeAt(0); + if (first === 43 || first === 45) { + third = it.charCodeAt(2); + if (third === 88 || third === 120) return NaN; // Number('+0x1') should be NaN, old V8 fix + } else if (first === 48) { + switch (it.charCodeAt(1)) { + case 66: case 98: radix = 2; maxCode = 49; break; // fast equal of /^0b[01]+$/i + case 79: case 111: radix = 8; maxCode = 55; break; // fast equal of /^0o[0-7]+$/i + default: return +it; + } + digits = it.slice(2); + length = digits.length; + for (index = 0; index < length; index++) { + code = digits.charCodeAt(index); + // parseInt parses a string to a first unavailable symbol + // but ToNumber should return NaN if a string contains unavailable symbols + if (code < 48 || code > maxCode) return NaN; + } return parseInt(digits, radix); + } + } return +it; + }; + + // `Number` constructor + // https://tc39.es/ecma262/#sec-number-constructor + if (isForced(NUMBER, !NativeNumber(' 0o1') || !NativeNumber('0b1') || NativeNumber('+0x1'))) { + var NumberWrapper = function Number(value) { + var it = arguments.length < 1 ? 0 : value; + var dummy = this; + return dummy instanceof NumberWrapper + // check on 1..constructor(foo) case + && (BROKEN_CLASSOF ? fails$8(function () { NumberPrototype.valueOf.call(dummy); }) : classof$1(dummy) != NUMBER) + ? inheritIfRequired(new NativeNumber(toNumber$1(it)), dummy, NumberWrapper) : toNumber$1(it); + }; + for (var keys = DESCRIPTORS$2 ? getOwnPropertyNames(NativeNumber) : ( + // ES3: + 'MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,' + + // ES2015 (in case, if modules with ES2015 Number statics required before): + 'EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,' + + 'MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger,' + + // ESNext + 'fromString,range' + ).split(','), j$1 = 0, key; keys.length > j$1; j$1++) { + if (has(NativeNumber, key = keys[j$1]) && !has(NumberWrapper, key)) { + defineProperty(NumberWrapper, key, getOwnPropertyDescriptor$2(NativeNumber, key)); + } + } + NumberWrapper.prototype = NumberPrototype; + NumberPrototype.constructor = NumberWrapper; + redefine$1(global$2, NUMBER, NumberWrapper); + } + + var fixRegExpWellKnownSymbolLogic$1 = fixRegexpWellKnownSymbolLogic; + var anObject$1 = anObject$m; + var toLength$3 = toLength$q; + var requireObjectCoercible$7 = requireObjectCoercible$e; + var advanceStringIndex = advanceStringIndex$3; + var regExpExec$1 = regexpExecAbstract; + + // @@match logic + fixRegExpWellKnownSymbolLogic$1('match', function (MATCH, nativeMatch, maybeCallNative) { + return [ + // `String.prototype.match` method + // https://tc39.es/ecma262/#sec-string.prototype.match + function match(regexp) { + var O = requireObjectCoercible$7(this); + var matcher = regexp == undefined ? undefined : regexp[MATCH]; + return matcher !== undefined ? matcher.call(regexp, O) : new RegExp(regexp)[MATCH](String(O)); + }, + // `RegExp.prototype[@@match]` method + // https://tc39.es/ecma262/#sec-regexp.prototype-@@match + function (string) { + var res = maybeCallNative(nativeMatch, this, string); + if (res.done) return res.value; + + var rx = anObject$1(this); + var S = String(string); + + if (!rx.global) return regExpExec$1(rx, S); + + var fullUnicode = rx.unicode; + rx.lastIndex = 0; + var A = []; + var n = 0; + var result; + while ((result = regExpExec$1(rx, S)) !== null) { + var matchStr = String(result[0]); + A[n] = matchStr; + if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength$3(rx.lastIndex), fullUnicode); + n++; + } + return n === 0 ? null : A; + } + ]; + }); + + var diacritics = {}; + + var remove$6 = diacritics.remove = removeDiacritics; + var replacementList = [{ + base: ' ', + chars: "\xA0" + }, { + base: '0', + chars: "\u07C0" + }, { + base: 'A', + 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" + }, { + base: 'AA', + chars: "\uA732" + }, { + base: 'AE', + chars: "\xC6\u01FC\u01E2" + }, { + base: 'AO', + chars: "\uA734" + }, { + base: 'AU', + chars: "\uA736" + }, { + base: 'AV', + chars: "\uA738\uA73A" + }, { + base: 'AY', + chars: "\uA73C" + }, { + base: 'B', + chars: "\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0181" + }, { + base: 'C', + chars: "\u24B8\uFF23\uA73E\u1E08\u0106C\u0108\u010A\u010C\xC7\u0187\u023B" + }, { + base: 'D', + chars: "\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018A\u0189\u1D05\uA779" + }, { + base: 'Dh', + chars: "\xD0" + }, { + base: 'DZ', + chars: "\u01F1\u01C4" + }, { + base: 'Dz', + chars: "\u01F2\u01C5" + }, { + base: 'E', + 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" + }, { + base: 'F', + chars: "\uA77C\u24BB\uFF26\u1E1E\u0191\uA77B" + }, { + base: 'G', + chars: "\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E\u0262" + }, { + base: 'H', + chars: "\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D" + }, { + base: 'I', + chars: "\u24BE\uFF29\xCC\xCD\xCE\u0128\u012A\u012C\u0130\xCF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197" + }, { + base: 'J', + chars: "\u24BF\uFF2A\u0134\u0248\u0237" + }, { + base: 'K', + chars: "\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2" + }, { + base: 'L', + chars: "\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780" + }, { + base: 'LJ', + chars: "\u01C7" + }, { + base: 'Lj', + chars: "\u01C8" + }, { + base: 'M', + chars: "\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C\u03FB" + }, { + base: 'N', + chars: "\uA7A4\u0220\u24C3\uFF2E\u01F8\u0143\xD1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u019D\uA790\u1D0E" + }, { + base: 'NJ', + chars: "\u01CA" + }, { + base: 'Nj', + chars: "\u01CB" + }, { + base: 'O', + chars: "\u24C4\uFF2F\xD2\xD3\xD4\u1ED2\u1ED0\u1ED6\u1ED4\xD5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\xD6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\xD8\u01FE\u0186\u019F\uA74A\uA74C" + }, { + base: 'OE', + chars: "\u0152" + }, { + base: 'OI', + chars: "\u01A2" + }, { + base: 'OO', + chars: "\uA74E" + }, { + base: 'OU', + chars: "\u0222" + }, { + base: 'P', + chars: "\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754" + }, { + base: 'Q', + chars: "\u24C6\uFF31\uA756\uA758\u024A" + }, { + base: 'R', + chars: "\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782" + }, { + base: 'S', + chars: "\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784" + }, { + base: 'T', + chars: "\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786" + }, { + base: 'Th', + chars: "\xDE" + }, { + base: 'TZ', + chars: "\uA728" + }, { + base: 'U', + chars: "\u24CA\uFF35\xD9\xDA\xDB\u0168\u1E78\u016A\u1E7A\u016C\xDC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244" + }, { + base: 'V', + chars: "\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245" + }, { + base: 'VY', + chars: "\uA760" + }, { + base: 'W', + chars: "\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72" + }, { + base: 'X', + chars: "\u24CD\uFF38\u1E8A\u1E8C" + }, { + base: 'Y', + chars: "\u24CE\uFF39\u1EF2\xDD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE" + }, { + base: 'Z', + chars: "\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762" + }, { + base: 'a', + chars: "\u24D0\uFF41\u1E9A\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" + }, { + base: 'aa', + chars: "\uA733" + }, { + base: 'ae', + chars: "\xE6\u01FD\u01E3" + }, { + base: 'ao', + chars: "\uA735" + }, { + base: 'au', + chars: "\uA737" + }, { + base: 'av', + chars: "\uA739\uA73B" + }, { + base: 'ay', + chars: "\uA73D" + }, { + base: 'b', + chars: "\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253\u0182" + }, { + base: 'c', + chars: "\uFF43\u24D2\u0107\u0109\u010B\u010D\xE7\u1E09\u0188\u023C\uA73F\u2184" + }, { + base: 'd', + chars: "\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\u018B\u13E7\u0501\uA7AA" + }, { + base: 'dh', + chars: "\xF0" + }, { + base: 'dz', + chars: "\u01F3\u01C6" + }, { + base: 'e', + 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" + }, { + base: 'f', + chars: "\u24D5\uFF46\u1E1F\u0192" + }, { + base: 'ff', + chars: "\uFB00" + }, { + base: 'fi', + chars: "\uFB01" + }, { + base: 'fl', + chars: "\uFB02" + }, { + base: 'ffi', + chars: "\uFB03" + }, { + base: 'ffl', + chars: "\uFB04" + }, { + base: 'g', + chars: "\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\uA77F\u1D79" + }, { + base: 'h', + chars: "\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265" + }, { + base: 'hv', + chars: "\u0195" + }, { + base: 'i', + chars: "\u24D8\uFF49\xEC\xED\xEE\u0129\u012B\u012D\xEF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131" + }, { + base: 'j', + chars: "\u24D9\uFF4A\u0135\u01F0\u0249" + }, { + base: 'k', + chars: "\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3" + }, { + base: 'l', + chars: "\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747\u026D" + }, { + base: 'lj', + chars: "\u01C9" + }, { + base: 'm', + chars: "\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F" + }, { + base: 'n', + chars: "\u24DD\uFF4E\u01F9\u0144\xF1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5\u043B\u0509" + }, { + base: 'nj', + chars: "\u01CC" + }, { + base: 'o', + chars: "\u24DE\uFF4F\xF2\xF3\xF4\u1ED3\u1ED1\u1ED7\u1ED5\xF5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\xF6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\xF8\u01FF\uA74B\uA74D\u0275\u0254\u1D11" + }, { + base: 'oe', + chars: "\u0153" + }, { + base: 'oi', + chars: "\u01A3" + }, { + base: 'oo', + chars: "\uA74F" + }, { + base: 'ou', + chars: "\u0223" + }, { + base: 'p', + chars: "\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755\u03C1" + }, { + base: 'q', + chars: "\u24E0\uFF51\u024B\uA757\uA759" + }, { + base: 'r', + chars: "\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783" + }, { + base: 's', + chars: "\u24E2\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B\u0282" + }, { + base: 'ss', + chars: "\xDF" + }, { + base: 't', + chars: "\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787" + }, { + base: 'th', + chars: "\xFE" + }, { + base: 'tz', + chars: "\uA729" + }, { + base: 'u', + chars: "\u24E4\uFF55\xF9\xFA\xFB\u0169\u1E79\u016B\u1E7B\u016D\xFC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289" + }, { + base: 'v', + chars: "\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C" + }, { + base: 'vy', + chars: "\uA761" + }, { + base: 'w', + chars: "\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73" + }, { + base: 'x', + chars: "\u24E7\uFF58\u1E8B\u1E8D" + }, { + base: 'y', + chars: "\u24E8\uFF59\u1EF3\xFD\u0177\u1EF9\u0233\u1E8F\xFF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF" + }, { + base: 'z', + chars: "\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763" + }]; + var diacriticsMap = {}; + + for (var i$1 = 0; i$1 < replacementList.length; i$1 += 1) { + var chars = replacementList[i$1].chars; + + for (var j = 0; j < chars.length; j += 1) { + diacriticsMap[chars[j]] = replacementList[i$1].base; + } + } + + function removeDiacritics(str) { + return str.replace(/[^\u0000-\u007e]/g, function (c) { + return diacriticsMap[c] || c; + }); + } + + diacritics.replacementList = replacementList; + diacritics.diacriticsMap = diacriticsMap; + + var lib = {}; + + var isArabic$1 = {}; + + Object.defineProperty(isArabic$1, "__esModule", { + value: true + }); + 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 + ]; + + function isArabic(_char) { + if (_char.length > 1) { + // allow the newer chars? + throw new Error('isArabic works on only one-character strings'); + } + + var code = _char.charCodeAt(0); + + for (var i = 0; i < arabicBlocks.length; i++) { + var block = arabicBlocks[i]; + + if (code >= block[0] && code <= block[1]) { + return true; + } + } + + return false; + } + + isArabic$1.isArabic = isArabic; + + function isMath(_char2) { + if (_char2.length > 2) { + // allow the newer chars? + throw new Error('isMath works on only one-character strings'); + } + + var code = _char2.charCodeAt(0); + + return code >= 0x660 && code <= 0x66C || code >= 0x6F0 && code <= 0x6F9; + } + + isArabic$1.isMath = isMath; + + var GlyphSplitter$1 = {}; + + var reference = {}; + + var unicodeArabic = {}; + + Object.defineProperty(unicodeArabic, "__esModule", { + value: true + }); + var arabicReference = { + "alef": { + "normal": ["\u0627"], + "madda_above": { + "normal": ["\u0627\u0653", "\u0622"], + "isolated": "\uFE81", + "final": "\uFE82" + }, + "hamza_above": { + "normal": ["\u0627\u0654", "\u0623"], + "isolated": "\uFE83", + "final": "\uFE84" + }, + "hamza_below": { + "normal": ["\u0627\u0655", "\u0625"], + "isolated": "\uFE87", + "final": "\uFE88" + }, + "wasla": { + "normal": "\u0671", + "isolated": "\uFB50", + "final": "\uFB51" + }, + "wavy_hamza_above": ["\u0672"], + "wavy_hamza_below": ["\u0627\u065F", "\u0673"], + "high_hamza": ["\u0675", "\u0627\u0674"], + "indic_two_above": ["\u0773"], + "indic_three_above": ["\u0774"], + "fathatan": { + "normal": ["\u0627\u064B"], + "final": "\uFD3C", + "isolated": "\uFD3D" + }, + "isolated": "\uFE8D", + "final": "\uFE8E" + }, + "beh": { + "normal": ["\u0628"], + "dotless": ["\u066E"], + "three_dots_horizontally_below": ["\u0750"], + "dot_below_three_dots_above": ["\u0751"], + "three_dots_pointing_upwards_below": ["\u0752"], + "three_dots_pointing_upwards_below_two_dots_above": ["\u0753"], + "two_dots_below_dot_above": ["\u0754"], + "inverted_small_v_below": ["\u0755"], + "small_v": ["\u0756"], + "small_v_below": ["\u08A0"], + "hamza_above": ["\u08A1"], + "small_meem_above": ["\u08B6"], + "isolated": "\uFE8F", + "final": "\uFE90", + "initial": "\uFE91", + "medial": "\uFE92" + }, + "teh marbuta": { + "normal": ["\u0629"], + "isolated": "\uFE93", + "final": "\uFE94" + }, + "teh": { + "normal": ["\u062A"], + "ring": ["\u067C"], + "three_dots_above_downwards": ["\u067D"], + "small_teh_above": ["\u08B8"], + "isolated": "\uFE95", + "final": "\uFE96", + "initial": "\uFE97", + "medial": "\uFE98" + }, + "theh": { + "normal": ["\u062B"], + "isolated": "\uFE99", + "final": "\uFE9A", + "initial": "\uFE9B", + "medial": "\uFE9C" + }, + "jeem": { + "normal": ["\u062C"], + "two_dots_above": ["\u08A2"], + "isolated": "\uFE9D", + "final": "\uFE9E", + "initial": "\uFE9F", + "medial": "\uFEA0" + }, + "hah": { + "normal": ["\u062D"], + "hamza_above": ["\u0681"], + "two_dots_vertical_above": ["\u0682"], + "three_dots_above": ["\u0685"], + "two_dots_above": ["\u0757"], + "three_dots_pointing_upwards_below": ["\u0758"], + "small_tah_below": ["\u076E"], + "small_tah_two_dots": ["\u076F"], + "small_tah_above": ["\u0772"], + "indic_four_below": ["\u077C"], + "isolated": "\uFEA1", + "final": "\uFEA2", + "initial": "\uFEA3", + "medial": "\uFEA4" + }, + "khah": { + "normal": ["\u062E"], + "isolated": "\uFEA5", + "final": "\uFEA6", + "initial": "\uFEA7", + "medial": "\uFEA8" + }, + "dal": { + "normal": ["\u062F"], + "ring": ["\u0689"], + "dot_below": ["\u068A"], + "dot_below_small_tah": ["\u068B"], + "three_dots_above_downwards": ["\u068F"], + "four_dots_above": ["\u0690"], + "inverted_v": ["\u06EE"], + "two_dots_vertically_below_small_tah": ["\u0759"], + "inverted_small_v_below": ["\u075A"], + "three_dots_below": ["\u08AE"], + "isolated": "\uFEA9", + "final": "\uFEAA" + }, + "thal": { + "normal": ["\u0630"], + "isolated": "\uFEAB", + "final": "\uFEAC" + }, + "reh": { + "normal": ["\u0631"], + "small_v": ["\u0692"], + "ring": ["\u0693"], + "dot_below": ["\u0694"], + "small_v_below": ["\u0695"], + "dot_below_dot_above": ["\u0696"], + "two_dots_above": ["\u0697"], + "four_dots_above": ["\u0699"], + "inverted_v": ["\u06EF"], + "stroke": ["\u075B"], + "two_dots_vertically_above": ["\u076B"], + "hamza_above": ["\u076C"], + "small_tah_two_dots": ["\u0771"], + "loop": ["\u08AA"], + "small_noon_above": ["\u08B9"], + "isolated": "\uFEAD", + "final": "\uFEAE" + }, + "zain": { + "normal": ["\u0632"], + "inverted_v_above": ["\u08B2"], + "isolated": "\uFEAF", + "final": "\uFEB0" + }, + "seen": { + "normal": ["\u0633"], + "dot_below_dot_above": ["\u069A"], + "three_dots_below": ["\u069B"], + "three_dots_below_three_dots_above": ["\u069C"], + "four_dots_above": ["\u075C"], + "two_dots_vertically_above": ["\u076D"], + "small_tah_two_dots": ["\u0770"], + "indic_four_above": ["\u077D"], + "inverted_v": ["\u077E"], + "isolated": "\uFEB1", + "final": "\uFEB2", + "initial": "\uFEB3", + "medial": "\uFEB4" + }, + "sheen": { + "normal": ["\u0634"], + "dot_below": ["\u06FA"], + "isolated": "\uFEB5", + "final": "\uFEB6", + "initial": "\uFEB7", + "medial": "\uFEB8" + }, + "sad": { + "normal": ["\u0635"], + "two_dots_below": ["\u069D"], + "three_dots_above": ["\u069E"], + "three_dots_below": ["\u08AF"], + "isolated": "\uFEB9", + "final": "\uFEBA", + "initial": "\uFEBB", + "medial": "\uFEBC" + }, + "dad": { + "normal": ["\u0636"], + "dot_below": ["\u06FB"], + "isolated": "\uFEBD", + "final": "\uFEBE", + "initial": "\uFEBF", + "medial": "\uFEC0" + }, + "tah": { + "normal": ["\u0637"], + "three_dots_above": ["\u069F"], + "two_dots_above": ["\u08A3"], + "isolated": "\uFEC1", + "final": "\uFEC2", + "initial": "\uFEC3", + "medial": "\uFEC4" + }, + "zah": { + "normal": ["\u0638"], + "isolated": "\uFEC5", + "final": "\uFEC6", + "initial": "\uFEC7", + "medial": "\uFEC8" + }, + "ain": { + "normal": ["\u0639"], + "three_dots_above": ["\u06A0"], + "two_dots_above": ["\u075D"], + "three_dots_pointing_downwards_above": ["\u075E"], + "two_dots_vertically_above": ["\u075F"], + "three_dots_below": ["\u08B3"], + "isolated": "\uFEC9", + "final": "\uFECA", + "initial": "\uFECB", + "medial": "\uFECC" + }, + "ghain": { + "normal": ["\u063A"], + "dot_below": ["\u06FC"], + "isolated": "\uFECD", + "final": "\uFECE", + "initial": "\uFECF", + "medial": "\uFED0" + }, + "feh": { + "normal": ["\u0641"], + "dotless": ["\u06A1"], + "dot_moved_below": ["\u06A2"], + "dot_below": ["\u06A3"], + "three_dots_below": ["\u06A5"], + "two_dots_below": ["\u0760"], + "three_dots_pointing_upwards_below": ["\u0761"], + "dot_below_three_dots_above": ["\u08A4"], + "isolated": "\uFED1", + "final": "\uFED2", + "initial": "\uFED3", + "medial": "\uFED4" + }, + "qaf": { + "normal": ["\u0642"], + "dotless": ["\u066F"], + "dot_above": ["\u06A7"], + "three_dots_above": ["\u06A8"], + "dot_below": ["\u08A5"], + "isolated": "\uFED5", + "final": "\uFED6", + "initial": "\uFED7", + "medial": "\uFED8" + }, + "kaf": { + "normal": ["\u0643"], + "swash": ["\u06AA"], + "ring": ["\u06AB"], + "dot_above": ["\u06AC"], + "three_dots_below": ["\u06AE"], + "two_dots_above": ["\u077F"], + "dot_below": ["\u08B4"], + "isolated": "\uFED9", + "final": "\uFEDA", + "initial": "\uFEDB", + "medial": "\uFEDC" + }, + "lam": { + "normal": ["\u0644"], + "small_v": ["\u06B5"], + "dot_above": ["\u06B6"], + "three_dots_above": ["\u06B7"], + "three_dots_below": ["\u06B8"], + "bar": ["\u076A"], + "double_bar": ["\u08A6"], + "isolated": "\uFEDD", + "final": "\uFEDE", + "initial": "\uFEDF", + "medial": "\uFEE0" + }, + "meem": { + "normal": ["\u0645"], + "dot_above": ["\u0765"], + "dot_below": ["\u0766"], + "three_dots_above": ["\u08A7"], + "isolated": "\uFEE1", + "final": "\uFEE2", + "initial": "\uFEE3", + "medial": "\uFEE4" + }, + "noon": { + "normal": ["\u0646"], + "dot_below": ["\u06B9"], + "ring": ["\u06BC"], + "three_dots_above": ["\u06BD"], + "two_dots_below": ["\u0767"], + "small_tah": ["\u0768"], + "small_v": ["\u0769"], + "isolated": "\uFEE5", + "final": "\uFEE6", + "initial": "\uFEE7", + "medial": "\uFEE8" + }, + "heh": { + "normal": ["\u0647"], + "isolated": "\uFEE9", + "final": "\uFEEA", + "initial": "\uFEEB", + "medial": "\uFEEC" + }, + "waw": { + "normal": ["\u0648"], + "hamza_above": { + "normal": ["\u0624", "\u0648\u0654"], + "isolated": "\uFE85", + "final": "\uFE86" + }, + "high_hamza": ["\u0676", "\u0648\u0674"], + "ring": ["\u06C4"], + "two_dots_above": ["\u06CA"], + "dot_above": ["\u06CF"], + "indic_two_above": ["\u0778"], + "indic_three_above": ["\u0779"], + "dot_within": ["\u08AB"], + "isolated": "\uFEED", + "final": "\uFEEE" + }, + "alef_maksura": { + "normal": ["\u0649"], + "hamza_above": ["\u0626", "\u064A\u0654"], + "initial": "\uFBE8", + "medial": "\uFBE9", + "isolated": "\uFEEF", + "final": "\uFEF0" + }, + "yeh": { + "normal": ["\u064A"], + "hamza_above": { + "normal": ["\u0626", "\u0649\u0654"], + "isolated": "\uFE89", + "final": "\uFE8A", + "initial": "\uFE8B", + "medial": "\uFE8C" + }, + "two_dots_below_hamza_above": ["\u08A8"], + "high_hamza": ["\u0678", "\u064A\u0674"], + "tail": ["\u06CD"], + "small_v": ["\u06CE"], + "three_dots_below": ["\u06D1"], + "two_dots_below_dot_above": ["\u08A9"], + "two_dots_below_small_noon_above": ["\u08BA"], + "isolated": "\uFEF1", + "final": "\uFEF2", + "initial": "\uFEF3", + "medial": "\uFEF4" + }, + "tteh": { + "normal": ["\u0679"], + "isolated": "\uFB66", + "final": "\uFB67", + "initial": "\uFB68", + "medial": "\uFB69" + }, + "tteheh": { + "normal": ["\u067A"], + "isolated": "\uFB5E", + "final": "\uFB5F", + "initial": "\uFB60", + "medial": "\uFB61" + }, + "beeh": { + "normal": ["\u067B"], + "isolated": "\uFB52", + "final": "\uFB53", + "initial": "\uFB54", + "medial": "\uFB55" + }, + "peh": { + "normal": ["\u067E"], + "small_meem_above": ["\u08B7"], + "isolated": "\uFB56", + "final": "\uFB57", + "initial": "\uFB58", + "medial": "\uFB59" + }, + "teheh": { + "normal": ["\u067F"], + "isolated": "\uFB62", + "final": "\uFB63", + "initial": "\uFB64", + "medial": "\uFB65" + }, + "beheh": { + "normal": ["\u0680"], + "isolated": "\uFB5A", + "final": "\uFB5B", + "initial": "\uFB5C", + "medial": "\uFB5D" + }, + "nyeh": { + "normal": ["\u0683"], + "isolated": "\uFB76", + "final": "\uFB77", + "initial": "\uFB78", + "medial": "\uFB79" + }, + "dyeh": { + "normal": ["\u0684"], + "isolated": "\uFB72", + "final": "\uFB73", + "initial": "\uFB74", + "medial": "\uFB75" + }, + "tcheh": { + "normal": ["\u0686"], + "dot_above": ["\u06BF"], + "isolated": "\uFB7A", + "final": "\uFB7B", + "initial": "\uFB7C", + "medial": "\uFB7D" + }, + "tcheheh": { + "normal": ["\u0687"], + "isolated": "\uFB7E", + "final": "\uFB7F", + "initial": "\uFB80", + "medial": "\uFB81" + }, + "ddal": { + "normal": ["\u0688"], + "isolated": "\uFB88", + "final": "\uFB89" + }, + "dahal": { + "normal": ["\u068C"], + "isolated": "\uFB84", + "final": "\uFB85" + }, + "ddahal": { + "normal": ["\u068D"], + "isolated": "\uFB82", + "final": "\uFB83" + }, + "dul": { + "normal": ["\u068F", "\u068E"], + "isolated": "\uFB86", + "final": "\uFB87" + }, + "rreh": { + "normal": ["\u0691"], + "isolated": "\uFB8C", + "final": "\uFB8D" + }, + "jeh": { + "normal": ["\u0698"], + "isolated": "\uFB8A", + "final": "\uFB8B" + }, + "veh": { + "normal": ["\u06A4"], + "isolated": "\uFB6A", + "final": "\uFB6B", + "initial": "\uFB6C", + "medial": "\uFB6D" + }, + "peheh": { + "normal": ["\u06A6"], + "isolated": "\uFB6E", + "final": "\uFB6F", + "initial": "\uFB70", + "medial": "\uFB71" + }, + "keheh": { + "normal": ["\u06A9"], + "dot_above": ["\u0762"], + "three_dots_above": ["\u0763"], + "three_dots_pointing_upwards_below": ["\u0764"], + "isolated": "\uFB8E", + "final": "\uFB8F", + "initial": "\uFB90", + "medial": "\uFB91" + }, + "ng": { + "normal": ["\u06AD"], + "isolated": "\uFBD3", + "final": "\uFBD4", + "initial": "\uFBD5", + "medial": "\uFBD6" + }, + "gaf": { + "normal": ["\u06AF"], + "ring": ["\u06B0"], + "two_dots_below": ["\u06B2"], + "three_dots_above": ["\u06B4"], + "inverted_stroke": ["\u08B0"], + "isolated": "\uFB92", + "final": "\uFB93", + "initial": "\uFB94", + "medial": "\uFB95" + }, + "ngoeh": { + "normal": ["\u06B1"], + "isolated": "\uFB9A", + "final": "\uFB9B", + "initial": "\uFB9C", + "medial": "\uFB9D" + }, + "gueh": { + "normal": ["\u06B3"], + "isolated": "\uFB96", + "final": "\uFB97", + "initial": "\uFB98", + "medial": "\uFB99" + }, + "noon ghunna": { + "normal": ["\u06BA"], + "isolated": "\uFB9E", + "final": "\uFB9F" + }, + "rnoon": { + "normal": ["\u06BB"], + "isolated": "\uFBA0", + "final": "\uFBA1", + "initial": "\uFBA2", + "medial": "\uFBA3" + }, + "heh doachashmee": { + "normal": ["\u06BE"], + "isolated": "\uFBAA", + "final": "\uFBAB", + "initial": "\uFBAC", + "medial": "\uFBAD" + }, + "heh goal": { + "normal": ["\u06C1"], + "hamza_above": ["\u06C1\u0654", "\u06C2"], + "isolated": "\uFBA6", + "final": "\uFBA7", + "initial": "\uFBA8", + "medial": "\uFBA9" + }, + "teh marbuta goal": { + "normal": ["\u06C3"] + }, + "kirghiz oe": { + "normal": ["\u06C5"], + "isolated": "\uFBE0", + "final": "\uFBE1" + }, + "oe": { + "normal": ["\u06C6"], + "isolated": "\uFBD9", + "final": "\uFBDA" + }, + "u": { + "normal": ["\u06C7"], + "hamza_above": { + "normal": ["\u0677", "\u06C7\u0674"], + "isolated": "\uFBDD" + }, + "isolated": "\uFBD7", + "final": "\uFBD8" + }, + "yu": { + "normal": ["\u06C8"], + "isolated": "\uFBDB", + "final": "\uFBDC" + }, + "kirghiz yu": { + "normal": ["\u06C9"], + "isolated": "\uFBE2", + "final": "\uFBE3" + }, + "ve": { + "normal": ["\u06CB"], + "isolated": "\uFBDE", + "final": "\uFBDF" + }, + "farsi yeh": { + "normal": ["\u06CC"], + "indic_two_above": ["\u0775"], + "indic_three_above": ["\u0776"], + "indic_four_above": ["\u0777"], + "isolated": "\uFBFC", + "final": "\uFBFD", + "initial": "\uFBFE", + "medial": "\uFBFF" + }, + "e": { + "normal": ["\u06D0"], + "isolated": "\uFBE4", + "final": "\uFBE5", + "initial": "\uFBE6", + "medial": "\uFBE7" + }, + "yeh barree": { + "normal": ["\u06D2"], + "hamza_above": { + "normal": ["\u06D2\u0654", "\u06D3"], + "isolated": "\uFBB0", + "final": "\uFBB1" + }, + "indic_two_above": ["\u077A"], + "indic_three_above": ["\u077B"], + "isolated": "\uFBAE", + "final": "\uFBAF" + }, + "ae": { + "normal": ["\u06D5"], + "isolated": "\u06D5", + "final": "\uFEEA", + "yeh_above": { + "normal": ["\u06C0", "\u06D5\u0654"], + "isolated": "\uFBA4", + "final": "\uFBA5" + } + }, + "rohingya yeh": { + "normal": ["\u08AC"] + }, + "low alef": { + "normal": ["\u08AD"] + }, + "straight waw": { + "normal": ["\u08B1"] + }, + "african feh": { + "normal": ["\u08BB"] + }, + "african qaf": { + "normal": ["\u08BC"] + }, + "african noon": { + "normal": ["\u08BD"] + } + }; + + unicodeArabic["default"] = arabicReference; + + var unicodeLigatures = {}; + + Object.defineProperty(unicodeLigatures, "__esModule", { + value: true + }); + var ligatureReference = { + "\u0626\u0627": { + "isolated": "\uFBEA", + "final": "\uFBEB" + }, + "\u0626\u06D5": { + "isolated": "\uFBEC", + "final": "\uFBED" + }, + "\u0626\u0648": { + "isolated": "\uFBEE", + "final": "\uFBEF" + }, + "\u0626\u06C7": { + "isolated": "\uFBF0", + "final": "\uFBF1" + }, + "\u0626\u06C6": { + "isolated": "\uFBF2", + "final": "\uFBF3" + }, + "\u0626\u06C8": { + "isolated": "\uFBF4", + "final": "\uFBF5" + }, + "\u0626\u06D0": { + "isolated": "\uFBF6", + "final": "\uFBF7", + "initial": "\uFBF8" + }, + "\u0626\u0649": { + "uighur_kirghiz": { + "isolated": "\uFBF9", + "final": "\uFBFA", + "initial": "\uFBFB" + }, + "isolated": "\uFC03", + "final": "\uFC68" + }, + "\u0626\u062C": { + "isolated": "\uFC00", + "initial": "\uFC97" + }, + "\u0626\u062D": { + "isolated": "\uFC01", + "initial": "\uFC98" + }, + "\u0626\u0645": { + "isolated": "\uFC02", + "final": "\uFC66", + "initial": "\uFC9A", + "medial": "\uFCDF" + }, + "\u0626\u064A": { + "isolated": "\uFC04", + "final": "\uFC69" + }, + "\u0628\u062C": { + "isolated": "\uFC05", + "initial": "\uFC9C" + }, + "\u0628\u062D": { + "isolated": "\uFC06", + "initial": "\uFC9D" + }, + "\u0628\u062E": { + "isolated": "\uFC07", + "initial": "\uFC9E" + }, + "\u0628\u0645": { + "isolated": "\uFC08", + "final": "\uFC6C", + "initial": "\uFC9F", + "medial": "\uFCE1" + }, + "\u0628\u0649": { + "isolated": "\uFC09", + "final": "\uFC6E" + }, + "\u0628\u064A": { + "isolated": "\uFC0A", + "final": "\uFC6F" + }, + "\u062A\u062C": { + "isolated": "\uFC0B", + "initial": "\uFCA1" + }, + "\u062A\u062D": { + "isolated": "\uFC0C", + "initial": "\uFCA2" + }, + "\u062A\u062E": { + "isolated": "\uFC0D", + "initial": "\uFCA3" + }, + "\u062A\u0645": { + "isolated": "\uFC0E", + "final": "\uFC72", + "initial": "\uFCA4", + "medial": "\uFCE3" + }, + "\u062A\u0649": { + "isolated": "\uFC0F", + "final": "\uFC74" + }, + "\u062A\u064A": { + "isolated": "\uFC10", + "final": "\uFC75" + }, + "\u062B\u062C": { + "isolated": "\uFC11" + }, + "\u062B\u0645": { + "isolated": "\uFC12", + "final": "\uFC78", + "initial": "\uFCA6", + "medial": "\uFCE5" + }, + "\u062B\u0649": { + "isolated": "\uFC13", + "final": "\uFC7A" + }, + "\u062B\u0648": { + "isolated": "\uFC14" + }, + "\u062C\u062D": { + "isolated": "\uFC15", + "initial": "\uFCA7" + }, + "\u062C\u0645": { + "isolated": "\uFC16", + "initial": "\uFCA8" + }, + "\u062D\u062C": { + "isolated": "\uFC17", + "initial": "\uFCA9" + }, + "\u062D\u0645": { + "isolated": "\uFC18", + "initial": "\uFCAA" + }, + "\u062E\u062C": { + "isolated": "\uFC19", + "initial": "\uFCAB" + }, + "\u062E\u062D": { + "isolated": "\uFC1A" + }, + "\u062E\u0645": { + "isolated": "\uFC1B", + "initial": "\uFCAC" + }, + "\u0633\u062C": { + "isolated": "\uFC1C", + "initial": "\uFCAD", + "medial": "\uFD34" + }, + "\u0633\u062D": { + "isolated": "\uFC1D", + "initial": "\uFCAE", + "medial": "\uFD35" + }, + "\u0633\u062E": { + "isolated": "\uFC1E", + "initial": "\uFCAF", + "medial": "\uFD36" + }, + "\u0633\u0645": { + "isolated": "\uFC1F", + "initial": "\uFCB0", + "medial": "\uFCE7" + }, + "\u0635\u062D": { + "isolated": "\uFC20", + "initial": "\uFCB1" + }, + "\u0635\u0645": { + "isolated": "\uFC21", + "initial": "\uFCB3" + }, + "\u0636\u062C": { + "isolated": "\uFC22", + "initial": "\uFCB4" + }, + "\u0636\u062D": { + "isolated": "\uFC23", + "initial": "\uFCB5" + }, + "\u0636\u062E": { + "isolated": "\uFC24", + "initial": "\uFCB6" + }, + "\u0636\u0645": { + "isolated": "\uFC25", + "initial": "\uFCB7" + }, + "\u0637\u062D": { + "isolated": "\uFC26", + "initial": "\uFCB8" + }, + "\u0637\u0645": { + "isolated": "\uFC27", + "initial": "\uFD33", + "medial": "\uFD3A" + }, + "\u0638\u0645": { + "isolated": "\uFC28", + "initial": "\uFCB9", + "medial": "\uFD3B" + }, + "\u0639\u062C": { + "isolated": "\uFC29", + "initial": "\uFCBA" + }, + "\u0639\u0645": { + "isolated": "\uFC2A", + "initial": "\uFCBB" + }, + "\u063A\u062C": { + "isolated": "\uFC2B", + "initial": "\uFCBC" + }, + "\u063A\u0645": { + "isolated": "\uFC2C", + "initial": "\uFCBD" + }, + "\u0641\u062C": { + "isolated": "\uFC2D", + "initial": "\uFCBE" + }, + "\u0641\u062D": { + "isolated": "\uFC2E", + "initial": "\uFCBF" + }, + "\u0641\u062E": { + "isolated": "\uFC2F", + "initial": "\uFCC0" + }, + "\u0641\u0645": { + "isolated": "\uFC30", + "initial": "\uFCC1" + }, + "\u0641\u0649": { + "isolated": "\uFC31", + "final": "\uFC7C" + }, + "\u0641\u064A": { + "isolated": "\uFC32", + "final": "\uFC7D" + }, + "\u0642\u062D": { + "isolated": "\uFC33", + "initial": "\uFCC2" + }, + "\u0642\u0645": { + "isolated": "\uFC34", + "initial": "\uFCC3" + }, + "\u0642\u0649": { + "isolated": "\uFC35", + "final": "\uFC7E" + }, + "\u0642\u064A": { + "isolated": "\uFC36", + "final": "\uFC7F" + }, + "\u0643\u0627": { + "isolated": "\uFC37", + "final": "\uFC80" + }, + "\u0643\u062C": { + "isolated": "\uFC38", + "initial": "\uFCC4" + }, + "\u0643\u062D": { + "isolated": "\uFC39", + "initial": "\uFCC5" + }, + "\u0643\u062E": { + "isolated": "\uFC3A", + "initial": "\uFCC6" + }, + "\u0643\u0644": { + "isolated": "\uFC3B", + "final": "\uFC81", + "initial": "\uFCC7", + "medial": "\uFCEB" + }, + "\u0643\u0645": { + "isolated": "\uFC3C", + "final": "\uFC82", + "initial": "\uFCC8", + "medial": "\uFCEC" + }, + "\u0643\u0649": { + "isolated": "\uFC3D", + "final": "\uFC83" + }, + "\u0643\u064A": { + "isolated": "\uFC3E", + "final": "\uFC84" + }, + "\u0644\u062C": { + "isolated": "\uFC3F", + "initial": "\uFCC9" + }, + "\u0644\u062D": { + "isolated": "\uFC40", + "initial": "\uFCCA" + }, + "\u0644\u062E": { + "isolated": "\uFC41", + "initial": "\uFCCB" + }, + "\u0644\u0645": { + "isolated": "\uFC42", + "final": "\uFC85", + "initial": "\uFCCC", + "medial": "\uFCED" + }, + "\u0644\u0649": { + "isolated": "\uFC43", + "final": "\uFC86" + }, + "\u0644\u064A": { + "isolated": "\uFC44", + "final": "\uFC87" + }, + "\u0645\u062C": { + "isolated": "\uFC45", + "initial": "\uFCCE" + }, + "\u0645\u062D": { + "isolated": "\uFC46", + "initial": "\uFCCF" + }, + "\u0645\u062E": { + "isolated": "\uFC47", + "initial": "\uFCD0" + }, + "\u0645\u0645": { + "isolated": "\uFC48", + "final": "\uFC89", + "initial": "\uFCD1" + }, + "\u0645\u0649": { + "isolated": "\uFC49" + }, + "\u0645\u064A": { + "isolated": "\uFC4A" + }, + "\u0646\u062C": { + "isolated": "\uFC4B", + "initial": "\uFCD2" + }, + "\u0646\u062D": { + "isolated": "\uFC4C", + "initial": "\uFCD3" + }, + "\u0646\u062E": { + "isolated": "\uFC4D", + "initial": "\uFCD4" + }, + "\u0646\u0645": { + "isolated": "\uFC4E", + "final": "\uFC8C", + "initial": "\uFCD5", + "medial": "\uFCEE" + }, + "\u0646\u0649": { + "isolated": "\uFC4F", + "final": "\uFC8E" + }, + "\u0646\u064A": { + "isolated": "\uFC50", + "final": "\uFC8F" + }, + "\u0647\u062C": { + "isolated": "\uFC51", + "initial": "\uFCD7" + }, + "\u0647\u0645": { + "isolated": "\uFC52", + "initial": "\uFCD8" + }, + "\u0647\u0649": { + "isolated": "\uFC53" + }, + "\u0647\u064A": { + "isolated": "\uFC54" + }, + "\u064A\u062C": { + "isolated": "\uFC55", + "initial": "\uFCDA" + }, + "\u064A\u062D": { + "isolated": "\uFC56", + "initial": "\uFCDB" + }, + "\u064A\u062E": { + "isolated": "\uFC57", + "initial": "\uFCDC" + }, + "\u064A\u0645": { + "isolated": "\uFC58", + "final": "\uFC93", + "initial": "\uFCDD", + "medial": "\uFCF0" + }, + "\u064A\u0649": { + "isolated": "\uFC59", + "final": "\uFC95" + }, + "\u064A\u064A": { + "isolated": "\uFC5A", + "final": "\uFC96" + }, + "\u0630\u0670": { + "isolated": "\uFC5B" + }, + "\u0631\u0670": { + "isolated": "\uFC5C" + }, + "\u0649\u0670": { + "isolated": "\uFC5D", + "final": "\uFC90" + }, + "\u064C\u0651": { + "isolated": "\uFC5E" + }, + "\u064D\u0651": { + "isolated": "\uFC5F" + }, + "\u064E\u0651": { + "isolated": "\uFC60" + }, + "\u064F\u0651": { + "isolated": "\uFC61" + }, + "\u0650\u0651": { + "isolated": "\uFC62" + }, + "\u0651\u0670": { + "isolated": "\uFC63" + }, + "\u0626\u0631": { + "final": "\uFC64" + }, + "\u0626\u0632": { + "final": "\uFC65" + }, + "\u0626\u0646": { + "final": "\uFC67" + }, + "\u0628\u0631": { + "final": "\uFC6A" + }, + "\u0628\u0632": { + "final": "\uFC6B" + }, + "\u0628\u0646": { + "final": "\uFC6D" + }, + "\u062A\u0631": { + "final": "\uFC70" + }, + "\u062A\u0632": { + "final": "\uFC71" + }, + "\u062A\u0646": { + "final": "\uFC73" + }, + "\u062B\u0631": { + "final": "\uFC76" + }, + "\u062B\u0632": { + "final": "\uFC77" + }, + "\u062B\u0646": { + "final": "\uFC79" + }, + "\u062B\u064A": { + "final": "\uFC7B" + }, + "\u0645\u0627": { + "final": "\uFC88" + }, + "\u0646\u0631": { + "final": "\uFC8A" + }, + "\u0646\u0632": { + "final": "\uFC8B" + }, + "\u0646\u0646": { + "final": "\uFC8D" + }, + "\u064A\u0631": { + "final": "\uFC91" + }, + "\u064A\u0632": { + "final": "\uFC92" + }, + "\u064A\u0646": { + "final": "\uFC94" + }, + "\u0626\u062E": { + "initial": "\uFC99" + }, + "\u0626\u0647": { + "initial": "\uFC9B", + "medial": "\uFCE0" + }, + "\u0628\u0647": { + "initial": "\uFCA0", + "medial": "\uFCE2" + }, + "\u062A\u0647": { + "initial": "\uFCA5", + "medial": "\uFCE4" + }, + "\u0635\u062E": { + "initial": "\uFCB2" + }, + "\u0644\u0647": { + "initial": "\uFCCD" + }, + "\u0646\u0647": { + "initial": "\uFCD6", + "medial": "\uFCEF" + }, + "\u0647\u0670": { + "initial": "\uFCD9" + }, + "\u064A\u0647": { + "initial": "\uFCDE", + "medial": "\uFCF1" + }, + "\u062B\u0647": { + "medial": "\uFCE6" + }, + "\u0633\u0647": { + "medial": "\uFCE8", + "initial": "\uFD31" + }, + "\u0634\u0645": { + "medial": "\uFCE9", + "isolated": "\uFD0C", + "final": "\uFD28", + "initial": "\uFD30" + }, + "\u0634\u0647": { + "medial": "\uFCEA", + "initial": "\uFD32" + }, + "\u0640\u064E\u0651": { + "medial": "\uFCF2" + }, + "\u0640\u064F\u0651": { + "medial": "\uFCF3" + }, + "\u0640\u0650\u0651": { + "medial": "\uFCF4" + }, + "\u0637\u0649": { + "isolated": "\uFCF5", + "final": "\uFD11" + }, + "\u0637\u064A": { + "isolated": "\uFCF6", + "final": "\uFD12" + }, + "\u0639\u0649": { + "isolated": "\uFCF7", + "final": "\uFD13" + }, + "\u0639\u064A": { + "isolated": "\uFCF8", + "final": "\uFD14" + }, + "\u063A\u0649": { + "isolated": "\uFCF9", + "final": "\uFD15" + }, + "\u063A\u064A": { + "isolated": "\uFCFA", + "final": "\uFD16" + }, + "\u0633\u0649": { + "isolated": "\uFCFB" + }, + "\u0633\u064A": { + "isolated": "\uFCFC", + "final": "\uFD18" + }, + "\u0634\u0649": { + "isolated": "\uFCFD", + "final": "\uFD19" + }, + "\u0634\u064A": { + "isolated": "\uFCFE", + "final": "\uFD1A" + }, + "\u062D\u0649": { + "isolated": "\uFCFF", + "final": "\uFD1B" + }, + "\u062D\u064A": { + "isolated": "\uFD00", + "final": "\uFD1C" + }, + "\u062C\u0649": { + "isolated": "\uFD01", + "final": "\uFD1D" + }, + "\u062C\u064A": { + "isolated": "\uFD02", + "final": "\uFD1E" + }, + "\u062E\u0649": { + "isolated": "\uFD03", + "final": "\uFD1F" + }, + "\u062E\u064A": { + "isolated": "\uFD04", + "final": "\uFD20" + }, + "\u0635\u0649": { + "isolated": "\uFD05", + "final": "\uFD21" + }, + "\u0635\u064A": { + "isolated": "\uFD06", + "final": "\uFD22" + }, + "\u0636\u0649": { + "isolated": "\uFD07", + "final": "\uFD23" + }, + "\u0636\u064A": { + "isolated": "\uFD08", + "final": "\uFD24" + }, + "\u0634\u062C": { + "isolated": "\uFD09", + "final": "\uFD25", + "initial": "\uFD2D", + "medial": "\uFD37" + }, + "\u0634\u062D": { + "isolated": "\uFD0A", + "final": "\uFD26", + "initial": "\uFD2E", + "medial": "\uFD38" + }, + "\u0634\u062E": { + "isolated": "\uFD0B", + "final": "\uFD27", + "initial": "\uFD2F", + "medial": "\uFD39" + }, + "\u0634\u0631": { + "isolated": "\uFD0D", + "final": "\uFD29" + }, + "\u0633\u0631": { + "isolated": "\uFD0E", + "final": "\uFD2A" + }, + "\u0635\u0631": { + "isolated": "\uFD0F", + "final": "\uFD2B" + }, + "\u0636\u0631": { + "isolated": "\uFD10", + "final": "\uFD2C" + }, + "\u0633\u0639": { + "final": "\uFD17" + }, + "\u062A\u062C\u0645": { + "initial": "\uFD50" + }, + "\u062A\u062D\u062C": { + "final": "\uFD51", + "initial": "\uFD52" + }, + "\u062A\u062D\u0645": { + "initial": "\uFD53" + }, + "\u062A\u062E\u0645": { + "initial": "\uFD54" + }, + "\u062A\u0645\u062C": { + "initial": "\uFD55" + }, + "\u062A\u0645\u062D": { + "initial": "\uFD56" + }, + "\u062A\u0645\u062E": { + "initial": "\uFD57" + }, + "\u062C\u0645\u062D": { + "final": "\uFD58", + "initial": "\uFD59" + }, + "\u062D\u0645\u064A": { + "final": "\uFD5A" + }, + "\u062D\u0645\u0649": { + "final": "\uFD5B" + }, + "\u0633\u062D\u062C": { + "initial": "\uFD5C" + }, + "\u0633\u062C\u062D": { + "initial": "\uFD5D" + }, + "\u0633\u062C\u0649": { + "final": "\uFD5E" + }, + "\u0633\u0645\u062D": { + "final": "\uFD5F", + "initial": "\uFD60" + }, + "\u0633\u0645\u062C": { + "initial": "\uFD61" + }, + "\u0633\u0645\u0645": { + "final": "\uFD62", + "initial": "\uFD63" + }, + "\u0635\u062D\u062D": { + "final": "\uFD64", + "initial": "\uFD65" + }, + "\u0635\u0645\u0645": { + "final": "\uFD66", + "initial": "\uFDC5" + }, + "\u0634\u062D\u0645": { + "final": "\uFD67", + "initial": "\uFD68" + }, + "\u0634\u062C\u064A": { + "final": "\uFD69" + }, + "\u0634\u0645\u062E": { + "final": "\uFD6A", + "initial": "\uFD6B" + }, + "\u0634\u0645\u0645": { + "final": "\uFD6C", + "initial": "\uFD6D" + }, + "\u0636\u062D\u0649": { + "final": "\uFD6E" + }, + "\u0636\u062E\u0645": { + "final": "\uFD6F", + "initial": "\uFD70" + }, + "\u0636\u0645\u062D": { + "final": "\uFD71" + }, + "\u0637\u0645\u062D": { + "initial": "\uFD72" + }, + "\u0637\u0645\u0645": { + "initial": "\uFD73" + }, + "\u0637\u0645\u064A": { + "final": "\uFD74" + }, + "\u0639\u062C\u0645": { + "final": "\uFD75", + "initial": "\uFDC4" + }, + "\u0639\u0645\u0645": { + "final": "\uFD76", + "initial": "\uFD77" + }, + "\u0639\u0645\u0649": { + "final": "\uFD78" + }, + "\u063A\u0645\u0645": { + "final": "\uFD79" + }, + "\u063A\u0645\u064A": { + "final": "\uFD7A" + }, + "\u063A\u0645\u0649": { + "final": "\uFD7B" + }, + "\u0641\u062E\u0645": { + "final": "\uFD7C", + "initial": "\uFD7D" + }, + "\u0642\u0645\u062D": { + "final": "\uFD7E", + "initial": "\uFDB4" + }, + "\u0642\u0645\u0645": { + "final": "\uFD7F" + }, + "\u0644\u062D\u0645": { + "final": "\uFD80", + "initial": "\uFDB5" + }, + "\u0644\u062D\u064A": { + "final": "\uFD81" + }, + "\u0644\u062D\u0649": { + "final": "\uFD82" + }, + "\u0644\u062C\u062C": { + "initial": "\uFD83", + "final": "\uFD84" + }, + "\u0644\u062E\u0645": { + "final": "\uFD85", + "initial": "\uFD86" + }, + "\u0644\u0645\u062D": { + "final": "\uFD87", + "initial": "\uFD88" + }, + "\u0645\u062D\u062C": { + "initial": "\uFD89" + }, + "\u0645\u062D\u0645": { + "initial": "\uFD8A" + }, + "\u0645\u062D\u064A": { + "final": "\uFD8B" + }, + "\u0645\u062C\u062D": { + "initial": "\uFD8C" + }, + "\u0645\u062C\u0645": { + "initial": "\uFD8D" + }, + "\u0645\u062E\u062C": { + "initial": "\uFD8E" + }, + "\u0645\u062E\u0645": { + "initial": "\uFD8F" + }, + "\u0645\u062C\u062E": { + "initial": "\uFD92" + }, + "\u0647\u0645\u062C": { + "initial": "\uFD93" + }, + "\u0647\u0645\u0645": { + "initial": "\uFD94" + }, + "\u0646\u062D\u0645": { + "initial": "\uFD95" + }, + "\u0646\u062D\u0649": { + "final": "\uFD96" + }, + "\u0646\u062C\u0645": { + "final": "\uFD97", + "initial": "\uFD98" + }, + "\u0646\u062C\u0649": { + "final": "\uFD99" + }, + "\u0646\u0645\u064A": { + "final": "\uFD9A" + }, + "\u0646\u0645\u0649": { + "final": "\uFD9B" + }, + "\u064A\u0645\u0645": { + "final": "\uFD9C", + "initial": "\uFD9D" + }, + "\u0628\u062E\u064A": { + "final": "\uFD9E" + }, + "\u062A\u062C\u064A": { + "final": "\uFD9F" + }, + "\u062A\u062C\u0649": { + "final": "\uFDA0" + }, + "\u062A\u062E\u064A": { + "final": "\uFDA1" + }, + "\u062A\u062E\u0649": { + "final": "\uFDA2" + }, + "\u062A\u0645\u064A": { + "final": "\uFDA3" + }, + "\u062A\u0645\u0649": { + "final": "\uFDA4" + }, + "\u062C\u0645\u064A": { + "final": "\uFDA5" + }, + "\u062C\u062D\u0649": { + "final": "\uFDA6" + }, + "\u062C\u0645\u0649": { + "final": "\uFDA7" + }, + "\u0633\u062E\u0649": { + "final": "\uFDA8" + }, + "\u0635\u062D\u064A": { + "final": "\uFDA9" + }, + "\u0634\u062D\u064A": { + "final": "\uFDAA" + }, + "\u0636\u062D\u064A": { + "final": "\uFDAB" + }, + "\u0644\u062C\u064A": { + "final": "\uFDAC" + }, + "\u0644\u0645\u064A": { + "final": "\uFDAD" + }, + "\u064A\u062D\u064A": { + "final": "\uFDAE" + }, + "\u064A\u062C\u064A": { + "final": "\uFDAF" + }, + "\u064A\u0645\u064A": { + "final": "\uFDB0" + }, + "\u0645\u0645\u064A": { + "final": "\uFDB1" + }, + "\u0642\u0645\u064A": { + "final": "\uFDB2" + }, + "\u0646\u062D\u064A": { + "final": "\uFDB3" + }, + "\u0639\u0645\u064A": { + "final": "\uFDB6" + }, + "\u0643\u0645\u064A": { + "final": "\uFDB7" + }, + "\u0646\u062C\u062D": { + "initial": "\uFDB8", + "final": "\uFDBD" + }, + "\u0645\u062E\u064A": { + "final": "\uFDB9" + }, + "\u0644\u062C\u0645": { + "initial": "\uFDBA", + "final": "\uFDBC" + }, + "\u0643\u0645\u0645": { + "final": "\uFDBB", + "initial": "\uFDC3" + }, + "\u062C\u062D\u064A": { + "final": "\uFDBE" + }, + "\u062D\u062C\u064A": { + "final": "\uFDBF" + }, + "\u0645\u062C\u064A": { + "final": "\uFDC0" + }, + "\u0641\u0645\u064A": { + "final": "\uFDC1" + }, + "\u0628\u062D\u064A": { + "final": "\uFDC2" + }, + "\u0633\u062E\u064A": { + "final": "\uFDC6" + }, + "\u0646\u062C\u064A": { + "final": "\uFDC7" + }, + "\u0644\u0622": { + "isolated": "\uFEF5", + "final": "\uFEF6" + }, + "\u0644\u0623": { + "isolated": "\uFEF7", + "final": "\uFEF8" + }, + "\u0644\u0625": { + "isolated": "\uFEF9", + "final": "\uFEFA" + }, + "\u0644\u0627": { + "isolated": "\uFEFB", + "final": "\uFEFC" + }, + "words": { + "\u0635\u0644\u06D2": "\uFDF0", + "\u0642\u0644\u06D2": "\uFDF1", + "\u0627\u0644\u0644\u0647": "\uFDF2", + "\u0627\u0643\u0628\u0631": "\uFDF3", + "\u0645\u062D\u0645\u062F": "\uFDF4", + "\u0635\u0644\u0639\u0645": "\uFDF5", + "\u0631\u0633\u0648\u0644": "\uFDF6", + "\u0639\u0644\u064A\u0647": "\uFDF7", + "\u0648\u0633\u0644\u0645": "\uFDF8", + "\u0635\u0644\u0649": "\uFDF9", + "\u0635\u0644\u0649\u0627\u0644\u0644\u0647\u0639\u0644\u064A\u0647\u0648\u0633\u0644\u0645": "\uFDFA", + "\u062C\u0644\u062C\u0644\u0627\u0644\u0647": "\uFDFB", + "\u0631\u06CC\u0627\u0644": "\uFDFC" + } + }; + + unicodeLigatures["default"] = ligatureReference; + + Object.defineProperty(reference, "__esModule", { + value: true + }); + var unicode_arabic_1$3 = unicodeArabic; + var unicode_ligatures_1$2 = unicodeLigatures; + var letterList = Object.keys(unicode_arabic_1$3["default"]); + reference.letterList = letterList; + var ligatureList = Object.keys(unicode_ligatures_1$2["default"]); + reference.ligatureList = ligatureList; + var ligatureWordList = Object.keys(unicode_ligatures_1$2["default"].words); + reference.ligatureWordList = ligatureWordList; + var lams = "\u0644\u06B5\u06B6\u06B7\u06B8"; + reference.lams = lams; + var alefs = "\u0627\u0622\u0623\u0625\u0671\u0672\u0673\u0675\u0773\u0774"; + reference.alefs = alefs; // for (var l = 1; l < lams.length; l++) { + // console.log('-'); + // for (var a = 0; a < alefs.length; a++) { + // console.log(a + ': ' + lams[l] + alefs[a]); + // } + // } + + var tashkeel = "\u0605\u0640\u0670\u0674\u06DF\u06E7\u06E8"; + reference.tashkeel = tashkeel; + + function addToTashkeel(start, finish) { + for (var i = start; i <= finish; i++) { + reference.tashkeel = tashkeel += String.fromCharCode(i); + } + } + + addToTashkeel(0x0610, 0x061A); + addToTashkeel(0x064B, 0x065F); + addToTashkeel(0x06D6, 0x06DC); + addToTashkeel(0x06E0, 0x06E4); + addToTashkeel(0x06EA, 0x06ED); + addToTashkeel(0x08D3, 0x08E1); + addToTashkeel(0x08E3, 0x08FF); + addToTashkeel(0xFE70, 0xFE7F); + var lineBreakers = "\u0627\u0629\u0648\u06C0\u06CF\u06FD\u06FE\u076B\u076C\u0771\u0773\u0774\u0778\u0779\u08E2\u08B1\u08B2\u08B9"; + reference.lineBreakers = lineBreakers; + + function addToLineBreakers(start, finish) { + for (var i = start; i <= finish; i++) { + reference.lineBreakers = lineBreakers += String.fromCharCode(i); + } + } + + addToLineBreakers(0x0600, 0x061F); // it's OK to include tashkeel in this range as it is ignored + + addToLineBreakers(0x0621, 0x0625); + addToLineBreakers(0x062F, 0x0632); + addToLineBreakers(0x0660, 0x066D); // numerals, math + + addToLineBreakers(0x0671, 0x0677); + addToLineBreakers(0x0688, 0x0699); + addToLineBreakers(0x06C3, 0x06CB); + addToLineBreakers(0x06D2, 0x06F9); + addToLineBreakers(0x0759, 0x075B); + addToLineBreakers(0x08AA, 0x08AE); + addToLineBreakers(0xFB50, 0xFDFD); // presentation forms look like they could connect, but never do + // Presentation Forms A includes diacritics but they are meant to stand alone + + addToLineBreakers(0xFE80, 0xFEFC); // presentation forms look like they could connect, but never do + // numerals, math + + addToLineBreakers(0x10E60, 0x10E7F); + addToLineBreakers(0x1EC70, 0x1ECBF); + addToLineBreakers(0x1EE00, 0x1EEFF); + + Object.defineProperty(GlyphSplitter$1, "__esModule", { + value: true + }); + var isArabic_1$6 = isArabic$1; + var reference_1$5 = reference; + + function GlyphSplitter(word) { + var letters = []; + var lastLetter = ''; + word.split('').forEach(function (letter) { + if (isArabic_1$6.isArabic(letter)) { + if (reference_1$5.tashkeel.indexOf(letter) > -1) { + letters[letters.length - 1] += letter; + } else if (lastLetter.length && (reference_1$5.lams.indexOf(lastLetter) === 0 && reference_1$5.alefs.indexOf(letter) > -1 || reference_1$5.lams.indexOf(lastLetter) > 0 && reference_1$5.alefs.indexOf(letter) === 0)) { + // valid LA forms + letters[letters.length - 1] += letter; + } else { + letters.push(letter); + } + } else { + letters.push(letter); + } + + if (reference_1$5.tashkeel.indexOf(letter) === -1) { + lastLetter = letter; + } + }); + return letters; + } + + GlyphSplitter$1.GlyphSplitter = GlyphSplitter; + + var BaselineSplitter$1 = {}; + + Object.defineProperty(BaselineSplitter$1, "__esModule", { + value: true + }); + var isArabic_1$5 = isArabic$1; + var reference_1$4 = reference; + + function BaselineSplitter(word) { + var letters = []; + var lastLetter = ''; + word.split('').forEach(function (letter) { + if (isArabic_1$5.isArabic(letter) && isArabic_1$5.isArabic(lastLetter)) { + if (lastLetter.length && reference_1$4.tashkeel.indexOf(letter) > -1) { + letters[letters.length - 1] += letter; + } else if (reference_1$4.lineBreakers.indexOf(lastLetter) > -1) { + letters.push(letter); + } else { + letters[letters.length - 1] += letter; + } + } else { + letters.push(letter); + } + + if (reference_1$4.tashkeel.indexOf(letter) === -1) { + // don't allow tashkeel to hide line break + lastLetter = letter; + } + }); + return letters; + } + + BaselineSplitter$1.BaselineSplitter = BaselineSplitter; + + var Normalization = {}; + + Object.defineProperty(Normalization, "__esModule", { + value: true + }); + var unicode_arabic_1$2 = unicodeArabic; + var unicode_ligatures_1$1 = unicodeLigatures; + var isArabic_1$4 = isArabic$1; + var reference_1$3 = reference; + + function Normal(word, breakPresentationForm) { + // default is to turn initial/isolated/medial/final presentation form to generic + if (typeof breakPresentationForm === 'undefined') { + breakPresentationForm = true; + } + + var returnable = ''; + word.split('').forEach(function (letter) { + if (!isArabic_1$4.isArabic(letter)) { + returnable += letter; + return; + } + + for (var w = 0; w < reference_1$3.letterList.length; w++) { + // ok so we are checking this potential lettertron + var letterForms = unicode_arabic_1$2["default"][reference_1$3.letterList[w]]; + var versions = Object.keys(letterForms); + + for (var v = 0; v < versions.length; v++) { + var localVersion = letterForms[versions[v]]; + + if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') { + // look at this embedded object + var embeddedForms = Object.keys(localVersion); + + for (var ef = 0; ef < embeddedForms.length; ef++) { + var form = localVersion[embeddedForms[ef]]; + + if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) { + // match + // console.log('embedded match'); + if (form === letter) { + // match exact + if (breakPresentationForm && localVersion['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(embeddedForms[ef]) > -1) { + // replace presentation form + // console.log('keeping normal form of the letter'); + if (_typeof(localVersion['normal']) === 'object') { + returnable += localVersion['normal'][0]; + } else { + returnable += localVersion['normal']; + } + + return; + } // console.log('keeping this letter'); + + + returnable += letter; + return; + } else if (_typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) { + // match + returnable += form[0]; // console.log('added the first letter from the same array'); + + return; + } + } + } + } else if (localVersion === letter) { + // match exact + if (breakPresentationForm && letterForms['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(versions[v]) > -1) { + // replace presentation form + // console.log('keeping normal form of the letter'); + if (_typeof(letterForms['normal']) === 'object') { + returnable += letterForms['normal'][0]; + } else { + returnable += letterForms['normal']; + } + + return; + } // console.log('keeping this letter'); + + + returnable += letter; + return; + } else if (_typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) { + // match + returnable += localVersion[0]; // console.log('added the first letter from the same array'); + + return; + } + } + } // try ligatures + + + for (var v2 = 0; v2 < reference_1$3.ligatureList.length; v2++) { + var normalForm = reference_1$3.ligatureList[v2]; + + if (normalForm !== 'words') { + var ligForms = Object.keys(unicode_ligatures_1$1["default"][normalForm]); + + for (var f = 0; f < ligForms.length; f++) { + if (unicode_ligatures_1$1["default"][normalForm][ligForms[f]] === letter) { + returnable += normalForm; + return; + } + } + } + } // try words ligatures + + + for (var v3 = 0; v3 < reference_1$3.ligatureWordList.length; v3++) { + var _normalForm = reference_1$3.ligatureWordList[v3]; + + if (unicode_ligatures_1$1["default"].words[_normalForm] === letter) { + returnable += _normalForm; + return; + } + } + + returnable += letter; // console.log('kept the letter') + }); + return returnable; + } + + Normalization.Normal = Normal; + + var CharShaper$1 = {}; + + Object.defineProperty(CharShaper$1, "__esModule", { + value: true + }); + var unicode_arabic_1$1 = unicodeArabic; + var isArabic_1$3 = isArabic$1; + var reference_1$2 = reference; + + function CharShaper(letter, form) { + if (!isArabic_1$3.isArabic(letter)) { + // fail not Arabic + throw new Error('Not Arabic'); + } + + if (letter === "\u0621") { + // hamza alone + return "\u0621"; + } + + for (var w = 0; w < reference_1$2.letterList.length; w++) { + // ok so we are checking this potential lettertron + var letterForms = unicode_arabic_1$1["default"][reference_1$2.letterList[w]]; + var versions = Object.keys(letterForms); + + for (var v = 0; v < versions.length; v++) { + var localVersion = letterForms[versions[v]]; + + if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) { + if (versions.indexOf(form) > -1) { + return letterForms[form]; + } + } else if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') { + // check embedded + var embeddedVersions = Object.keys(localVersion); + + for (var ev = 0; ev < embeddedVersions.length; ev++) { + if (localVersion[embeddedVersions[ev]] === letter || _typeof(localVersion[embeddedVersions[ev]]) === 'object' && localVersion[embeddedVersions[ev]].indexOf && localVersion[embeddedVersions[ev]].indexOf(letter) > -1) { + if (embeddedVersions.indexOf(form) > -1) { + return localVersion[form]; + } + } + } + } + } + } + } + + CharShaper$1.CharShaper = CharShaper; + + var WordShaper$2 = {}; + + Object.defineProperty(WordShaper$2, "__esModule", { + value: true + }); + var isArabic_1$2 = isArabic$1; + var reference_1$1 = reference; + var CharShaper_1$1 = CharShaper$1; + var unicode_ligatures_1 = unicodeLigatures; + + function WordShaper$1(word) { + var state = 'initial'; + var output = ''; + + for (var w = 0; w < word.length; w++) { + var nextLetter = ' '; + + for (var nxw = w + 1; nxw < word.length; nxw++) { + if (!isArabic_1$2.isArabic(word[nxw])) { + break; + } + + if (reference_1$1.tashkeel.indexOf(word[nxw]) === -1) { + nextLetter = word[nxw]; + break; + } + } + + if (!isArabic_1$2.isArabic(word[w]) || isArabic_1$2.isMath(word[w])) { + // space or other non-Arabic + output += word[w]; + state = 'initial'; + } else if (reference_1$1.tashkeel.indexOf(word[w]) > -1) { + // tashkeel - add without changing state + output += word[w]; + } else if (nextLetter === ' ' || // last Arabic letter in this word + reference_1$1.lineBreakers.indexOf(word[w]) > -1) { + // the current letter is known to break lines + output += CharShaper_1$1.CharShaper(word[w], state === 'initial' ? 'isolated' : 'final'); + state = 'initial'; + } else if (reference_1$1.lams.indexOf(word[w]) > -1 && reference_1$1.alefs.indexOf(nextLetter) > -1) { + // LA letters - advance an additional letter after this + output += unicode_ligatures_1["default"][word[w] + nextLetter][state === 'initial' ? 'isolated' : 'final']; + + while (word[w] !== nextLetter) { + w++; + } + + state = 'initial'; + } else { + output += CharShaper_1$1.CharShaper(word[w], state); + state = 'medial'; + } + } + + return output; + } + + WordShaper$2.WordShaper = WordShaper$1; + + var ParentLetter$1 = {}; + + Object.defineProperty(ParentLetter$1, "__esModule", { + value: true + }); + var unicode_arabic_1 = unicodeArabic; + var isArabic_1$1 = isArabic$1; + var reference_1 = reference; + + function ParentLetter(letter) { + if (!isArabic_1$1.isArabic(letter)) { + throw new Error('Not an Arabic letter'); + } + + for (var w = 0; w < reference_1.letterList.length; w++) { + // ok so we are checking this potential lettertron + var letterForms = unicode_arabic_1["default"][reference_1.letterList[w]]; + var versions = Object.keys(letterForms); + + for (var v = 0; v < versions.length; v++) { + var localVersion = letterForms[versions[v]]; + + if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') { + // look at this embedded object + var embeddedForms = Object.keys(localVersion); + + for (var ef = 0; ef < embeddedForms.length; ef++) { + var form = localVersion[embeddedForms[ef]]; + + if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) { + // match + return localVersion; + } + } + } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) { + // match + return letterForms; + } + } + + return null; + } + } + + ParentLetter$1.ParentLetter = ParentLetter; + + function GrandparentLetter(letter) { + if (!isArabic_1$1.isArabic(letter)) { + throw new Error('Not an Arabic letter'); + } + + for (var w = 0; w < reference_1.letterList.length; w++) { + // ok so we are checking this potential lettertron + var letterForms = unicode_arabic_1["default"][reference_1.letterList[w]]; + var versions = Object.keys(letterForms); + + for (var v = 0; v < versions.length; v++) { + var localVersion = letterForms[versions[v]]; + + if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') { + // look at this embedded object + var embeddedForms = Object.keys(localVersion); + + for (var ef = 0; ef < embeddedForms.length; ef++) { + var form = localVersion[embeddedForms[ef]]; + + if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) { + // match + return letterForms; + } + } + } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) { + // match + return letterForms; + } + } + + return null; + } + } + + ParentLetter$1.GrandparentLetter = GrandparentLetter; + + Object.defineProperty(lib, "__esModule", { + value: true + }); + var isArabic_1 = isArabic$1; + lib.isArabic = isArabic_1.isArabic; + var GlyphSplitter_1 = GlyphSplitter$1; + lib.GlyphSplitter = GlyphSplitter_1.GlyphSplitter; + var BaselineSplitter_1 = BaselineSplitter$1; + lib.BaselineSplitter = BaselineSplitter_1.BaselineSplitter; + var Normalization_1 = Normalization; + lib.Normal = Normalization_1.Normal; + var CharShaper_1 = CharShaper$1; + lib.CharShaper = CharShaper_1.CharShaper; + var WordShaper_1 = WordShaper$2; + var WordShaper = lib.WordShaper = WordShaper_1.WordShaper; + var ParentLetter_1 = ParentLetter$1; + lib.ParentLetter = ParentLetter_1.ParentLetter; + lib.GrandparentLetter = ParentLetter_1.GrandparentLetter; + + var rtlRegex = /[\u0590-\u05FF\u0600-\u06FF\u0750-\u07BF\u08A0–\u08BF]/; + function fixRTLTextForSvg(inputText) { + var ret = '', + rtlBuffer = []; + var arabicRegex = /[\u0600-\u06FF]/g; + var arabicDiacritics = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED]/g; + var arabicMath = /[\u0660-\u066C\u06F0-\u06F9]+/g; + var thaanaVowel = /[\u07A6-\u07B0]/; + var hebrewSign = /[\u0591-\u05bd\u05bf\u05c1-\u05c5\u05c7]/; // Arabic word shaping + + if (arabicRegex.test(inputText)) { + inputText = WordShaper(inputText); + } + + for (var n = 0; n < inputText.length; n++) { + var c = inputText[n]; + + if (arabicMath.test(c)) { + // Arabic numbers go LTR + ret += rtlBuffer.reverse().join(''); + rtlBuffer = [c]; + } else { + if (rtlBuffer.length && arabicMath.test(rtlBuffer[rtlBuffer.length - 1])) { + ret += rtlBuffer.reverse().join(''); + rtlBuffer = []; + } + + if ((thaanaVowel.test(c) || hebrewSign.test(c) || arabicDiacritics.test(c)) && rtlBuffer.length) { + rtlBuffer[rtlBuffer.length - 1] += c; + } else if (rtlRegex.test(c) // include Arabic presentation forms + || c.charCodeAt(0) >= 64336 && c.charCodeAt(0) <= 65023 || c.charCodeAt(0) >= 65136 && c.charCodeAt(0) <= 65279) { + rtlBuffer.push(c); + } else if (c === ' ' && rtlBuffer.length) { + // whitespace within RTL text + rtlBuffer = [rtlBuffer.reverse().join('') + ' ']; + } else { + // non-RTL character + ret += rtlBuffer.reverse().join('') + c; + rtlBuffer = []; + } + } + } + + ret += rtlBuffer.reverse().join(''); + return ret; + } + + var DESCRIPTORS$1 = descriptors; + var objectKeys = objectKeys$4; + var toIndexedObject = toIndexedObject$b; + var propertyIsEnumerable = objectPropertyIsEnumerable.f; + + // `Object.{ entries, values }` methods implementation + var createMethod$1 = function (TO_ENTRIES) { + return function (it) { + var O = toIndexedObject(it); + var keys = objectKeys(O); + var length = keys.length; + var i = 0; + var result = []; + var key; + while (length > i) { + key = keys[i++]; + if (!DESCRIPTORS$1 || propertyIsEnumerable.call(O, key)) { + result.push(TO_ENTRIES ? [key, O[key]] : O[key]); + } + } + return result; + }; + }; + + var objectToArray = { + // `Object.entries` method + // https://tc39.es/ecma262/#sec-object.entries + entries: createMethod$1(true), + // `Object.values` method + // https://tc39.es/ecma262/#sec-object.values + values: createMethod$1(false) + }; + + var $$k = _export; + var $values = objectToArray.values; + + // `Object.values` method + // https://tc39.es/ecma262/#sec-object.values + $$k({ target: 'Object', stat: true }, { + values: function values(O) { + return $values(O); + } + }); + + // https://github.com/openstreetmap/iD/issues/772 + // http://mathiasbynens.be/notes/localstorage-pattern#comment-9 + var _storage; + + try { + _storage = localStorage; + } catch (e) {} // eslint-disable-line no-empty + + + _storage = _storage || function () { + var s = {}; + return { + getItem: function getItem(k) { + return s[k]; + }, + setItem: function setItem(k, v) { + return s[k] = v; + }, + removeItem: function removeItem(k) { + return delete s[k]; + } + }; + }(); // + // corePreferences is an interface for persisting basic key-value strings + // within and between iD sessions on the same site. + // + + /** + * @param {string} k + * @param {string?} v + * @returns {boolean} true if the action succeeded + */ + + + function corePreferences(k, v) { + try { + if (arguments.length === 1) return _storage.getItem(k);else if (v === null) _storage.removeItem(k);else _storage.setItem(k, v); + return true; + } catch (e) { + /* eslint-disable no-console */ + if (typeof console !== 'undefined') { + console.error('localStorage quota exceeded'); + } + /* eslint-enable no-console */ + + + return false; + } + } + + var vparse = {exports: {}}; + + (function (module) { + (function (window) { + + function parseVersion(v) { + var m = v.replace(/[^0-9.]/g, '').match(/[0-9]*\.|[0-9]+/g) || []; + v = { + major: +m[0] || 0, + minor: +m[1] || 0, + patch: +m[2] || 0, + build: +m[3] || 0 + }; + v.isEmpty = !v.major && !v.minor && !v.patch && !v.build; + v.parsed = [v.major, v.minor, v.patch, v.build]; + v.text = v.parsed.join('.'); + v.compare = compare; + return v; + } + + function compare(v) { + if (typeof v === 'string') { + v = parseVersion(v); + } + + for (var i = 0; i < 4; i++) { + if (this.parsed[i] !== v.parsed[i]) { + return this.parsed[i] > v.parsed[i] ? 1 : -1; + } + } + + return 0; + } + /* istanbul ignore next */ + + + if (module && 'object' === 'object') { + module.exports = parseVersion; + } else { + window.parseVersion = parseVersion; + } + })(commonjsGlobal); + })(vparse); + + var parseVersion = vparse.exports; + + var name = "iD"; + var version = "2.20.2"; + var description = "A friendly editor for OpenStreetMap"; + var main = "dist/iD.min.js"; + var repository = "github:openstreetmap/iD"; + var homepage = "https://github.com/openstreetmap/iD"; + var bugs = "https://github.com/openstreetmap/iD/issues"; + var keywords = ["editor","openstreetmap"]; + var license = "ISC"; + 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"}; + 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"}; + 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":"^21.0.0","@rollup/plugin-json":"^4.0.1","@rollup/plugin-node-resolve":"~13.0.5",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"}; + var engines = {node:">=10"}; + var browserslist = ["> 0.2%, last 6 major versions, Firefox ESR, IE 11, maintained node versions"]; + var packageJSON = { + name: name, + version: version, + description: description, + main: main, + repository: repository, + homepage: homepage, + bugs: bugs, + keywords: keywords, + license: license, + scripts: scripts, + dependencies: dependencies, + devDependencies: devDependencies, + engines: engines, + browserslist: browserslist + }; + + var _mainFileFetcher = coreFileFetcher(); // singleton + // coreFileFetcher asynchronously fetches data from JSON files + // + + function coreFileFetcher() { + var ociVersion = packageJSON.devDependencies['osm-community-index']; + var v = parseVersion(ociVersion); + var vMinor = "".concat(v.major, ".").concat(v.minor); + var _this = {}; + var _inflight = {}; + var _fileMap = { + 'address_formats': 'data/address_formats.min.json', + 'deprecated': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/deprecated.min.json', + 'discarded': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/discarded.min.json', + 'imagery': 'data/imagery.min.json', + 'intro_graph': 'data/intro_graph.min.json', + 'keepRight': 'data/keepRight.min.json', + 'languages': 'data/languages.min.json', + 'locales': 'locales/index.min.json', + 'oci_defaults': "https://cdn.jsdelivr.net/npm/osm-community-index@".concat(vMinor, "/dist/defaults.min.json"), + 'oci_features': "https://cdn.jsdelivr.net/npm/osm-community-index@".concat(vMinor, "/dist/featureCollection.min.json"), + 'oci_resources': "https://cdn.jsdelivr.net/npm/osm-community-index@".concat(vMinor, "/dist/resources.min.json"), + 'preset_categories': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/preset_categories.min.json', + 'preset_defaults': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/preset_defaults.min.json', + 'preset_fields': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/fields.min.json', + 'preset_presets': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/presets.min.json', + 'phone_formats': 'data/phone_formats.min.json', + 'qa_data': 'data/qa_data.min.json', + 'shortcuts': 'data/shortcuts.min.json', + 'territory_languages': 'data/territory_languages.min.json', + 'wmf_sitematrix': 'https://cdn.jsdelivr.net/npm/wmf-sitematrix@0.1/wikipedia.min.json' + }; + var _cachedData = {}; // expose the cache; useful for tests + + _this.cache = function () { + return _cachedData; + }; // Returns a Promise to fetch data + // (resolved with the data if we have it already) + + + _this.get = function (which) { + if (_cachedData[which]) { + return Promise.resolve(_cachedData[which]); + } + + var file = _fileMap[which]; + + var url = file && _this.asset(file); + + if (!url) { + return Promise.reject("Unknown data file for \"".concat(which, "\"")); + } + + var prom = _inflight[url]; + + if (!prom) { + _inflight[url] = prom = fetch(url).then(function (response) { + // fetch in PhantomJS tests may return ok=false and status=0 even if it's okay + if (!response.ok && response.status !== 0 || !response.json) { + throw new Error(response.status + ' ' + response.statusText); + } + + if (response.status === 204 || response.status === 205) return; // No Content, Reset Content + + return response.json(); + }).then(function (result) { + delete _inflight[url]; + + if (!result) { + throw new Error("No data loaded for \"".concat(which, "\"")); + } + + _cachedData[which] = result; + return result; + })["catch"](function (err) { + delete _inflight[url]; + throw err; + }); + } + + return prom; + }; // Accessor for the file map + + + _this.fileMap = function (val) { + if (!arguments.length) return _fileMap; + _fileMap = val; + return _this; + }; + + var _assetPath = ''; + + _this.assetPath = function (val) { + if (!arguments.length) return _assetPath; + _assetPath = val; + return _this; + }; + + var _assetMap = {}; + + _this.assetMap = function (val) { + if (!arguments.length) return _assetMap; + _assetMap = val; + return _this; + }; + + _this.asset = function (val) { + if (/^http(s)?:\/\//i.test(val)) return val; + var filename = _assetPath + val; + return _assetMap[filename] || filename; + }; + + return _this; + } + + var classof = classofRaw$1; + + // `thisNumberValue` abstract operation + // https://tc39.es/ecma262/#sec-thisnumbervalue + var thisNumberValue$2 = function (value) { + if (typeof value != 'number' && classof(value) != 'Number') { + throw TypeError('Incorrect invocation'); + } + return +value; + }; + + var toInteger$1 = toInteger$b; + var requireObjectCoercible$6 = requireObjectCoercible$e; + + // `String.prototype.repeat` method implementation + // https://tc39.es/ecma262/#sec-string.prototype.repeat + var stringRepeat = function repeat(count) { + var str = String(requireObjectCoercible$6(this)); + var result = ''; + var n = toInteger$1(count); + if (n < 0 || n == Infinity) throw RangeError('Wrong number of repetitions'); + for (;n > 0; (n >>>= 1) && (str += str)) if (n & 1) result += str; + return result; + }; + + var $$j = _export; + var toInteger = toInteger$b; + var thisNumberValue$1 = thisNumberValue$2; + var repeat$2 = stringRepeat; + var fails$7 = fails$N; + + var nativeToFixed = 1.0.toFixed; + var floor = Math.floor; + + var pow = function (x, n, acc) { + return n === 0 ? acc : n % 2 === 1 ? pow(x, n - 1, acc * x) : pow(x * x, n / 2, acc); + }; + + var log = function (x) { + var n = 0; + var x2 = x; + while (x2 >= 4096) { + n += 12; + x2 /= 4096; + } + while (x2 >= 2) { + n += 1; + x2 /= 2; + } return n; + }; + + var multiply = function (data, n, c) { + var index = -1; + var c2 = c; + while (++index < 6) { + c2 += n * data[index]; + data[index] = c2 % 1e7; + c2 = floor(c2 / 1e7); + } + }; + + var divide = function (data, n) { + var index = 6; + var c = 0; + while (--index >= 0) { + c += data[index]; + data[index] = floor(c / n); + c = (c % n) * 1e7; + } + }; + + var dataToString = function (data) { + var index = 6; + var s = ''; + while (--index >= 0) { + if (s !== '' || index === 0 || data[index] !== 0) { + var t = String(data[index]); + s = s === '' ? t : s + repeat$2.call('0', 7 - t.length) + t; + } + } return s; + }; + + var FORCED$4 = nativeToFixed && ( + 0.00008.toFixed(3) !== '0.000' || + 0.9.toFixed(0) !== '1' || + 1.255.toFixed(2) !== '1.25' || + 1000000000000000128.0.toFixed(0) !== '1000000000000000128' + ) || !fails$7(function () { + // V8 ~ Android 4.3- + nativeToFixed.call({}); + }); + + // `Number.prototype.toFixed` method + // https://tc39.es/ecma262/#sec-number.prototype.tofixed + $$j({ target: 'Number', proto: true, forced: FORCED$4 }, { + toFixed: function toFixed(fractionDigits) { + var number = thisNumberValue$1(this); + var fractDigits = toInteger(fractionDigits); + var data = [0, 0, 0, 0, 0, 0]; + var sign = ''; + var result = '0'; + var e, z, j, k; + + if (fractDigits < 0 || fractDigits > 20) throw RangeError('Incorrect fraction digits'); + // eslint-disable-next-line no-self-compare -- NaN check + if (number != number) return 'NaN'; + if (number <= -1e21 || number >= 1e21) return String(number); + if (number < 0) { + sign = '-'; + number = -number; + } + if (number > 1e-21) { + e = log(number * pow(2, 69, 1)) - 69; + z = e < 0 ? number * pow(2, -e, 1) : number / pow(2, e, 1); + z *= 0x10000000000000; + e = 52 - e; + if (e > 0) { + multiply(data, 0, z); + j = fractDigits; + while (j >= 7) { + multiply(data, 1e7, 0); + j -= 7; + } + multiply(data, pow(10, j, 1), 0); + j = e - 1; + while (j >= 23) { + divide(data, 1 << 23); + j -= 23; + } + divide(data, 1 << j); + multiply(data, 1, 1); + divide(data, 2); + result = dataToString(data); + } else { + multiply(data, 0, z); + multiply(data, 1 << -e, 0); + result = dataToString(data) + repeat$2.call('0', fractDigits); + } + } + if (fractDigits > 0) { + k = result.length; + result = sign + (k <= fractDigits + ? '0.' + repeat$2.call('0', fractDigits - k) + result + : result.slice(0, k - fractDigits) + '.' + result.slice(k - fractDigits)); + } else { + result = sign + result; + } return result; + } + }); + + var global$1 = global$F; + + var globalIsFinite = global$1.isFinite; + + // `Number.isFinite` method + // https://tc39.es/ecma262/#sec-number.isfinite + // eslint-disable-next-line es/no-number-isfinite -- safe + var numberIsFinite$1 = Number.isFinite || function isFinite(it) { + return typeof it == 'number' && globalIsFinite(it); + }; + + var $$i = _export; + var numberIsFinite = numberIsFinite$1; + + // `Number.isFinite` method + // https://tc39.es/ecma262/#sec-number.isfinite + $$i({ target: 'Number', stat: true }, { isFinite: numberIsFinite }); + + var $$h = _export; + var toAbsoluteIndex = toAbsoluteIndex$8; + + var fromCharCode = String.fromCharCode; + // eslint-disable-next-line es/no-string-fromcodepoint -- required for testing + var $fromCodePoint = String.fromCodePoint; + + // length should be 1, old FF problem + var INCORRECT_LENGTH = !!$fromCodePoint && $fromCodePoint.length != 1; + + // `String.fromCodePoint` method + // https://tc39.es/ecma262/#sec-string.fromcodepoint + $$h({ target: 'String', stat: true, forced: INCORRECT_LENGTH }, { + // eslint-disable-next-line no-unused-vars -- required for `.length` + fromCodePoint: function fromCodePoint(x) { + var elements = []; + var length = arguments.length; + var i = 0; + var code; + while (length > i) { + code = +arguments[i++]; + if (toAbsoluteIndex(code, 0x10FFFF) !== code) throw RangeError(code + ' is not a valid code point'); + elements.push(code < 0x10000 + ? fromCharCode(code) + : fromCharCode(((code -= 0x10000) >> 10) + 0xD800, code % 0x400 + 0xDC00) + ); + } return elements.join(''); + } + }); + + var fixRegExpWellKnownSymbolLogic = fixRegexpWellKnownSymbolLogic; + var anObject = anObject$m; + var requireObjectCoercible$5 = requireObjectCoercible$e; + var sameValue = sameValue$1; + var regExpExec = regexpExecAbstract; + + // @@search logic + fixRegExpWellKnownSymbolLogic('search', function (SEARCH, nativeSearch, maybeCallNative) { + return [ + // `String.prototype.search` method + // https://tc39.es/ecma262/#sec-string.prototype.search + function search(regexp) { + var O = requireObjectCoercible$5(this); + var searcher = regexp == undefined ? undefined : regexp[SEARCH]; + return searcher !== undefined ? searcher.call(regexp, O) : new RegExp(regexp)[SEARCH](String(O)); + }, + // `RegExp.prototype[@@search]` method + // https://tc39.es/ecma262/#sec-regexp.prototype-@@search + function (string) { + var res = maybeCallNative(nativeSearch, this, string); + if (res.done) return res.value; + + var rx = anObject(this); + var S = String(string); + + var previousLastIndex = rx.lastIndex; + if (!sameValue(previousLastIndex, 0)) rx.lastIndex = 0; + var result = regExpExec(rx, S); + if (!sameValue(rx.lastIndex, previousLastIndex)) rx.lastIndex = previousLastIndex; + return result === null ? -1 : result.index; + } + ]; + }); + + var rbush$2 = {exports: {}}; + + var quickselect$2 = {exports: {}}; + + (function (module, exports) { + (function (global, factory) { + module.exports = factory() ; + })(commonjsGlobal, function () { + + function quickselect(arr, k, left, right, compare) { + quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare); + } + + function quickselectStep(arr, k, left, right, compare) { + while (right > left) { + if (right - left > 600) { + var n = right - left + 1; + var m = k - left + 1; + var z = Math.log(n); + var s = 0.5 * Math.exp(2 * z / 3); + var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); + var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); + var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); + quickselectStep(arr, k, newLeft, newRight, compare); + } + + var t = arr[k]; + var i = left; + var j = right; + swap(arr, left, k); + if (compare(arr[right], t) > 0) swap(arr, left, right); + + while (i < j) { + swap(arr, i, j); + i++; + j--; + + while (compare(arr[i], t) < 0) { + i++; + } + + while (compare(arr[j], t) > 0) { + j--; + } + } + + if (compare(arr[left], t) === 0) swap(arr, left, j);else { + j++; + swap(arr, j, right); + } + if (j <= k) left = j + 1; + if (k <= j) right = j - 1; + } + } + + function swap(arr, i, j) { + var tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + function defaultCompare(a, b) { + return a < b ? -1 : a > b ? 1 : 0; + } + + return quickselect; + }); + })(quickselect$2); + + rbush$2.exports = rbush$1; + + rbush$2.exports["default"] = rbush$1; + + var quickselect$1 = quickselect$2.exports; + + function rbush$1(maxEntries, format) { + if (!(this instanceof rbush$1)) return new rbush$1(maxEntries, format); // max entries in a node is 9 by default; min node fill is 40% for best performance + + this._maxEntries = Math.max(4, maxEntries || 9); + this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4)); + + if (format) { + this._initFormat(format); + } + + this.clear(); + } + + rbush$1.prototype = { + all: function all() { + return this._all(this.data, []); + }, + search: function search(bbox) { + var node = this.data, + result = [], + toBBox = this.toBBox; + if (!intersects$1(bbox, node)) return result; + var nodesToSearch = [], + i, + len, + child, + childBBox; + + while (node) { + for (i = 0, len = node.children.length; i < len; i++) { + child = node.children[i]; + childBBox = node.leaf ? toBBox(child) : child; + + if (intersects$1(bbox, childBBox)) { + if (node.leaf) result.push(child);else if (contains$1(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child); + } + } + + node = nodesToSearch.pop(); + } + + return result; + }, + collides: function collides(bbox) { + var node = this.data, + toBBox = this.toBBox; + if (!intersects$1(bbox, node)) return false; + var nodesToSearch = [], + i, + len, + child, + childBBox; + + while (node) { + for (i = 0, len = node.children.length; i < len; i++) { + child = node.children[i]; + childBBox = node.leaf ? toBBox(child) : child; + + if (intersects$1(bbox, childBBox)) { + if (node.leaf || contains$1(bbox, childBBox)) return true; + nodesToSearch.push(child); + } + } + + node = nodesToSearch.pop(); + } + + return false; + }, + load: function load(data) { + if (!(data && data.length)) return this; + + if (data.length < this._minEntries) { + for (var i = 0, len = data.length; i < len; i++) { + this.insert(data[i]); + } + + return this; + } // recursively build the tree with the given data from scratch using OMT algorithm + + + var node = this._build(data.slice(), 0, data.length - 1, 0); + + if (!this.data.children.length) { + // save as is if tree is empty + this.data = node; + } else if (this.data.height === node.height) { + // split root if trees have the same height + this._splitRoot(this.data, node); + } else { + if (this.data.height < node.height) { + // swap trees if inserted one is bigger + var tmpNode = this.data; + this.data = node; + node = tmpNode; + } // insert the small tree into the large tree at appropriate level + + + this._insert(node, this.data.height - node.height - 1, true); + } + + return this; + }, + insert: function insert(item) { + if (item) this._insert(item, this.data.height - 1); + return this; + }, + clear: function clear() { + this.data = createNode$1([]); + return this; + }, + remove: function remove(item, equalsFn) { + if (!item) return this; + var node = this.data, + bbox = this.toBBox(item), + path = [], + indexes = [], + i, + parent, + index, + goingUp; // depth-first iterative tree traversal + + while (node || path.length) { + if (!node) { + // go up + node = path.pop(); + parent = path[path.length - 1]; + i = indexes.pop(); + goingUp = true; + } + + if (node.leaf) { + // check current node + index = findItem$1(item, node.children, equalsFn); + + if (index !== -1) { + // item found, remove the item and condense tree upwards + node.children.splice(index, 1); + path.push(node); + + this._condense(path); + + return this; + } + } + + if (!goingUp && !node.leaf && contains$1(node, bbox)) { + // go down + path.push(node); + indexes.push(i); + i = 0; + parent = node; + node = node.children[0]; + } else if (parent) { + // go right + i++; + node = parent.children[i]; + goingUp = false; + } else node = null; // nothing found + + } + + return this; + }, + toBBox: function toBBox(item) { + return item; + }, + compareMinX: compareNodeMinX$1, + compareMinY: compareNodeMinY$1, + toJSON: function toJSON() { + return this.data; + }, + fromJSON: function fromJSON(data) { + this.data = data; + return this; + }, + _all: function _all(node, result) { + var nodesToSearch = []; + + while (node) { + if (node.leaf) result.push.apply(result, node.children);else nodesToSearch.push.apply(nodesToSearch, node.children); + node = nodesToSearch.pop(); + } + + return result; + }, + _build: function _build(items, left, right, height) { + var N = right - left + 1, + M = this._maxEntries, + node; + + if (N <= M) { + // reached leaf level; return leaf + node = createNode$1(items.slice(left, right + 1)); + calcBBox$1(node, this.toBBox); + return node; + } + + if (!height) { + // target height of the bulk-loaded tree + height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization + + M = Math.ceil(N / Math.pow(M, height - 1)); + } + + node = createNode$1([]); + node.leaf = false; + node.height = height; // split the items into M mostly square tiles + + var N2 = Math.ceil(N / M), + N1 = N2 * Math.ceil(Math.sqrt(M)), + i, + j, + right2, + right3; + multiSelect$1(items, left, right, N1, this.compareMinX); + + for (i = left; i <= right; i += N1) { + right2 = Math.min(i + N1 - 1, right); + multiSelect$1(items, i, right2, N2, this.compareMinY); + + for (j = i; j <= right2; j += N2) { + right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively + + node.children.push(this._build(items, j, right3, height - 1)); + } + } + + calcBBox$1(node, this.toBBox); + return node; + }, + _chooseSubtree: function _chooseSubtree(bbox, node, level, path) { + var i, len, child, targetNode, area, enlargement, minArea, minEnlargement; + + while (true) { + path.push(node); + if (node.leaf || path.length - 1 === level) break; + minArea = minEnlargement = Infinity; + + for (i = 0, len = node.children.length; i < len; i++) { + child = node.children[i]; + area = bboxArea$1(child); + enlargement = enlargedArea$1(bbox, child) - area; // choose entry with the least area enlargement + + if (enlargement < minEnlargement) { + minEnlargement = enlargement; + minArea = area < minArea ? area : minArea; + targetNode = child; + } else if (enlargement === minEnlargement) { + // otherwise choose one with the smallest area + if (area < minArea) { + minArea = area; + targetNode = child; + } + } + } + + node = targetNode || node.children[0]; + } + + return node; + }, + _insert: function _insert(item, level, isNode) { + var toBBox = this.toBBox, + bbox = isNode ? item : toBBox(item), + insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too + + var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node + + + node.children.push(item); + extend$2(node, bbox); // split on node overflow; propagate upwards if necessary + + while (level >= 0) { + if (insertPath[level].children.length > this._maxEntries) { + this._split(insertPath, level); + + level--; + } else break; + } // adjust bboxes along the insertion path + + + this._adjustParentBBoxes(bbox, insertPath, level); + }, + // split overflowed node into two + _split: function _split(insertPath, level) { + var node = insertPath[level], + M = node.children.length, + m = this._minEntries; + + this._chooseSplitAxis(node, m, M); + + var splitIndex = this._chooseSplitIndex(node, m, M); + + var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex)); + newNode.height = node.height; + newNode.leaf = node.leaf; + calcBBox$1(node, this.toBBox); + calcBBox$1(newNode, this.toBBox); + if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode); + }, + _splitRoot: function _splitRoot(node, newNode) { + // split root node + this.data = createNode$1([node, newNode]); + this.data.height = node.height + 1; + this.data.leaf = false; + calcBBox$1(this.data, this.toBBox); + }, + _chooseSplitIndex: function _chooseSplitIndex(node, m, M) { + var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index; + minOverlap = minArea = Infinity; + + for (i = m; i <= M - m; i++) { + bbox1 = distBBox$1(node, 0, i, this.toBBox); + bbox2 = distBBox$1(node, i, M, this.toBBox); + overlap = intersectionArea$1(bbox1, bbox2); + area = bboxArea$1(bbox1) + bboxArea$1(bbox2); // choose distribution with minimum overlap + + if (overlap < minOverlap) { + minOverlap = overlap; + index = i; + minArea = area < minArea ? area : minArea; + } else if (overlap === minOverlap) { + // otherwise choose distribution with minimum area + if (area < minArea) { + minArea = area; + index = i; + } + } + } + + return index; + }, + // sorts node children by the best axis for split + _chooseSplitAxis: function _chooseSplitAxis(node, m, M) { + var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1, + compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1, + xMargin = this._allDistMargin(node, m, M, compareMinX), + yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX, + // otherwise it's already sorted by minY + + + if (xMargin < yMargin) node.children.sort(compareMinX); + }, + // total margin of all possible split distributions where each node is at least m full + _allDistMargin: function _allDistMargin(node, m, M, compare) { + node.children.sort(compare); + var toBBox = this.toBBox, + leftBBox = distBBox$1(node, 0, m, toBBox), + rightBBox = distBBox$1(node, M - m, M, toBBox), + margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox), + i, + child; + + for (i = m; i < M - m; i++) { + child = node.children[i]; + extend$2(leftBBox, node.leaf ? toBBox(child) : child); + margin += bboxMargin$1(leftBBox); + } + + for (i = M - m - 1; i >= m; i--) { + child = node.children[i]; + extend$2(rightBBox, node.leaf ? toBBox(child) : child); + margin += bboxMargin$1(rightBBox); + } + + return margin; + }, + _adjustParentBBoxes: function _adjustParentBBoxes(bbox, path, level) { + // adjust bboxes along the given tree path + for (var i = level; i >= 0; i--) { + extend$2(path[i], bbox); + } + }, + _condense: function _condense(path) { + // go through the path, removing empty nodes and updating bboxes + for (var i = path.length - 1, siblings; i >= 0; i--) { + if (path[i].children.length === 0) { + if (i > 0) { + siblings = path[i - 1].children; + siblings.splice(siblings.indexOf(path[i]), 1); + } else this.clear(); + } else calcBBox$1(path[i], this.toBBox); + } + }, + _initFormat: function _initFormat(format) { + // data format (minX, minY, maxX, maxY accessors) + // uses eval-type function compilation instead of just accepting a toBBox function + // because the algorithms are very sensitive to sorting functions performance, + // so they should be dead simple and without inner calls + var compareArr = ['return a', ' - b', ';']; + this.compareMinX = new Function('a', 'b', compareArr.join(format[0])); + this.compareMinY = new Function('a', 'b', compareArr.join(format[1])); + this.toBBox = new Function('a', 'return {minX: a' + format[0] + ', minY: a' + format[1] + ', maxX: a' + format[2] + ', maxY: a' + format[3] + '};'); + } + }; + + function findItem$1(item, items, equalsFn) { + if (!equalsFn) return items.indexOf(item); + + for (var i = 0; i < items.length; i++) { + if (equalsFn(item, items[i])) return i; + } + + return -1; + } // calculate node's bbox from bboxes of its children + + + function calcBBox$1(node, toBBox) { + distBBox$1(node, 0, node.children.length, toBBox, node); + } // min bounding rectangle of node children from k to p-1 + + + function distBBox$1(node, k, p, toBBox, destNode) { + if (!destNode) destNode = createNode$1(null); + destNode.minX = Infinity; + destNode.minY = Infinity; + destNode.maxX = -Infinity; + destNode.maxY = -Infinity; + + for (var i = k, child; i < p; i++) { + child = node.children[i]; + extend$2(destNode, node.leaf ? toBBox(child) : child); + } + + return destNode; + } + + function extend$2(a, b) { + a.minX = Math.min(a.minX, b.minX); + a.minY = Math.min(a.minY, b.minY); + a.maxX = Math.max(a.maxX, b.maxX); + a.maxY = Math.max(a.maxY, b.maxY); + return a; + } + + function compareNodeMinX$1(a, b) { + return a.minX - b.minX; + } + + function compareNodeMinY$1(a, b) { + return a.minY - b.minY; + } + + function bboxArea$1(a) { + return (a.maxX - a.minX) * (a.maxY - a.minY); + } + + function bboxMargin$1(a) { + return a.maxX - a.minX + (a.maxY - a.minY); + } + + function enlargedArea$1(a, b) { + return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY)); + } + + function intersectionArea$1(a, b) { + var minX = Math.max(a.minX, b.minX), + minY = Math.max(a.minY, b.minY), + maxX = Math.min(a.maxX, b.maxX), + maxY = Math.min(a.maxY, b.maxY); + return Math.max(0, maxX - minX) * Math.max(0, maxY - minY); + } + + function contains$1(a, b) { + return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY; + } + + function intersects$1(a, b) { + return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY; + } + + function createNode$1(children) { + return { + children: children, + height: 1, + leaf: true, + minX: Infinity, + minY: Infinity, + maxX: -Infinity, + maxY: -Infinity + }; + } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other; + // combines selection algorithm with binary divide & conquer approach + + + function multiSelect$1(arr, left, right, n, compare) { + var stack = [left, right], + mid; + + while (stack.length) { + right = stack.pop(); + left = stack.pop(); + if (right - left <= n) continue; + mid = left + Math.ceil((right - left) / n / 2) * n; + quickselect$1(arr, mid, left, right, compare); + stack.push(left, mid, mid, right); + } + } + + var lineclip_1 = lineclip$2; + lineclip$2.polyline = lineclip$2; + lineclip$2.polygon = polygonclip$1; // Cohen-Sutherland line clippign algorithm, adapted to efficiently + // handle polylines rather than just segments + + function lineclip$2(points, bbox, result) { + var len = points.length, + codeA = bitCode$1(points[0], bbox), + part = [], + i, + a, + b, + codeB, + lastCode; + if (!result) result = []; + + for (i = 1; i < len; i++) { + a = points[i - 1]; + b = points[i]; + codeB = lastCode = bitCode$1(b, bbox); + + while (true) { + if (!(codeA | codeB)) { + // accept + part.push(a); + + if (codeB !== lastCode) { + // segment went outside + part.push(b); + + if (i < len - 1) { + // start a new line + result.push(part); + part = []; + } + } else if (i === len - 1) { + part.push(b); + } + + break; + } else if (codeA & codeB) { + // trivial reject + break; + } else if (codeA) { + // a outside, intersect with clip edge + a = intersect$1(a, b, codeA, bbox); + codeA = bitCode$1(a, bbox); + } else { + // b outside + b = intersect$1(a, b, codeB, bbox); + codeB = bitCode$1(b, bbox); + } + } + + codeA = lastCode; + } + + if (part.length) result.push(part); + return result; + } // Sutherland-Hodgeman polygon clipping algorithm + + + function polygonclip$1(points, bbox) { + var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle + + for (edge = 1; edge <= 8; edge *= 2) { + result = []; + prev = points[points.length - 1]; + prevInside = !(bitCode$1(prev, bbox) & edge); + + for (i = 0; i < points.length; i++) { + p = points[i]; + inside = !(bitCode$1(p, bbox) & edge); // if segment goes through the clip window, add an intersection + + if (inside !== prevInside) result.push(intersect$1(prev, p, edge, bbox)); + if (inside) result.push(p); // add a point if it's inside + + prev = p; + prevInside = inside; + } + + points = result; + if (!points.length) break; + } + + return result; + } // intersect a segment against one of the 4 lines that make up the bbox + + + function intersect$1(a, b, edge, bbox) { + return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top + edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom + edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right + edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left + null; + } // bit code reflects the point position relative to the bbox: + // left mid right + // top 1001 1000 1010 + // mid 0001 0000 0010 + // bottom 0101 0100 0110 + + + function bitCode$1(p, bbox) { + var code = 0; + if (p[0] < bbox[0]) code |= 1; // left + else if (p[0] > bbox[2]) code |= 2; // right + + if (p[1] < bbox[1]) code |= 4; // bottom + else if (p[1] > bbox[3]) code |= 8; // top + + return code; + } + + var rbush = rbush$2.exports; + var lineclip$1 = lineclip_1; + var whichPolygon_1 = whichPolygon; + + function whichPolygon(data) { + var bboxes = []; + + for (var i = 0; i < data.features.length; i++) { + var feature = data.features[i]; + var coords = feature.geometry.coordinates; + + if (feature.geometry.type === 'Polygon') { + bboxes.push(treeItem(coords, feature.properties)); + } else if (feature.geometry.type === 'MultiPolygon') { + for (var j = 0; j < coords.length; j++) { + bboxes.push(treeItem(coords[j], feature.properties)); + } + } + } + + var tree = rbush().load(bboxes); + + function query(p, multi) { + var output = [], + result = tree.search({ + minX: p[0], + minY: p[1], + maxX: p[0], + maxY: p[1] + }); + + for (var i = 0; i < result.length; i++) { + if (insidePolygon(result[i].coords, p)) { + if (multi) output.push(result[i].props);else return result[i].props; + } + } + + return multi && output.length ? output : null; + } + + query.tree = tree; + + query.bbox = function queryBBox(bbox) { + var output = []; + var result = tree.search({ + minX: bbox[0], + minY: bbox[1], + maxX: bbox[2], + maxY: bbox[3] + }); + + for (var i = 0; i < result.length; i++) { + if (polygonIntersectsBBox(result[i].coords, bbox)) { + output.push(result[i].props); + } + } + + return output; + }; + + return query; + } + + function polygonIntersectsBBox(polygon, bbox) { + var bboxCenter = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2]; + if (insidePolygon(polygon, bboxCenter)) return true; + + for (var i = 0; i < polygon.length; i++) { + if (lineclip$1(polygon[i], bbox).length > 0) return true; + } + + return false; + } // ray casting algorithm for detecting if point is in polygon + + + function insidePolygon(rings, p) { + var inside = false; + + for (var i = 0, len = rings.length; i < len; i++) { + var ring = rings[i]; + + for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) { + if (rayIntersect(p, ring[j], ring[k])) inside = !inside; + } + } + + return inside; + } + + function rayIntersect(p, p1, p2) { + return p1[1] > p[1] !== p2[1] > p[1] && p[0] < (p2[0] - p1[0]) * (p[1] - p1[1]) / (p2[1] - p1[1]) + p1[0]; + } + + function treeItem(coords, props) { + var item = { + minX: Infinity, + minY: Infinity, + maxX: -Infinity, + maxY: -Infinity, + coords: coords, + props: props + }; + + for (var i = 0; i < coords[0].length; i++) { + var p = coords[0][i]; + item.minX = Math.min(item.minX, p[0]); + item.minY = Math.min(item.minY, p[1]); + item.maxX = Math.max(item.maxX, p[0]); + item.maxY = Math.max(item.maxY, p[1]); + } + + return item; + } + + var type = "FeatureCollection"; + var features = [{ + type: "Feature", + properties: { + wikidata: "Q21", + nameEn: "England", + aliases: ["GB-ENG"], + country: "GB", + groups: ["Q23666", "Q3336843", "154", "150", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["44"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q22", + nameEn: "Scotland", + aliases: ["GB-SCT"], + country: "GB", + groups: ["Q23666", "Q3336843", "154", "150", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["44"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q25", + nameEn: "Wales", + aliases: ["GB-WLS"], + country: "GB", + groups: ["Q23666", "Q3336843", "154", "150", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["44"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q26", + nameEn: "Northern Ireland", + aliases: ["GB-NIR"], + country: "GB", + groups: ["Q22890", "Q3336843", "154", "150", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["44"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q35", + nameEn: "Denmark", + country: "DK", + groups: ["EU", "154", "150", "UN"], + callingCodes: ["45"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[12.16597, 56.60205], [10.40861, 58.38489], [7.28637, 57.35913], [8.02459, 55.09613], [8.45719, 55.06747], [8.55769, 54.91837], [8.63979, 54.91069], [8.76387, 54.8948], [8.81178, 54.90518], [8.92795, 54.90452], [9.04629, 54.87249], [9.14275, 54.87421], [9.20571, 54.85841], [9.24631, 54.84726], [9.23445, 54.83432], [9.2474, 54.8112], [9.32771, 54.80602], [9.33849, 54.80233], [9.36496, 54.81749], [9.38532, 54.83968], [9.41213, 54.84254], [9.43155, 54.82586], [9.4659, 54.83131], [9.58937, 54.88785], [9.62734, 54.88057], [9.61187, 54.85548], [9.73563, 54.8247], [9.89314, 54.84171], [10.16755, 54.73883], [10.31111, 54.65968], [11.00303, 54.63689], [11.90309, 54.38543], [12.85844, 54.82438], [13.93395, 54.84044], [15.36991, 54.73263], [15.79951, 55.54655], [14.89259, 55.5623], [14.28399, 55.1553], [12.84405, 55.13257], [12.60345, 55.42675], [12.88472, 55.63369], [12.6372, 55.91371], [12.65312, 56.04345], [12.07466, 56.29488], [12.16597, 56.60205]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q55", + nameEn: "Netherlands", + country: "NL", + groups: ["EU", "155", "150", "UN"], + callingCodes: ["31"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[5.45168, 54.20039], [2.56575, 51.85301], [3.36263, 51.37112], [3.38696, 51.33436], [3.35847, 51.31572], [3.38289, 51.27331], [3.41704, 51.25933], [3.43488, 51.24135], [3.52698, 51.2458], [3.51502, 51.28697], [3.58939, 51.30064], [3.78999, 51.25766], [3.78783, 51.2151], [3.90125, 51.20371], [3.97889, 51.22537], [4.01957, 51.24504], [4.05165, 51.24171], [4.16721, 51.29348], [4.24024, 51.35371], [4.21923, 51.37443], [4.33265, 51.37687], [4.34086, 51.35738], [4.39292, 51.35547], [4.43777, 51.36989], [4.38064, 51.41965], [4.39747, 51.43316], [4.38122, 51.44905], [4.47736, 51.4778], [4.5388, 51.48184], [4.54675, 51.47265], [4.52846, 51.45002], [4.53521, 51.4243], [4.57489, 51.4324], [4.65442, 51.42352], [4.72935, 51.48424], [4.74578, 51.48937], [4.77321, 51.50529], [4.78803, 51.50284], [4.84139, 51.4799], [4.82409, 51.44736], [4.82946, 51.4213], [4.78314, 51.43319], [4.76577, 51.43046], [4.77229, 51.41337], [4.78941, 51.41102], [4.84988, 51.41502], [4.90016, 51.41404], [4.92152, 51.39487], [5.00393, 51.44406], [5.0106, 51.47167], [5.03281, 51.48679], [5.04774, 51.47022], [5.07891, 51.4715], [5.10456, 51.43163], [5.07102, 51.39469], [5.13105, 51.34791], [5.13377, 51.31592], [5.16222, 51.31035], [5.2002, 51.32243], [5.24244, 51.30495], [5.22542, 51.26888], [5.23814, 51.26064], [5.26461, 51.26693], [5.29716, 51.26104], [5.33886, 51.26314], [5.347, 51.27502], [5.41672, 51.26248], [5.4407, 51.28169], [5.46519, 51.2849], [5.48476, 51.30053], [5.515, 51.29462], [5.5569, 51.26544], [5.5603, 51.22249], [5.65145, 51.19788], [5.65528, 51.18736], [5.70344, 51.1829], [5.74617, 51.18928], [5.77735, 51.17845], [5.77697, 51.1522], [5.82564, 51.16753], [5.85508, 51.14445], [5.80798, 51.11661], [5.8109, 51.10861], [5.83226, 51.10585], [5.82921, 51.09328], [5.79903, 51.09371], [5.79835, 51.05834], [5.77258, 51.06196], [5.75961, 51.03113], [5.77688, 51.02483], [5.76242, 50.99703], [5.71864, 50.96092], [5.72875, 50.95428], [5.74752, 50.96202], [5.75927, 50.95601], [5.74644, 50.94723], [5.72545, 50.92312], [5.72644, 50.91167], [5.71626, 50.90796], [5.69858, 50.91046], [5.67886, 50.88142], [5.64504, 50.87107], [5.64009, 50.84742], [5.65259, 50.82309], [5.70118, 50.80764], [5.68995, 50.79641], [5.70107, 50.7827], [5.68091, 50.75804], [5.69469, 50.75529], [5.72216, 50.76398], [5.73904, 50.75674], [5.74356, 50.7691], [5.76533, 50.78159], [5.77513, 50.78308], [5.80673, 50.7558], [5.84548, 50.76542], [5.84888, 50.75448], [5.88734, 50.77092], [5.89129, 50.75125], [5.89132, 50.75124], [5.95942, 50.7622], [5.97545, 50.75441], [6.01976, 50.75398], [6.02624, 50.77453], [5.97497, 50.79992], [5.98404, 50.80988], [6.00462, 50.80065], [6.02328, 50.81694], [6.01921, 50.84435], [6.05623, 50.8572], [6.05702, 50.85179], [6.07431, 50.84674], [6.07693, 50.86025], [6.08805, 50.87223], [6.07486, 50.89307], [6.09297, 50.92066], [6.01615, 50.93367], [6.02697, 50.98303], [5.95282, 50.98728], [5.90296, 50.97356], [5.90493, 51.00198], [5.87849, 51.01969], [5.86735, 51.05182], [5.9134, 51.06736], [5.9541, 51.03496], [5.98292, 51.07469], [6.16706, 51.15677], [6.17384, 51.19589], [6.07889, 51.17038], [6.07889, 51.24432], [6.16977, 51.33169], [6.22674, 51.36135], [6.22641, 51.39948], [6.20654, 51.40049], [6.21724, 51.48568], [6.18017, 51.54096], [6.09055, 51.60564], [6.11759, 51.65609], [6.02767, 51.6742], [6.04091, 51.71821], [5.95003, 51.7493], [5.98665, 51.76944], [5.94568, 51.82786], [5.99848, 51.83195], [6.06705, 51.86136], [6.10337, 51.84829], [6.16902, 51.84094], [6.11551, 51.89769], [6.15349, 51.90439], [6.21443, 51.86801], [6.29872, 51.86801], [6.30593, 51.84998], [6.40704, 51.82771], [6.38815, 51.87257], [6.47179, 51.85395], [6.50231, 51.86313], [6.58556, 51.89386], [6.68386, 51.91861], [6.72319, 51.89518], [6.82357, 51.96711], [6.83035, 51.9905], [6.68128, 52.05052], [6.76117, 52.11895], [6.83984, 52.11728], [6.97189, 52.20329], [6.9897, 52.2271], [7.03729, 52.22695], [7.06365, 52.23789], [7.02703, 52.27941], [7.07044, 52.37805], [7.03417, 52.40237], [6.99041, 52.47235], [6.94293, 52.43597], [6.69507, 52.488], [6.71641, 52.62905], [6.77307, 52.65375], [7.04557, 52.63318], [7.07253, 52.81083], [7.21694, 53.00742], [7.17898, 53.13817], [7.22681, 53.18165], [7.21679, 53.20058], [7.19052, 53.31866], [7.00198, 53.32672], [6.91025, 53.44221], [5.45168, 54.20039]], [[4.93295, 51.44945], [4.95244, 51.45207], [4.9524, 51.45014], [4.93909, 51.44632], [4.93295, 51.44945]], [[4.91493, 51.4353], [4.91935, 51.43634], [4.92227, 51.44252], [4.91811, 51.44621], [4.92287, 51.44741], [4.92811, 51.4437], [4.92566, 51.44273], [4.92815, 51.43856], [4.92879, 51.44161], [4.93544, 51.44634], [4.94025, 51.44193], [4.93416, 51.44185], [4.93471, 51.43861], [4.94265, 51.44003], [4.93986, 51.43064], [4.92952, 51.42984], [4.92652, 51.43329], [4.91493, 51.4353]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q782", + nameEn: "Hawaii", + aliases: ["US-HI"], + country: "US", + groups: ["Q35657", "061", "009", "UN"], + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-177.8563, 29.18961], [-179.49839, 27.86265], [-151.6784, 9.55515], [-154.05867, 45.51124], [-177.5224, 27.7635], [-177.8563, 29.18961]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q797", + nameEn: "Alaska", + aliases: ["US-AK"], + country: "US", + groups: ["Q35657", "021", "003", "019", "UN"], + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q3492", + nameEn: "Sumatra", + aliases: ["ID-SM"], + country: "ID", + groups: ["035", "142", "UN"], + driveSide: "left", + callingCodes: ["62"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q3757", + nameEn: "Java", + aliases: ["ID-JW"], + country: "ID", + groups: ["035", "142", "UN"], + driveSide: "left", + callingCodes: ["62"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q3795", + nameEn: "Kalimantan", + aliases: ["ID-KA"], + country: "ID", + groups: ["Q36117", "035", "142", "UN"], + driveSide: "left", + callingCodes: ["62"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q3803", + nameEn: "Lesser Sunda Islands", + aliases: ["ID-NU"], + country: "ID", + groups: ["035", "142", "UN"], + driveSide: "left", + callingCodes: ["62"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q3812", + nameEn: "Sulawesi", + aliases: ["ID-SL"], + country: "ID", + groups: ["035", "142", "UN"], + driveSide: "left", + callingCodes: ["62"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q3827", + nameEn: "Maluku Islands", + aliases: ["ID-ML"], + country: "ID", + groups: ["035", "142", "UN"], + driveSide: "left", + callingCodes: ["62"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q3845", + nameEn: "Western New Guinea", + aliases: ["ID-PP"], + country: "ID", + groups: ["035", "142", "UN"], + driveSide: "left", + callingCodes: ["62"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q5765", + nameEn: "Balearic Islands", + aliases: ["ES-IB"], + country: "ES", + groups: ["EU", "039", "150", "UN"], + callingCodes: ["34 971"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-2.27707, 35.35051], [5.10072, 39.89531], [3.75438, 42.33445], [-2.27707, 35.35051]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q5823", + nameEn: "Ceuta", + aliases: ["ES-CE"], + country: "ES", + groups: ["EA", "EU", "015", "002", "UN"], + level: "subterritory", + callingCodes: ["34"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q5831", + nameEn: "Melilla", + aliases: ["ES-ML"], + country: "ES", + groups: ["EA", "EU", "015", "002", "UN"], + level: "subterritory", + callingCodes: ["34"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q7835", + nameEn: "Crimea", + country: "RU", + groups: ["151", "150", "UN"], + level: "subterritory", + callingCodes: ["7"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q12837", + nameEn: "Iberia", + level: "sharedLandform" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q14056", + nameEn: "Jan Mayen", + aliases: ["NO-22"], + country: "NO", + groups: ["SJ", "154", "150", "UN"], + level: "subterritory" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-9.18243, 72.23144], [-10.71459, 70.09565], [-5.93364, 70.76368], [-9.18243, 72.23144]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q19188", + nameEn: "Mainland China", + country: "CN", + groups: ["030", "142", "UN"], + callingCodes: ["86"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[125.6131, 53.07229], [125.17522, 53.20225], [124.46078, 53.21881], [123.86158, 53.49391], [123.26989, 53.54843], [122.85966, 53.47395], [122.35063, 53.49565], [121.39213, 53.31888], [120.85633, 53.28499], [120.0451, 52.7359], [120.04049, 52.58773], [120.46454, 52.63811], [120.71673, 52.54099], [120.61346, 52.32447], [120.77337, 52.20805], [120.65907, 51.93544], [120.10963, 51.671], [119.13553, 50.37412], [119.38598, 50.35162], [119.27996, 50.13348], [119.11003, 50.00276], [118.61623, 49.93809], [117.82343, 49.52696], [117.48208, 49.62324], [117.27597, 49.62544], [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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q22890", + nameEn: "Ireland", + level: "sharedLandform" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q23666", + nameEn: "Great Britain", + country: "GB", + level: "sharedLandform" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q23681", + nameEn: "Northern Cyprus", + groups: ["Q644636", "145", "142"], + driveSide: "left", + callingCodes: ["90 392"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q25231", + nameEn: "Svalbard", + aliases: ["NO-21"], + country: "NO", + groups: ["SJ", "154", "150", "UN"], + level: "subterritory", + callingCodes: ["47 79"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-7.49892, 77.24208], [32.07813, 72.01005], [36.85549, 84.09565], [-7.49892, 77.24208]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q25263", + nameEn: "Azores", + aliases: ["PT-20"], + country: "PT", + groups: ["Q3320166", "Q2914565", "Q105472", "EU", "039", "150", "UN"], + callingCodes: ["351"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-23.12984, 40.26428], [-36.43765, 41.39418], [-22.54767, 33.34416], [-23.12984, 40.26428]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q25359", + nameEn: "Navassa Island", + aliases: ["UM-76"], + country: "US", + groups: ["UM", "Q1352230", "029", "003", "419", "019", "UN"], + level: "subterritory", + roadSpeedUnit: "mph", + roadHeightUnit: "ft" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-74.7289, 18.71009], [-75.71816, 18.46438], [-74.76465, 18.06252], [-74.7289, 18.71009]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q25396", + nameEn: "Bonaire", + aliases: ["BQ-BO", "NL-BQ1"], + country: "NL", + groups: ["Q1451600", "BQ", "029", "003", "419", "019", "UN"], + level: "subterritory", + callingCodes: ["599 7"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-67.89186, 12.4116], [-68.90012, 12.62309], [-68.33524, 11.78151], [-68.01417, 11.77722], [-67.89186, 12.4116]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q25528", + nameEn: "Saba", + aliases: ["BQ-SA", "NL-BQ2"], + country: "NL", + groups: ["Q1451600", "BQ", "029", "003", "419", "019", "UN"], + level: "subterritory", + callingCodes: ["599 4"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-63.07669, 17.79659], [-63.81314, 17.95045], [-63.22932, 17.32592], [-63.07669, 17.79659]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q26180", + nameEn: "Sint Eustatius", + aliases: ["BQ-SE", "NL-BQ3"], + country: "NL", + groups: ["Q1451600", "BQ", "029", "003", "419", "019", "UN"], + level: "subterritory", + callingCodes: ["599 3"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-63.07669, 17.79659], [-63.34999, 16.94218], [-62.76692, 17.64353], [-63.07669, 17.79659]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q26253", + nameEn: "Madeira", + aliases: ["PT-30"], + country: "PT", + groups: ["Q3320166", "Q2914565", "Q105472", "EU", "039", "150", "UN"], + callingCodes: ["351"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-19.30302, 33.65304], [-16.04789, 29.65076], [-11.68307, 33.12333], [-19.30302, 33.65304]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q26927", + nameEn: "Lakshadweep", + aliases: ["IN-LD"], + country: "IN", + groups: ["034", "142", "UN"], + driveSide: "left", + callingCodes: ["91"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[67.64074, 11.57295], [76.59015, 5.591], [72.67494, 13.58102], [67.64074, 11.57295]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q27329", + nameEn: "Asian Russia", + country: "RU", + groups: ["142", "UN"], + callingCodes: ["7"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-179.99933, 64.74703], [-172.76104, 63.77445], [-169.03888, 65.48473], [-168.95635, 65.98512], [-168.25765, 71.99091], [-179.9843, 71.90735], [-179.99933, 64.74703]]], [[[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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q34366", + nameEn: "Tasmania", + aliases: ["AU-TAS"], + country: "AU", + groups: ["053", "009", "UN"], + driveSide: "left", + callingCodes: ["61"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[123.64533, -39.13605], [159.69067, -56.28945], [159.74028, -39.1978], [123.64533, -39.13605]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q34497", + nameEn: "Saint Helena", + aliases: ["SH-HL"], + country: "GB", + groups: ["SH", "BOTS", "011", "202", "002", "UN"], + level: "subterritory", + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["290"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-8.3824, -13.9131], [-6.17428, -19.07236], [-3.29308, -15.22647], [-8.3824, -13.9131]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q35657", + nameEn: "US States", + country: "US", + level: "subcountryGroup" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q36117", + nameEn: "Borneo", + level: "sharedLandform" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q36678", + nameEn: "West Bank", + country: "PS", + groups: ["145", "142"], + callingCodes: ["970"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q37362", + nameEn: "Akrotiri and Dhekelia", + aliases: ["SBA"], + country: "GB" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q38095", + nameEn: "Gal\xE1pagos Islands", + aliases: ["EC-W"], + country: "EC", + groups: ["005", "419", "019", "UN"], + callingCodes: ["593"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-93.12365, 2.64343], [-92.46744, -2.52874], [-87.07749, -0.8849], [-93.12365, 2.64343]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q39760", + nameEn: "Gaza Strip", + country: "PS", + groups: ["145", "142"], + callingCodes: ["970"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[34.052, 31.46619], [34.21853, 31.32363], [34.23572, 31.2966], [34.24012, 31.29591], [34.26742, 31.21998], [34.29417, 31.24194], [34.36523, 31.28963], [34.37381, 31.30598], [34.36505, 31.36404], [34.40077, 31.40926], [34.48892, 31.48365], [34.56797, 31.54197], [34.48681, 31.59711], [34.29262, 31.70393], [34.052, 31.46619]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q40888", + nameEn: "Andaman and Nicobar Islands", + aliases: ["IN-AN"], + country: "IN", + groups: ["034", "142", "UN"], + driveSide: "left", + callingCodes: ["91"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[94.42132, 5.96581], [94.6371, 13.81803], [86.7822, 13.41052], [94.42132, 5.96581]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q41684", + nameEn: "Stewart Island", + country: "NZ", + groups: ["053", "009", "UN"], + driveSide: "left", + callingCodes: ["64"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[166.59185, -47.61313], [169.70504, -47.56021], [167.52103, -46.41337], [166.59185, -47.61313]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q43296", + nameEn: "Wake Island", + aliases: ["WK", "WAK", "WKUM", "872", "UM-79"], + country: "US", + groups: ["UM", "Q1352230", "057", "009", "UN"], + level: "subterritory", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[167.34779, 18.97692], [166.67967, 20.14834], [165.82549, 18.97692], [167.34779, 18.97692]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q46275", + nameEn: "New Zealand Subantarctic Islands", + country: "NZ", + groups: ["Q851132", "053", "009", "UN"], + driveSide: "left" + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q46395", + nameEn: "British Overseas Territories", + aliases: ["BOTS", "UKOTS"], + country: "GB", + level: "subcountryGroup" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q46772", + nameEn: "Kerguelen Islands", + country: "FR", + groups: ["TF", "Q1451600", "014", "202", "002", "UN"], + level: "subterritory" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[61.9216, -49.39746], [70.67507, -51.14192], [74.25129, -45.45074], [61.9216, -49.39746]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q46879", + nameEn: "Baker Island", + aliases: ["UM-81"], + country: "US", + groups: ["UM", "Q1352230", "061", "009", "UN"], + level: "subterritory", + roadSpeedUnit: "mph", + roadHeightUnit: "ft" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-175.33482, -1.40631], [-175.31323, 0.5442], [-177.91421, 0.39582], [-175.33482, -1.40631]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q47863", + nameEn: "Midway Atoll", + aliases: ["MI", "MID", "MIUM", "488", "UM-71"], + country: "US", + groups: ["UM", "Q1352230", "061", "009", "UN"], + level: "subterritory", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-176.29741, 29.09786], [-177.77531, 29.29793], [-177.5224, 27.7635], [-176.29741, 29.09786]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q62218", + nameEn: "Jarvis Island", + aliases: ["UM-86"], + country: "US", + groups: ["UM", "Q1352230", "061", "009", "UN"], + level: "subterritory", + roadSpeedUnit: "mph", + roadHeightUnit: "ft" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-160.42921, -1.4364], [-159.12443, 0.19975], [-160.38779, 0.30331], [-160.42921, -1.4364]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q105472", + nameEn: "Macaronesia", + level: "sharedLandform" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q114935", + nameEn: "Kermadec Islands", + country: "NZ", + groups: ["Q851132", "053", "009", "UN"], + driveSide: "left", + callingCodes: ["64"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-174.40891, -29.09438], [-180, -24.21376], [-179.96512, -35.00791], [-174.40891, -29.09438]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q115459", + nameEn: "Chatham Islands", + aliases: ["NZ-CIT"], + country: "NZ", + groups: ["Q851132", "053", "009", "UN"], + driveSide: "left", + callingCodes: ["64"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-179.93224, -45.18423], [-172.47015, -45.17912], [-176.30998, -41.38382], [-179.93224, -45.18423]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q118863", + nameEn: "North Island", + country: "NZ", + groups: ["053", "009", "UN"], + driveSide: "left", + callingCodes: ["64"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q120755", + nameEn: "South Island", + country: "NZ", + groups: ["053", "009", "UN"], + driveSide: "left", + callingCodes: ["64"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q123076", + nameEn: "Palmyra Atoll", + aliases: ["UM-95"], + country: "US", + groups: ["UM", "Q1352230", "061", "009", "UN"], + level: "subterritory", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-161.06795, 5.2462], [-161.0731, 7.1291], [-163.24478, 5.24198], [-161.06795, 5.2462]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q130574", + nameEn: "Chafarinas Islands", + country: "ES", + groups: ["EU", "Q191011", "015", "002", "UN"], + level: "subterritory" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-2.40316, 35.16893], [-2.43262, 35.20652], [-2.45965, 35.16527], [-2.40316, 35.16893]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q130895", + nameEn: "Kingman Reef", + aliases: ["UM-89"], + country: "US", + groups: ["UM", "Q1352230", "061", "009", "UN"], + level: "subterritory", + roadSpeedUnit: "mph", + roadHeightUnit: "ft" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-161.0731, 7.1291], [-163.16627, 7.15036], [-163.24478, 5.24198], [-161.0731, 7.1291]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q131008", + nameEn: "Johnston Atoll", + aliases: ["JT", "JTN", "JTUM", "396", "UM-67"], + country: "US", + groups: ["UM", "Q1352230", "061", "009", "UN"], + level: "subterritory", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-170.65691, 16.57199], [-168.87689, 16.01159], [-169.2329, 17.4933], [-170.65691, 16.57199]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q131305", + nameEn: "Howland Island", + aliases: ["UM-84"], + country: "US", + groups: ["UM", "Q1352230", "061", "009", "UN"], + level: "subterritory", + roadSpeedUnit: "mph", + roadHeightUnit: "ft" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-177.91421, 0.39582], [-175.31323, 0.5442], [-176.74464, 2.28109], [-177.91421, 0.39582]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q133888", + nameEn: "Ashmore and Cartier Islands", + country: "AU", + groups: ["053", "009", "UN"], + driveSide: "left", + callingCodes: ["61"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[123.7463, -11.1783], [120.6877, -13.59408], [125.29076, -12.33139], [123.7463, -11.1783]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q153732", + nameEn: "Mariana Islands", + level: "sharedLandform" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q172216", + nameEn: "Coral Sea Islands", + country: "AU", + groups: ["053", "009", "UN"], + driveSide: "left", + callingCodes: ["61"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q179313", + nameEn: "Alderney", + country: "GB", + groups: ["GG", "830", "Q185086", "154", "150", "UN"], + level: "subterritory", + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["44 01481"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-2.36485, 49.48223], [-2.09454, 49.46288], [-2.02963, 49.91866], [-2.49556, 49.79012], [-2.36485, 49.48223]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q185086", + nameEn: "Crown Dependencies", + country: "GB", + level: "subcountryGroup" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q190571", + nameEn: "Scattered Islands", + country: "FR", + groups: ["TF", "Q1451600", "014", "202", "002", "UN"], + level: "subterritory" + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q191011", + nameEn: "Plazas de soberan\xEDa", + country: "ES" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q191146", + nameEn: "Pe\xF1\xF3n de V\xE9lez de la Gomera", + country: "ES", + groups: ["EU", "Q191011", "015", "002", "UN"], + level: "subterritory" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-4.30191, 35.17419], [-4.30112, 35.17058], [-4.29436, 35.17149], [-4.30191, 35.17419]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q201698", + nameEn: "Crozet Islands", + country: "FR", + groups: ["TF", "Q1451600", "014", "202", "002", "UN"], + level: "subterritory" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[55.03425, -43.65017], [46.31615, -46.28749], [54.5587, -47.93013], [55.03425, -43.65017]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q578170", + nameEn: "Contiguous United States", + aliases: ["CONUS"], + country: "US", + groups: ["Q35657", "021", "003", "019", "UN"], + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q620634", + nameEn: "Bir Tawil", + groups: ["015", "002"], + level: "territory" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[33.17563, 22.00405], [33.57251, 21.72406], [33.99686, 21.76784], [34.0765, 22.00501], [33.17563, 22.00405]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q639185", + nameEn: "Peros Banhos", + country: "GB", + groups: ["IO", "BOTS", "014", "202", "002", "UN"], + level: "subterritory" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[72.12587, -4.02588], [70.1848, -6.37445], [72.09518, -5.61768], [72.12587, -4.02588]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q644636", + nameEn: "Cyprus", + level: "sharedLandform" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q851132", + nameEn: "New Zealand Outlying Islands", + country: "NZ", + level: "subcountryGroup" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q875134", + nameEn: "European Russia", + country: "RU", + groups: ["151", "150", "UN"], + callingCodes: ["7"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q1083368", + nameEn: "Mainland Finland", + country: "FI", + groups: ["EU", "154", "150", "UN"], + callingCodes: ["358"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[29.12697, 69.69193], [28.36883, 69.81658], [28.32849, 69.88605], [27.97558, 69.99671], [27.95542, 70.0965], [27.57226, 70.06215], [27.05802, 69.92069], [26.64461, 69.96565], [26.40261, 69.91377], [25.96904, 69.68397], [25.69679, 69.27039], [25.75729, 68.99383], [25.61613, 68.89602], [25.42455, 68.90328], [25.12206, 68.78684], [25.10189, 68.63307], [24.93048, 68.61102], [24.90023, 68.55579], [24.74898, 68.65143], [24.18432, 68.73936], [24.02299, 68.81601], [23.781, 68.84514], [23.68017, 68.70276], [23.13064, 68.64684], [22.53321, 68.74393], [22.38367, 68.71561], [22.27276, 68.89514], [21.63833, 69.27485], [21.27827, 69.31281], [21.00732, 69.22755], [20.98641, 69.18809], [21.11099, 69.10291], [21.05775, 69.0356], [20.72171, 69.11874], [20.55258, 69.06069], [20.78802, 69.03087], [20.91658, 68.96764], [20.85104, 68.93142], [20.90649, 68.89696], [21.03001, 68.88969], [22.00429, 68.50692], [22.73028, 68.40881], [23.10336, 68.26551], [23.15377, 68.14759], [23.26469, 68.15134], [23.40081, 68.05545], [23.65793, 67.9497], [23.45627, 67.85297], [23.54701, 67.59306], [23.39577, 67.46974], [23.75372, 67.43688], [23.75372, 67.29914], [23.54701, 67.25435], [23.58735, 67.20752], [23.56214, 67.17038], [23.98563, 66.84149], [23.98059, 66.79585], [23.89488, 66.772], [23.85959, 66.56434], [23.63776, 66.43568], [23.67591, 66.3862], [23.64982, 66.30603], [23.71339, 66.21299], [23.90497, 66.15802], [24.15791, 65.85385], [24.14798, 65.83466], [24.15107, 65.81427], [24.14112, 65.39731], [20.15877, 63.06556], [19.23413, 60.61414], [20.96741, 60.71528], [21.15143, 60.54555], [21.08159, 60.20167], [21.02509, 60.12142], [21.35468, 59.67511], [20.5104, 59.15546], [26.32936, 60.00121], [27.44953, 60.22766], [27.71177, 60.3893], [27.77352, 60.52722], [28.47974, 60.93365], [28.82816, 61.1233], [29.01829, 61.17448], [31.10136, 62.43042], [31.38369, 62.66284], [31.58535, 62.91642], [31.29294, 63.09035], [31.23244, 63.22239], [30.49637, 63.46666], [29.98213, 63.75795], [30.25437, 63.83364], [30.55687, 64.09036], [30.4762, 64.25728], [30.06279, 64.35782], [30.01238, 64.57513], [30.12329, 64.64862], [30.05271, 64.79072], [29.68972, 64.80789], [29.61914, 65.05993], [29.84096, 65.1109], [29.8813, 65.22101], [29.61914, 65.23791], [29.68972, 65.31803], [29.84096, 65.56945], [29.74013, 65.64025], [29.97205, 65.70256], [30.16363, 65.66935], [29.91155, 66.13863], [28.9839, 66.94139], [29.91155, 67.51507], [30.02041, 67.67523], [29.66955, 67.79872], [29.34179, 68.06655], [28.62982, 68.19816], [28.43941, 68.53366], [28.78224, 68.86696], [28.45957, 68.91417], [28.91738, 69.04774], [28.81248, 69.11997], [28.8629, 69.22395], [29.31664, 69.47994], [29.12697, 69.69193]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q1184963", + nameEn: "Alhucemas Islands", + country: "ES", + groups: ["EU", "Q191011", "015", "002", "UN"], + level: "subterritory" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-3.90602, 35.21494], [-3.88372, 35.20767], [-3.89343, 35.22728], [-3.90602, 35.21494]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q1298289", + nameEn: "Egmont Islands", + country: "GB", + groups: ["IO", "BOTS", "014", "202", "002", "UN"], + level: "subterritory" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[70.1848, -6.37445], [70.67958, -8.2663], [72.17991, -6.68509], [70.1848, -6.37445]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q1352230", + nameEn: "US Territories", + country: "US", + level: "subcountryGroup" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q1451600", + nameEn: "Overseas Countries and Territories of the EU", + aliases: ["OCT"], + level: "subunion" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q1544253", + nameEn: "Great Chagos Bank", + country: "GB", + groups: ["IO", "BOTS", "014", "202", "002", "UN"], + level: "subterritory" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[70.1848, -6.37445], [72.17991, -6.68509], [73.20573, -5.20727], [70.1848, -6.37445]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q1585511", + nameEn: "Salomon Atoll", + country: "GB", + groups: ["IO", "BOTS", "014", "202", "002", "UN"], + level: "subterritory" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[72.09518, -5.61768], [73.20573, -5.20727], [72.12587, -4.02588], [72.09518, -5.61768]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q1681727", + nameEn: "Saint-Paul and Amsterdam", + country: "FR", + groups: ["TF", "Q1451600", "014", "202", "002", "UN"], + level: "subterritory" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[76.31747, -42.16264], [80.15867, -36.04977], [71.22311, -38.75287], [76.31747, -42.16264]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q1901211", + nameEn: "East Malaysia", + country: "MY", + groups: ["Q36117", "035", "142", "UN"], + driveSide: "left", + callingCodes: ["60"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q1973345", + nameEn: "Peninsular Malaysia", + country: "MY", + groups: ["035", "142", "UN"], + driveSide: "left", + callingCodes: ["60"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q2093907", + nameEn: "Three Kings Islands", + country: "NZ", + groups: ["Q851132", "053", "009", "UN"], + driveSide: "left" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[174.17679, -32.62487], [170.93268, -32.97889], [171.97383, -34.64644], [174.17679, -32.62487]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q2298216", + nameEn: "Solander Islands", + country: "NZ", + groups: ["Q851132", "053", "009", "UN"], + driveSide: "left" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[167.39068, -46.49187], [166.5534, -46.39484], [166.84561, -46.84889], [167.39068, -46.49187]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q2872203", + nameEn: "Mainland Australia", + country: "AU", + groups: ["053", "009", "UN"], + level: "subcountryGroup", + driveSide: "left", + callingCodes: ["61"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q2914565", + nameEn: "Autonomous Regions of Portugal", + country: "PT", + level: "subcountryGroup" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q2915956", + nameEn: "Mainland Portugal", + country: "PT", + groups: ["Q12837", "EU", "039", "150", "UN"], + level: "subcountryGroup", + callingCodes: ["351"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q3311985", + nameEn: "Guernsey", + country: "GB", + groups: ["GG", "830", "Q185086", "154", "150", "UN"], + level: "subterritory", + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["44 01481"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-2.49556, 49.79012], [-3.28154, 49.57329], [-2.65349, 49.15373], [-2.36485, 49.48223], [-2.49556, 49.79012]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q3320166", + nameEn: "Outermost Regions of the EU", + aliases: ["OMR"], + level: "subunion" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q3336843", + nameEn: "Countries of the United Kingdom", + country: "GB", + level: "subcountryGroup" + }, + geometry: null + }, { + type: "Feature", + properties: { + wikidata: "Q6736667", + nameEn: "Mainland India", + country: "IN", + groups: ["034", "142", "UN"], + driveSide: "left", + callingCodes: ["91"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q9143535", + nameEn: "Akrotiri", + country: "GB", + groups: ["Q644636", "Q37362", "BOTS", "145", "142", "UN"], + level: "subterritory", + driveSide: "left", + callingCodes: ["357"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q9206745", + nameEn: "Dhekelia", + country: "GB", + groups: ["Q644636", "Q37362", "BOTS", "145", "142", "UN"], + level: "subterritory", + driveSide: "left", + callingCodes: ["357"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q16390686", + nameEn: "Peninsular Spain", + country: "ES", + groups: ["Q12837", "EU", "039", "150", "UN"], + callingCodes: ["34"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q98059339", + nameEn: "Mainland Norway", + country: "NO", + groups: ["154", "150", "UN"], + callingCodes: ["47"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[10.40861, 58.38489], [10.64958, 58.89391], [11.08911, 58.98745], [11.15367, 59.07862], [11.34459, 59.11672], [11.4601, 58.99022], [11.45199, 58.89604], [11.65732, 58.90177], [11.8213, 59.24985], [11.69297, 59.59442], [11.92112, 59.69531], [11.87121, 59.86039], [12.15641, 59.8926], [12.36317, 59.99259], [12.52003, 60.13846], [12.59133, 60.50559], [12.2277, 61.02442], [12.69115, 61.06584], [12.86939, 61.35427], [12.57707, 61.56547], [12.40595, 61.57226], [12.14746, 61.7147], [12.29187, 62.25699], [12.07085, 62.6297], [12.19919, 63.00104], [11.98529, 63.27487], [12.19919, 63.47935], [12.14928, 63.59373], [12.74105, 64.02171], [13.23411, 64.09087], [13.98222, 64.00953], [14.16051, 64.18725], [14.11117, 64.46674], [13.64276, 64.58402], [14.50926, 65.31786], [14.53778, 66.12399], [15.05113, 66.15572], [15.49318, 66.28509], [15.37197, 66.48217], [16.35589, 67.06419], [16.39154, 67.21653], [16.09922, 67.4364], [16.12774, 67.52106], [16.38441, 67.52923], [16.7409, 67.91037], [17.30416, 68.11591], [17.90787, 67.96537], [18.13836, 68.20874], [18.1241, 68.53721], [18.39503, 68.58672], [18.63032, 68.50849], [18.97255, 68.52416], [19.93508, 68.35911], [20.22027, 68.48759], [19.95647, 68.55546], [20.22027, 68.67246], [20.33435, 68.80174], [20.28444, 68.93283], [20.0695, 69.04469], [20.55258, 69.06069], [20.72171, 69.11874], [21.05775, 69.0356], [21.11099, 69.10291], [20.98641, 69.18809], [21.00732, 69.22755], [21.27827, 69.31281], [21.63833, 69.27485], [22.27276, 68.89514], [22.38367, 68.71561], [22.53321, 68.74393], [23.13064, 68.64684], [23.68017, 68.70276], [23.781, 68.84514], [24.02299, 68.81601], [24.18432, 68.73936], [24.74898, 68.65143], [24.90023, 68.55579], [24.93048, 68.61102], [25.10189, 68.63307], [25.12206, 68.78684], [25.42455, 68.90328], [25.61613, 68.89602], [25.75729, 68.99383], [25.69679, 69.27039], [25.96904, 69.68397], [26.40261, 69.91377], [26.64461, 69.96565], [27.05802, 69.92069], [27.57226, 70.06215], [27.95542, 70.0965], [27.97558, 69.99671], [28.32849, 69.88605], [28.36883, 69.81658], [29.12697, 69.69193], [29.31664, 69.47994], [28.8629, 69.22395], [28.81248, 69.11997], [28.91738, 69.04774], [29.0444, 69.0119], [29.26623, 69.13794], [29.27631, 69.2811], [29.97205, 69.41623], [30.16363, 69.65244], [30.52662, 69.54699], [30.95011, 69.54699], [30.84095, 69.80584], [31.59909, 70.16571], [32.07813, 72.01005], [-11.60274, 67.73467], [7.28637, 57.35913], [10.40861, 58.38489]]]] + } + }, { + type: "Feature", + properties: { + wikidata: "Q98543636", + nameEn: "Mainland Ecuador", + country: "EC", + groups: ["005", "419", "019", "UN"], + callingCodes: ["593"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + m49: "001", + wikidata: "Q2", + nameEn: "World", + aliases: ["Earth", "Planet"], + level: "world" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "002", + wikidata: "Q15", + nameEn: "Africa", + level: "region" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "003", + wikidata: "Q49", + nameEn: "North America", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "005", + wikidata: "Q18", + nameEn: "South America", + level: "intermediateRegion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "009", + wikidata: "Q538", + nameEn: "Oceania", + level: "region" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "011", + wikidata: "Q4412", + nameEn: "Western Africa", + level: "intermediateRegion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "013", + wikidata: "Q27611", + nameEn: "Central America", + level: "intermediateRegion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "014", + wikidata: "Q27407", + nameEn: "Eastern Africa", + level: "intermediateRegion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "015", + wikidata: "Q27381", + nameEn: "Northern Africa", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "017", + wikidata: "Q27433", + nameEn: "Middle Africa", + level: "intermediateRegion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "018", + wikidata: "Q27394", + nameEn: "Southern Africa", + level: "intermediateRegion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "019", + wikidata: "Q828", + nameEn: "Americas", + level: "region" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "021", + wikidata: "Q2017699", + nameEn: "Northern America", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "029", + wikidata: "Q664609", + nameEn: "Caribbean", + level: "intermediateRegion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "030", + wikidata: "Q27231", + nameEn: "Eastern Asia", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "034", + wikidata: "Q771405", + nameEn: "Southern Asia", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "035", + wikidata: "Q11708", + nameEn: "South-eastern Asia", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "039", + wikidata: "Q27449", + nameEn: "Southern Europe", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "053", + wikidata: "Q45256", + nameEn: "Australia and New Zealand", + aliases: ["Australasia"], + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "054", + wikidata: "Q37394", + nameEn: "Melanesia", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "057", + wikidata: "Q3359409", + nameEn: "Micronesia", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "061", + wikidata: "Q35942", + nameEn: "Polynesia", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "142", + wikidata: "Q48", + nameEn: "Asia", + level: "region" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "143", + wikidata: "Q27275", + nameEn: "Central Asia", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "145", + wikidata: "Q27293", + nameEn: "Western Asia", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "150", + wikidata: "Q46", + nameEn: "Europe", + level: "region" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "151", + wikidata: "Q27468", + nameEn: "Eastern Europe", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "154", + wikidata: "Q27479", + nameEn: "Northern Europe", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "155", + wikidata: "Q27496", + nameEn: "Western Europe", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "202", + wikidata: "Q132959", + nameEn: "Sub-Saharan Africa", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "419", + wikidata: "Q72829598", + nameEn: "Latin America and the Caribbean", + level: "subregion" + }, + geometry: null + }, { + type: "Feature", + properties: { + m49: "680", + wikidata: "Q3405693", + nameEn: "Sark", + country: "GB", + groups: ["GG", "830", "Q185086", "154", "150", "UN"], + level: "subterritory", + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["44 01481"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-2.36485, 49.48223], [-2.65349, 49.15373], [-2.09454, 49.46288], [-2.36485, 49.48223]]]] + } + }, { + type: "Feature", + properties: { + m49: "830", + wikidata: "Q42314", + nameEn: "Channel Islands", + level: "intermediateRegion" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "AC", + iso1A3: "ASC", + wikidata: "Q46197", + nameEn: "Ascension Island", + aliases: ["SH-AC"], + country: "GB", + groups: ["SH", "BOTS", "011", "202", "002", "UN"], + isoStatus: "excRes", + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["247"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-14.82771, -8.70814], [-13.33271, -8.07391], [-14.91926, -6.63386], [-14.82771, -8.70814]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AD", + iso1A3: "AND", + iso1N3: "020", + wikidata: "Q228", + nameEn: "Andorra", + groups: ["Q12837", "039", "150", "UN"], + callingCodes: ["376"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[1.72515, 42.50338], [1.73683, 42.55492], [1.7858, 42.57698], [1.72588, 42.59098], [1.73452, 42.61515], [1.68267, 42.62533], [1.6625, 42.61982], [1.63485, 42.62957], [1.60085, 42.62703], [1.55418, 42.65669], [1.50867, 42.64483], [1.48043, 42.65203], [1.46718, 42.63296], [1.47986, 42.61346], [1.44197, 42.60217], [1.42512, 42.58292], [1.44529, 42.56722], [1.4234, 42.55959], [1.41245, 42.53539], [1.44759, 42.54431], [1.46661, 42.50949], [1.41648, 42.48315], [1.43838, 42.47848], [1.44529, 42.43724], [1.5127, 42.42959], [1.55073, 42.43299], [1.55937, 42.45808], [1.57953, 42.44957], [1.58933, 42.46275], [1.65674, 42.47125], [1.66826, 42.50779], [1.70571, 42.48867], [1.72515, 42.50338]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AE", + iso1A3: "ARE", + iso1N3: "784", + wikidata: "Q878", + nameEn: "United Arab Emirates", + groups: ["145", "142", "UN"], + callingCodes: ["971"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[56.26534, 25.62825], [56.25341, 25.61443], [56.26636, 25.60643], [56.25365, 25.60211], [56.20473, 25.61119], [56.18363, 25.65508], [56.14826, 25.66351], [56.13579, 25.73524], [56.17416, 25.77239], [56.13963, 25.82765], [56.19334, 25.9795], [56.15498, 26.06828], [56.08666, 26.05038], [55.81777, 26.18798], [55.14145, 25.62624], [53.97892, 24.64436], [52.82259, 25.51697], [52.35509, 25.00368], [52.02277, 24.75635], [51.83108, 24.71675], [51.58834, 24.66608], [51.41644, 24.39615], [51.58871, 24.27256], [51.59617, 24.12041], [52.56622, 22.94341], [55.13599, 22.63334], [55.2137, 22.71065], [55.22634, 23.10378], [55.57358, 23.669], [55.48677, 23.94946], [55.73301, 24.05994], [55.8308, 24.01633], [56.01799, 24.07426], [55.95472, 24.2172], [55.83367, 24.20193], [55.77658, 24.23476], [55.76558, 24.23227], [55.75257, 24.23466], [55.75382, 24.2466], [55.75939, 24.26114], [55.76781, 24.26209], [55.79145, 24.27914], [55.80747, 24.31069], [55.83395, 24.32776], [55.83271, 24.41521], [55.76461, 24.5287], [55.83271, 24.68567], [55.83408, 24.77858], [55.81348, 24.80102], [55.81116, 24.9116], [55.85094, 24.96858], [55.90849, 24.96771], [55.96316, 25.00857], [56.05715, 24.95727], [56.05106, 24.87461], [55.97467, 24.89639], [55.97836, 24.87673], [56.03535, 24.81161], [56.06128, 24.74457], [56.13684, 24.73699], [56.20062, 24.78565], [56.20568, 24.85063], [56.30269, 24.88334], [56.34873, 24.93205], [56.3227, 24.97284], [56.86325, 25.03856], [56.82555, 25.7713], [56.26534, 25.62825]], [[56.26062, 25.33108], [56.3005, 25.31815], [56.3111, 25.30107], [56.35172, 25.30681], [56.34438, 25.26653], [56.27628, 25.23404], [56.24341, 25.22867], [56.20872, 25.24104], [56.20838, 25.25668], [56.24465, 25.27505], [56.25008, 25.28843], [56.23362, 25.31253], [56.26062, 25.33108]]], [[[56.28423, 25.26344], [56.29379, 25.2754], [56.28102, 25.28486], [56.2716, 25.27916], [56.27086, 25.26128], [56.28423, 25.26344]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AF", + iso1A3: "AFG", + iso1N3: "004", + wikidata: "Q889", + nameEn: "Afghanistan", + groups: ["034", "142", "UN"], + callingCodes: ["93"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[70.61526, 38.34774], [70.60407, 38.28046], [70.54673, 38.24541], [70.4898, 38.12546], [70.17206, 37.93276], [70.1863, 37.84296], [70.27694, 37.81258], [70.28243, 37.66706], [70.15015, 37.52519], [69.95971, 37.5659], [69.93362, 37.61378], [69.84435, 37.60616], [69.80041, 37.5746], [69.51888, 37.5844], [69.44954, 37.4869], [69.36645, 37.40462], [69.45022, 37.23315], [69.39529, 37.16752], [69.25152, 37.09426], [69.03274, 37.25174], [68.96407, 37.32603], [68.88168, 37.33368], [68.91189, 37.26704], [68.80889, 37.32494], [68.81438, 37.23862], [68.6798, 37.27906], [68.61851, 37.19815], [68.41888, 37.13906], [68.41201, 37.10402], [68.29253, 37.10621], [68.27605, 37.00977], [68.18542, 37.02074], [68.02194, 36.91923], [67.87917, 37.0591], [67.7803, 37.08978], [67.78329, 37.1834], [67.51868, 37.26102], [67.2581, 37.17216], [67.2224, 37.24545], [67.13039, 37.27168], [67.08232, 37.35469], [66.95598, 37.40162], [66.64699, 37.32958], [66.55743, 37.35409], [66.30993, 37.32409], [65.72274, 37.55438], [65.64137, 37.45061], [65.64263, 37.34388], [65.51778, 37.23881], [64.97945, 37.21913], [64.61141, 36.6351], [64.62514, 36.44311], [64.57295, 36.34362], [64.43288, 36.24401], [64.05385, 36.10433], [63.98519, 36.03773], [63.56496, 35.95106], [63.53475, 35.90881], [63.29579, 35.85985], [63.12276, 35.86208], [63.10318, 35.81782], [63.23262, 35.67487], [63.10079, 35.63024], [63.12276, 35.53196], [63.0898, 35.43131], [62.90853, 35.37086], [62.74098, 35.25432], [62.62288, 35.22067], [62.48006, 35.28796], [62.29878, 35.13312], [62.29191, 35.25964], [62.15871, 35.33278], [62.05709, 35.43803], [61.97743, 35.4604], [61.77693, 35.41341], [61.58742, 35.43803], [61.27371, 35.61482], [61.18187, 35.30249], [61.0991, 35.27845], [61.12831, 35.09938], [61.06926, 34.82139], [61.00197, 34.70631], [60.99922, 34.63064], [60.72316, 34.52857], [60.91321, 34.30411], [60.66502, 34.31539], [60.50209, 34.13992], [60.5838, 33.80793], [60.5485, 33.73422], [60.57762, 33.59772], [60.69573, 33.56054], [60.91133, 33.55596], [60.88908, 33.50219], [60.56485, 33.12944], [60.86191, 32.22565], [60.84541, 31.49561], [61.70929, 31.37391], [61.80569, 31.16167], [61.80957, 31.12576], [61.83257, 31.0452], [61.8335, 30.97669], [61.78268, 30.92724], [61.80829, 30.84224], [60.87231, 29.86514], [62.47751, 29.40782], [63.5876, 29.50456], [64.12966, 29.39157], [64.19796, 29.50407], [64.62116, 29.58903], [65.04005, 29.53957], [66.24175, 29.85181], [66.36042, 29.9583], [66.23609, 30.06321], [66.34869, 30.404], [66.28413, 30.57001], [66.39194, 30.9408], [66.42645, 30.95309], [66.58175, 30.97532], [66.68166, 31.07597], [66.72561, 31.20526], [66.83273, 31.26867], [67.04147, 31.31561], [67.03323, 31.24519], [67.29964, 31.19586], [67.78854, 31.33203], [67.7748, 31.4188], [67.62374, 31.40473], [67.58323, 31.52772], [67.72056, 31.52304], [67.86887, 31.63536], [68.00071, 31.6564], [68.1655, 31.82691], [68.25614, 31.80357], [68.27605, 31.75863], [68.44222, 31.76446], [68.57475, 31.83158], [68.6956, 31.75687], [68.79997, 31.61665], [68.91078, 31.59687], [68.95995, 31.64822], [69.00939, 31.62249], [69.11514, 31.70782], [69.20577, 31.85957], [69.3225, 31.93186], [69.27032, 32.14141], [69.27932, 32.29119], [69.23599, 32.45946], [69.2868, 32.53938], [69.38155, 32.56601], [69.44747, 32.6678], [69.43649, 32.7302], [69.38018, 32.76601], [69.47082, 32.85834], [69.5436, 32.8768], [69.49854, 32.88843], [69.49004, 33.01509], [69.57656, 33.09911], [69.71526, 33.09911], [69.79766, 33.13247], [69.85259, 33.09451], [70.02563, 33.14282], [70.07369, 33.22557], [70.13686, 33.21064], [70.32775, 33.34496], [70.17062, 33.53535], [70.20141, 33.64387], [70.14785, 33.6553], [70.14236, 33.71701], [70.00503, 33.73528], [69.85671, 33.93719], [69.87307, 33.9689], [69.90203, 34.04194], [70.54336, 33.9463], [70.88119, 33.97933], [71.07345, 34.06242], [71.06933, 34.10564], [71.09307, 34.11961], [71.09453, 34.13524], [71.13078, 34.16503], [71.12815, 34.26619], [71.17662, 34.36769], [71.02401, 34.44835], [71.0089, 34.54568], [71.11602, 34.63047], [71.08718, 34.69034], [71.28356, 34.80882], [71.29472, 34.87728], [71.50329, 34.97328], [71.49917, 35.00478], [71.55273, 35.02615], [71.52938, 35.09023], [71.67495, 35.21262], [71.5541, 35.28776], [71.54294, 35.31037], [71.65435, 35.4479], [71.49917, 35.6267], [71.55273, 35.71483], [71.37969, 35.95865], [71.19505, 36.04134], [71.60491, 36.39429], [71.80267, 36.49924], [72.18135, 36.71838], [72.6323, 36.84601], [73.82685, 36.91421], [74.04856, 36.82648], [74.43389, 37.00977], [74.53739, 36.96224], [74.56453, 37.03023], [74.49981, 37.24518], [74.80605, 37.21565], [74.88887, 37.23275], [74.8294, 37.3435], [74.68383, 37.3948], [74.56161, 37.37734], [74.41055, 37.3948], [74.23339, 37.41116], [74.20308, 37.34208], [73.8564, 37.26158], [73.82552, 37.22659], [73.64974, 37.23643], [73.61129, 37.27469], [73.76647, 37.33913], [73.77197, 37.4417], [73.29633, 37.46495], [73.06884, 37.31729], [72.79693, 37.22222], [72.66381, 37.02014], [72.54095, 37.00007], [72.31676, 36.98115], [71.83229, 36.68084], [71.67083, 36.67346], [71.57195, 36.74943], [71.51502, 36.89128], [71.48481, 36.93218], [71.46923, 36.99925], [71.45578, 37.03094], [71.43097, 37.05855], [71.44127, 37.11856], [71.4494, 37.18137], [71.4555, 37.21418], [71.47386, 37.2269], [71.48339, 37.23937], [71.4824, 37.24921], [71.48536, 37.26017], [71.50674, 37.31502], [71.49821, 37.31975], [71.4862, 37.33405], [71.47685, 37.40281], [71.49612, 37.4279], [71.5256, 37.47971], [71.50616, 37.50733], [71.49693, 37.53527], [71.5065, 37.60912], [71.51972, 37.61945], [71.54186, 37.69691], [71.55234, 37.73209], [71.53053, 37.76534], [71.54324, 37.77104], [71.55752, 37.78677], [71.59255, 37.79956], [71.58843, 37.92425], [71.51565, 37.95349], [71.32871, 37.88564], [71.296, 37.93403], [71.2809, 37.91995], [71.24969, 37.93031], [71.27278, 37.96496], [71.27622, 37.99946], [71.28922, 38.01272], [71.29878, 38.04429], [71.36444, 38.15358], [71.37803, 38.25641], [71.33869, 38.27335], [71.33114, 38.30339], [71.21291, 38.32797], [71.1451, 38.40106], [71.10957, 38.40671], [71.10592, 38.42077], [71.09542, 38.42517], [71.0556, 38.40176], [71.03545, 38.44779], [70.98693, 38.48862], [70.92728, 38.43021], [70.88719, 38.46826], [70.84376, 38.44688], [70.82538, 38.45394], [70.81697, 38.44507], [70.80521, 38.44447], [70.79766, 38.44944], [70.78702, 38.45031], [70.78581, 38.45502], [70.77132, 38.45548], [70.75455, 38.4252], [70.72485, 38.4131], [70.69807, 38.41861], [70.67438, 38.40597], [70.6761, 38.39144], [70.69189, 38.37031], [70.64966, 38.34999], [70.61526, 38.34774]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AG", + iso1A3: "ATG", + iso1N3: "028", + wikidata: "Q781", + nameEn: "Antigua and Barbuda", + groups: ["029", "003", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + callingCodes: ["1 268"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-61.66959, 18.6782], [-62.58307, 16.68909], [-62.1023, 16.97277], [-61.23098, 16.62484], [-61.66959, 18.6782]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AI", + iso1A3: "AIA", + iso1N3: "660", + wikidata: "Q25228", + nameEn: "Anguilla", + country: "GB", + groups: ["BOTS", "029", "003", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + callingCodes: ["1 264"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-63.79029, 19.11219], [-63.35989, 18.06012], [-62.62718, 18.26185], [-63.79029, 19.11219]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AL", + iso1A3: "ALB", + iso1N3: "008", + wikidata: "Q222", + nameEn: "Albania", + groups: ["039", "150", "UN"], + callingCodes: ["355"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[20.07761, 42.55582], [20.01834, 42.54622], [20.00842, 42.5109], [19.9324, 42.51699], [19.82333, 42.46581], [19.76549, 42.50237], [19.74731, 42.57422], [19.77375, 42.58517], [19.73244, 42.66299], [19.65972, 42.62774], [19.4836, 42.40831], [19.42352, 42.36546], [19.42, 42.33019], [19.28623, 42.17745], [19.40687, 42.10024], [19.37548, 42.06835], [19.36867, 42.02564], [19.37691, 41.96977], [19.34601, 41.95675], [19.33812, 41.90669], [19.37451, 41.8842], [19.37597, 41.84849], [19.26406, 41.74971], [19.0384, 40.35325], [19.95905, 39.82857], [19.97622, 39.78684], [19.92466, 39.69533], [19.98042, 39.6504], [20.00957, 39.69227], [20.05189, 39.69112], [20.12956, 39.65805], [20.15988, 39.652], [20.22376, 39.64532], [20.22707, 39.67459], [20.27412, 39.69884], [20.31961, 39.72799], [20.29152, 39.80421], [20.30804, 39.81563], [20.38572, 39.78516], [20.41475, 39.81437], [20.41546, 39.82832], [20.31135, 39.99438], [20.37911, 39.99058], [20.42373, 40.06777], [20.48487, 40.06271], [20.51297, 40.08168], [20.55593, 40.06524], [20.61081, 40.07866], [20.62566, 40.0897], [20.67162, 40.09433], [20.71789, 40.27739], [20.78234, 40.35803], [20.7906, 40.42726], [20.83688, 40.47882], [20.94925, 40.46625], [20.96908, 40.51526], [21.03932, 40.56299], [21.05833, 40.66586], [20.98134, 40.76046], [20.95752, 40.76982], [20.98396, 40.79109], [20.97887, 40.85475], [20.97693, 40.90103], [20.94305, 40.92399], [20.83671, 40.92752], [20.81567, 40.89662], [20.73504, 40.9081], [20.71634, 40.91781], [20.65558, 41.08009], [20.63454, 41.0889], [20.59832, 41.09066], [20.58546, 41.11179], [20.59715, 41.13644], [20.51068, 41.2323], [20.49432, 41.33679], [20.52119, 41.34381], [20.55976, 41.4087], [20.51301, 41.442], [20.49039, 41.49277], [20.45331, 41.51436], [20.45809, 41.5549], [20.52103, 41.56473], [20.55508, 41.58113], [20.51769, 41.65975], [20.52937, 41.69292], [20.51301, 41.72433], [20.53405, 41.78099], [20.57144, 41.7897], [20.55976, 41.87068], [20.59524, 41.8818], [20.57946, 41.91593], [20.63069, 41.94913], [20.59434, 42.03879], [20.55633, 42.08173], [20.56955, 42.12097], [20.48857, 42.25444], [20.3819, 42.3029], [20.34479, 42.32656], [20.24399, 42.32168], [20.21797, 42.41237], [20.17127, 42.50469], [20.07761, 42.55582]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AM", + iso1A3: "ARM", + iso1N3: "051", + wikidata: "Q399", + nameEn: "Armenia", + groups: ["145", "142", "UN"], + callingCodes: ["374"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[45.0133, 41.29747], [44.93493, 41.25685], [44.81437, 41.30371], [44.80053, 41.25949], [44.81749, 41.23488], [44.84358, 41.23088], [44.89911, 41.21366], [44.87887, 41.20195], [44.82084, 41.21513], [44.72814, 41.20338], [44.61462, 41.24018], [44.59322, 41.1933], [44.46791, 41.18204], [44.34417, 41.2382], [44.34337, 41.20312], [44.32139, 41.2079], [44.18148, 41.24644], [44.16591, 41.19141], [43.84835, 41.16329], [43.74717, 41.1117], [43.67712, 41.13398], [43.4717, 41.12611], [43.44984, 41.0988], [43.47319, 41.02251], [43.58683, 40.98961], [43.67712, 40.93084], [43.67712, 40.84846], [43.74872, 40.7365], [43.7425, 40.66805], [43.63664, 40.54159], [43.54791, 40.47413], [43.60862, 40.43267], [43.59928, 40.34019], [43.71136, 40.16673], [43.65221, 40.14889], [43.65688, 40.11199], [43.92307, 40.01787], [44.1057, 40.03555], [44.1778, 40.02845], [44.26973, 40.04866], [44.46635, 39.97733], [44.61845, 39.8281], [44.75779, 39.7148], [44.88354, 39.74432], [44.92869, 39.72157], [45.06604, 39.79277], [45.18554, 39.67846], [45.17464, 39.58614], [45.21784, 39.58074], [45.23535, 39.61373], [45.30385, 39.61373], [45.29606, 39.57654], [45.46992, 39.49888], [45.70547, 39.60174], [45.80804, 39.56716], [45.83, 39.46487], [45.79225, 39.3695], [45.99774, 39.28931], [46.02303, 39.09978], [46.06973, 39.0744], [46.14785, 38.84206], [46.20601, 38.85262], [46.34059, 38.92076], [46.53497, 38.86548], [46.51805, 38.94982], [46.54296, 39.07078], [46.44022, 39.19636], [46.52584, 39.18912], [46.54141, 39.15895], [46.58032, 39.21204], [46.63481, 39.23013], [46.56476, 39.24942], [46.50093, 39.33736], [46.43244, 39.35181], [46.37795, 39.42039], [46.4013, 39.45405], [46.53051, 39.47809], [46.51027, 39.52373], [46.57721, 39.54414], [46.57098, 39.56694], [46.52117, 39.58734], [46.42465, 39.57534], [46.40286, 39.63651], [46.18493, 39.60533], [45.96543, 39.78859], [45.82533, 39.82925], [45.7833, 39.9475], [45.60895, 39.97733], [45.59806, 40.0131], [45.78642, 40.03218], [45.83779, 39.98925], [45.97944, 40.181], [45.95609, 40.27846], [45.65098, 40.37696], [45.42994, 40.53804], [45.45484, 40.57707], [45.35366, 40.65979], [45.4206, 40.7424], [45.55914, 40.78366], [45.60584, 40.87436], [45.40814, 40.97904], [45.44083, 41.01663], [45.39725, 41.02603], [45.35677, 40.99784], [45.28859, 41.03757], [45.26162, 41.0228], [45.25897, 41.0027], [45.1994, 41.04518], [45.16493, 41.05068], [45.1634, 41.08082], [45.1313, 41.09369], [45.12923, 41.06059], [45.06784, 41.05379], [45.08028, 41.10917], [45.19942, 41.13299], [45.1969, 41.168], [45.11811, 41.19967], [45.05201, 41.19211], [45.02932, 41.2101], [45.05497, 41.2464], [45.0133, 41.29747]], [[45.21324, 40.9817], [45.21219, 40.99001], [45.20518, 40.99348], [45.19312, 40.98998], [45.18382, 41.0066], [45.20625, 41.01484], [45.23487, 41.00226], [45.23095, 40.97828], [45.21324, 40.9817]], [[45.00864, 41.03411], [44.9903, 41.05657], [44.96031, 41.06345], [44.95383, 41.07553], [44.97169, 41.09176], [45.00864, 41.09407], [45.03406, 41.07931], [45.04517, 41.06653], [45.03792, 41.03938], [45.00864, 41.03411]]], [[[45.50279, 40.58424], [45.56071, 40.64765], [45.51825, 40.67382], [45.47927, 40.65023], [45.50279, 40.58424]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AO", + iso1A3: "AGO", + iso1N3: "024", + wikidata: "Q916", + nameEn: "Angola", + groups: ["017", "202", "002", "UN"], + callingCodes: ["244"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[16.55507, -5.85631], [13.04371, -5.87078], [12.42245, -6.07585], [11.95767, -5.94705], [12.20376, -5.76338], [12.26557, -5.74031], [12.52318, -5.74353], [12.52301, -5.17481], [12.53599, -5.1618], [12.53586, -5.14658], [12.51589, -5.1332], [12.49815, -5.14058], [12.46297, -5.09408], [12.60251, -5.01715], [12.63465, -4.94632], [12.70868, -4.95505], [12.8733, -4.74346], [13.11195, -4.67745], [13.09648, -4.63739], [12.91489, -4.47907], [12.87096, -4.40315], [12.76844, -4.38709], [12.64835, -4.55937], [12.40964, -4.60609], [12.32324, -4.78415], [12.25587, -4.79437], [12.20901, -4.75642], [12.16068, -4.90089], [12.00924, -5.02627], [11.50888, -5.33417], [10.5065, -17.25284], [11.75063, -17.25013], [12.07076, -17.15165], [12.52111, -17.24495], [12.97145, -16.98567], [13.36212, -16.98048], [13.95896, -17.43141], [14.28743, -17.38814], [18.39229, -17.38927], [18.84226, -17.80375], [21.14283, -17.94318], [21.42741, -18.02787], [23.47474, -17.62877], [23.20038, -17.47563], [22.17217, -16.50269], [22.00323, -16.18028], [21.97988, -13.00148], [24.03339, -12.99091], [23.90937, -12.844], [24.06672, -12.29058], [23.98804, -12.13149], [24.02603, -11.15368], [24.00027, -10.89356], [23.86868, -11.02856], [23.45631, -10.946], [23.16602, -11.10577], [22.54205, -11.05784], [22.25951, -11.24911], [22.17954, -10.85884], [22.32604, -10.76291], [22.19039, -9.94628], [21.84856, -9.59871], [21.79824, -7.29628], [20.56263, -7.28566], [20.61689, -6.90876], [20.31846, -6.91953], [20.30218, -6.98955], [19.5469, -7.00195], [19.33698, -7.99743], [18.33635, -8.00126], [17.5828, -8.13784], [16.96282, -7.21787], [16.55507, -5.85631]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AQ", + iso1A3: "ATA", + iso1N3: "010", + wikidata: "Q51", + nameEn: "Antarctica", + level: "region", + callingCodes: ["672"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[180, -60], [-180, -60], [-180, -90], [180, -90], [180, -60]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AR", + iso1A3: "ARG", + iso1N3: "032", + wikidata: "Q414", + nameEn: "Argentina", + aliases: ["RA"], + groups: ["005", "419", "019", "UN"], + callingCodes: ["54"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-72.31343, -50.58411], [-72.33873, -51.59954], [-71.99889, -51.98018], [-69.97824, -52.00845], [-68.41683, -52.33516], [-68.60702, -52.65781], [-68.60733, -54.9125], [-68.01394, -54.8753], [-67.46182, -54.92205], [-67.11046, -54.94199], [-66.07313, -55.19618], [-63.67376, -55.11859], [-54.78916, -36.21945], [-57.83001, -34.69099], [-58.34425, -34.15035], [-58.44442, -33.84033], [-58.40475, -33.11777], [-58.1224, -32.98842], [-58.22362, -32.52416], [-58.10036, -32.25338], [-58.20252, -31.86966], [-58.00076, -31.65016], [-58.0023, -31.53084], [-58.07569, -31.44916], [-57.98127, -31.3872], [-57.9908, -31.34924], [-57.86729, -31.06352], [-57.89476, -30.95994], [-57.8024, -30.77193], [-57.89115, -30.49572], [-57.64859, -30.35095], [-57.61478, -30.25165], [-57.65132, -30.19229], [-57.09386, -29.74211], [-56.81251, -29.48154], [-56.62789, -29.18073], [-56.57295, -29.11357], [-56.54171, -29.11447], [-56.05265, -28.62651], [-56.00458, -28.60421], [-56.01729, -28.51223], [-55.65418, -28.18304], [-55.6262, -28.17124], [-55.33303, -27.94661], [-55.16872, -27.86224], [-55.1349, -27.89759], [-54.90805, -27.73149], [-54.90159, -27.63132], [-54.67657, -27.57214], [-54.50416, -27.48232], [-54.41888, -27.40882], [-54.19268, -27.30751], [-54.19062, -27.27639], [-54.15978, -27.2889], [-53.80144, -27.09844], [-53.73372, -26.6131], [-53.68269, -26.33359], [-53.64505, -26.28089], [-53.64186, -26.25976], [-53.64632, -26.24798], [-53.63881, -26.25075], [-53.63739, -26.2496], [-53.65237, -26.23289], [-53.65018, -26.19501], [-53.73968, -26.10012], [-53.73391, -26.07006], [-53.7264, -26.0664], [-53.73086, -26.05842], [-53.73511, -26.04211], [-53.83691, -25.94849], [-53.90831, -25.55513], [-54.52926, -25.62846], [-54.5502, -25.58915], [-54.59398, -25.59224], [-54.62063, -25.91213], [-54.60664, -25.9691], [-54.67359, -25.98607], [-54.69333, -26.37705], [-54.70732, -26.45099], [-54.80868, -26.55669], [-55.00584, -26.78754], [-55.06351, -26.80195], [-55.16948, -26.96068], [-55.25243, -26.93808], [-55.39611, -26.97679], [-55.62322, -27.1941], [-55.59094, -27.32444], [-55.74475, -27.44485], [-55.89195, -27.3467], [-56.18313, -27.29851], [-56.85337, -27.5165], [-58.04205, -27.2387], [-58.59549, -27.29973], [-58.65321, -27.14028], [-58.3198, -26.83443], [-58.1188, -26.16704], [-57.87176, -25.93604], [-57.57431, -25.47269], [-57.80821, -25.13863], [-58.25492, -24.92528], [-58.33055, -24.97099], [-59.33886, -24.49935], [-59.45482, -24.34787], [-60.03367, -24.00701], [-60.28163, -24.04436], [-60.99754, -23.80934], [-61.0782, -23.62932], [-61.9756, -23.0507], [-62.22768, -22.55807], [-62.51761, -22.37684], [-62.64455, -22.25091], [-62.8078, -22.12534], [-62.81124, -21.9987], [-63.66482, -21.99918], [-63.68113, -22.0544], [-63.70963, -21.99934], [-63.93287, -21.99934], [-64.22918, -22.55807], [-64.31489, -22.88824], [-64.35108, -22.73282], [-64.4176, -22.67692], [-64.58888, -22.25035], [-64.67174, -22.18957], [-64.90014, -22.12136], [-64.99524, -22.08255], [-65.47435, -22.08908], [-65.57743, -22.07675], [-65.58694, -22.09794], [-65.61166, -22.09504], [-65.7467, -22.10105], [-65.9261, -21.93335], [-66.04832, -21.9187], [-66.03836, -21.84829], [-66.24077, -21.77837], [-66.29714, -22.08741], [-66.7298, -22.23644], [-67.18382, -22.81525], [-66.99632, -22.99839], [-67.33563, -24.04237], [-68.24825, -24.42596], [-68.56909, -24.69831], [-68.38372, -25.08636], [-68.57622, -25.32505], [-68.38372, -26.15353], [-68.56909, -26.28146], [-68.59048, -26.49861], [-68.27677, -26.90626], [-68.43363, -27.08414], [-68.77586, -27.16029], [-69.22504, -27.95042], [-69.66709, -28.44055], [-69.80969, -29.07185], [-69.99507, -29.28351], [-69.8596, -30.26131], [-70.14479, -30.36595], [-70.55832, -31.51559], [-69.88099, -33.34489], [-69.87386, -34.13344], [-70.49416, -35.24145], [-70.38008, -36.02375], [-70.95047, -36.4321], [-71.24279, -37.20264], [-70.89532, -38.6923], [-71.37826, -38.91474], [-71.92726, -40.72714], [-71.74901, -42.11711], [-72.15541, -42.15941], [-72.14828, -42.85321], [-71.64206, -43.64774], [-71.81318, -44.38097], [-71.16436, -44.46244], [-71.26418, -44.75684], [-72.06985, -44.81756], [-71.35687, -45.22075], [-71.75614, -45.61611], [-71.68577, -46.55385], [-71.94152, -47.13595], [-72.50478, -47.80586], [-72.27662, -48.28727], [-72.54042, -48.52392], [-72.56894, -48.81116], [-73.09655, -49.14342], [-73.45156, -49.79461], [-73.55259, -49.92488], [-73.15765, -50.78337], [-72.31343, -50.58411]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AS", + iso1A3: "ASM", + iso1N3: "016", + wikidata: "Q16641", + nameEn: "American Samoa", + aliases: ["US-AS"], + country: "US", + groups: ["Q1352230", "061", "009", "UN"], + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1 684"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-171.39864, -10.21587], [-170.99605, -15.1275], [-166.32598, -15.26169], [-171.39864, -10.21587]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AT", + iso1A3: "AUT", + iso1N3: "040", + wikidata: "Q40", + nameEn: "Austria", + groups: ["EU", "155", "150", "UN"], + callingCodes: ["43"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[15.34823, 48.98444], [15.28305, 48.98831], [15.26177, 48.95766], [15.16358, 48.94278], [15.15534, 48.99056], [14.99878, 49.01444], [14.97612, 48.96983], [14.98917, 48.90082], [14.95072, 48.79101], [14.98032, 48.77959], [14.9782, 48.7766], [14.98112, 48.77524], [14.9758, 48.76857], [14.95641, 48.75915], [14.94773, 48.76268], [14.81545, 48.7874], [14.80821, 48.77711], [14.80584, 48.73489], [14.72756, 48.69502], [14.71794, 48.59794], [14.66762, 48.58215], [14.60808, 48.62881], [14.56139, 48.60429], [14.4587, 48.64695], [14.43076, 48.58855], [14.33909, 48.55852], [14.20691, 48.5898], [14.09104, 48.5943], [14.01482, 48.63788], [14.06151, 48.66873], [13.84023, 48.76988], [13.82266, 48.75544], [13.81863, 48.73257], [13.79337, 48.71375], [13.81791, 48.69832], [13.81283, 48.68426], [13.81901, 48.6761], [13.82609, 48.62345], [13.80038, 48.59487], [13.80519, 48.58026], [13.76921, 48.55324], [13.7513, 48.5624], [13.74816, 48.53058], [13.72802, 48.51208], [13.66113, 48.53558], [13.65186, 48.55092], [13.62508, 48.55501], [13.59705, 48.57013], [13.57535, 48.55912], [13.51291, 48.59023], [13.50131, 48.58091], [13.50663, 48.57506], [13.46967, 48.55157], [13.45214, 48.56472], [13.43695, 48.55776], [13.45727, 48.51092], [13.42527, 48.45711], [13.43929, 48.43386], [13.40709, 48.37292], [13.30897, 48.31575], [13.26039, 48.29422], [13.18093, 48.29577], [13.126, 48.27867], [13.0851, 48.27711], [13.02083, 48.25689], [12.95306, 48.20629], [12.87126, 48.20318], [12.84475, 48.16556], [12.836, 48.1647], [12.8362, 48.15876], [12.82673, 48.15245], [12.80676, 48.14979], [12.78595, 48.12445], [12.7617, 48.12796], [12.74973, 48.10885], [12.76141, 48.07373], [12.8549, 48.01122], [12.87476, 47.96195], [12.91683, 47.95647], [12.9211, 47.95135], [12.91985, 47.94069], [12.92668, 47.93879], [12.93419, 47.94063], [12.93642, 47.94436], [12.93886, 47.94046], [12.94163, 47.92927], [13.00588, 47.84374], [12.98543, 47.82896], [12.96311, 47.79957], [12.93202, 47.77302], [12.94371, 47.76281], [12.9353, 47.74788], [12.91711, 47.74026], [12.90274, 47.72513], [12.91333, 47.7178], [12.92969, 47.71094], [12.98578, 47.7078], [13.01382, 47.72116], [13.07692, 47.68814], [13.09562, 47.63304], [13.06407, 47.60075], [13.06641, 47.58577], [13.04537, 47.58183], [13.05355, 47.56291], [13.03252, 47.53373], [13.04537, 47.49426], [12.9998, 47.46267], [12.98344, 47.48716], [12.9624, 47.47452], [12.85256, 47.52741], [12.84672, 47.54556], [12.80699, 47.54477], [12.77427, 47.58025], [12.82101, 47.61493], [12.76492, 47.64485], [12.77777, 47.66689], [12.7357, 47.6787], [12.6071, 47.6741], [12.57438, 47.63238], [12.53816, 47.63553], [12.50076, 47.62293], [12.44117, 47.6741], [12.43883, 47.6977], [12.37222, 47.68433], [12.336, 47.69534], [12.27991, 47.68827], [12.26004, 47.67725], [12.24017, 47.69534], [12.26238, 47.73544], [12.2542, 47.7433], [12.22571, 47.71776], [12.18303, 47.70065], [12.16217, 47.70105], [12.16769, 47.68167], [12.18347, 47.66663], [12.18507, 47.65984], [12.19895, 47.64085], [12.20801, 47.61082], [12.20398, 47.60667], [12.18568, 47.6049], [12.17737, 47.60121], [12.18145, 47.61019], [12.17824, 47.61506], [12.13734, 47.60639], [12.05788, 47.61742], [12.02282, 47.61033], [12.0088, 47.62451], [11.85572, 47.60166], [11.84052, 47.58354], [11.63934, 47.59202], [11.60681, 47.57881], [11.58811, 47.55515], [11.58578, 47.52281], [11.52618, 47.50939], [11.4362, 47.51413], [11.38128, 47.47465], [11.4175, 47.44621], [11.33804, 47.44937], [11.29597, 47.42566], [11.27844, 47.39956], [11.22002, 47.3964], [11.25157, 47.43277], [11.20482, 47.43198], [11.12536, 47.41222], [11.11835, 47.39719], [10.97111, 47.39561], [10.97111, 47.41617], [10.98513, 47.42882], [10.92437, 47.46991], [10.93839, 47.48018], [10.90918, 47.48571], [10.87061, 47.4786], [10.86945, 47.5015], [10.91268, 47.51334], [10.88814, 47.53701], [10.77596, 47.51729], [10.7596, 47.53228], [10.6965, 47.54253], [10.68832, 47.55752], [10.63456, 47.5591], [10.60337, 47.56755], [10.56912, 47.53584], [10.48849, 47.54057], [10.47329, 47.58552], [10.43473, 47.58394], [10.44992, 47.5524], [10.4324, 47.50111], [10.44291, 47.48453], [10.46278, 47.47901], [10.47446, 47.43318], [10.4359, 47.41183], [10.4324, 47.38494], [10.39851, 47.37623], [10.33424, 47.30813], [10.23257, 47.27088], [10.17531, 47.27167], [10.17648, 47.29149], [10.2147, 47.31014], [10.19998, 47.32832], [10.23757, 47.37609], [10.22774, 47.38904], [10.2127, 47.38019], [10.17648, 47.38889], [10.16362, 47.36674], [10.11805, 47.37228], [10.09819, 47.35724], [10.06897, 47.40709], [10.1052, 47.4316], [10.09001, 47.46005], [10.07131, 47.45531], [10.03859, 47.48927], [10.00003, 47.48216], [9.96029, 47.53899], [9.92407, 47.53111], [9.87733, 47.54688], [9.87499, 47.52953], [9.8189, 47.54688], [9.82591, 47.58158], [9.80254, 47.59419], [9.76748, 47.5934], [9.72736, 47.53457], [9.55125, 47.53629], [9.56312, 47.49495], [9.58208, 47.48344], [9.59482, 47.46305], [9.60205, 47.46165], [9.60484, 47.46358], [9.60841, 47.47178], [9.62158, 47.45858], [9.62475, 47.45685], [9.6423, 47.45599], [9.65728, 47.45383], [9.65863, 47.44847], [9.64483, 47.43842], [9.6446, 47.43233], [9.65043, 47.41937], [9.65136, 47.40504], [9.6629, 47.39591], [9.67334, 47.39191], [9.67445, 47.38429], [9.6711, 47.37824], [9.66243, 47.37136], [9.65427, 47.36824], [9.62476, 47.36639], [9.59978, 47.34671], [9.58513, 47.31334], [9.55857, 47.29919], [9.54773, 47.2809], [9.53116, 47.27029], [9.56766, 47.24281], [9.55176, 47.22585], [9.56981, 47.21926], [9.58264, 47.20673], [9.56539, 47.17124], [9.62623, 47.14685], [9.63395, 47.08443], [9.61216, 47.07732], [9.60717, 47.06091], [9.87935, 47.01337], [9.88266, 46.93343], [9.98058, 46.91434], [10.10715, 46.84296], [10.22675, 46.86942], [10.24128, 46.93147], [10.30031, 46.92093], [10.36933, 47.00212], [10.48376, 46.93891], [10.47197, 46.85698], [10.54783, 46.84505], [10.66405, 46.87614], [10.75753, 46.82258], [10.72974, 46.78972], [11.00764, 46.76896], [11.10618, 46.92966], [11.33355, 46.99862], [11.50739, 47.00644], [11.74789, 46.98484], [12.19254, 47.09331], [12.21781, 47.03996], [12.11675, 47.01241], [12.2006, 46.88854], [12.27591, 46.88651], [12.38708, 46.71529], [12.59992, 46.6595], [12.94445, 46.60401], [13.27627, 46.56059], [13.64088, 46.53438], [13.7148, 46.5222], [13.89837, 46.52331], [14.00422, 46.48474], [14.04002, 46.49117], [14.12097, 46.47724], [14.15989, 46.43327], [14.28326, 46.44315], [14.314, 46.43327], [14.42608, 46.44614], [14.45877, 46.41717], [14.52176, 46.42617], [14.56463, 46.37208], [14.5942, 46.43434], [14.66892, 46.44936], [14.72185, 46.49974], [14.81836, 46.51046], [14.83549, 46.56614], [14.86419, 46.59411], [14.87129, 46.61], [14.92283, 46.60848], [14.96002, 46.63459], [14.98024, 46.6009], [15.01451, 46.641], [15.14215, 46.66131], [15.23711, 46.63994], [15.41235, 46.65556], [15.45514, 46.63697], [15.46906, 46.61321], [15.54431, 46.6312], [15.55333, 46.64988], [15.54533, 46.66985], [15.59826, 46.68908], [15.62317, 46.67947], [15.63255, 46.68069], [15.6365, 46.6894], [15.6543, 46.69228], [15.6543, 46.70616], [15.67411, 46.70735], [15.69523, 46.69823], [15.72279, 46.69548], [15.73823, 46.70011], [15.76771, 46.69863], [15.78518, 46.70712], [15.8162, 46.71897], [15.87691, 46.7211], [15.94864, 46.68769], [15.98512, 46.68463], [15.99988, 46.67947], [16.04036, 46.6549], [16.04347, 46.68694], [16.02808, 46.71094], [15.99769, 46.7266], [15.98432, 46.74991], [15.99126, 46.78199], [15.99054, 46.82772], [16.05786, 46.83927], [16.10983, 46.867], [16.19904, 46.94134], [16.22403, 46.939], [16.27594, 46.9643], [16.28202, 47.00159], [16.51369, 47.00084], [16.43936, 47.03548], [16.52176, 47.05747], [16.46134, 47.09395], [16.52863, 47.13974], [16.44932, 47.14418], [16.46442, 47.16845], [16.4523, 47.18812], [16.42801, 47.18422], [16.41739, 47.20649], [16.43663, 47.21127], [16.44142, 47.25079], [16.47782, 47.25918], [16.45104, 47.41181], [16.49908, 47.39416], [16.52414, 47.41007], [16.57152, 47.40868], [16.6718, 47.46139], [16.64821, 47.50155], [16.71059, 47.52692], [16.64193, 47.63114], [16.58699, 47.61772], [16.4222, 47.66537], [16.55129, 47.72268], [16.53514, 47.73837], [16.54779, 47.75074], [16.61183, 47.76171], [16.65679, 47.74197], [16.72089, 47.73469], [16.7511, 47.67878], [16.82938, 47.68432], [16.86509, 47.72268], [16.87538, 47.68895], [17.08893, 47.70928], [17.05048, 47.79377], [17.07039, 47.81129], [17.00997, 47.86245], [17.08275, 47.87719], [17.11022, 47.92461], [17.09786, 47.97336], [17.16001, 48.00636], [17.07039, 48.0317], [17.09168, 48.09366], [17.05735, 48.14179], [17.02919, 48.13996], [16.97701, 48.17385], [16.89461, 48.31332], [16.90903, 48.32519], [16.84243, 48.35258], [16.83317, 48.38138], [16.83588, 48.3844], [16.8497, 48.38321], [16.85204, 48.44968], [16.94611, 48.53614], [16.93955, 48.60371], [16.90354, 48.71541], [16.79779, 48.70998], [16.71883, 48.73806], [16.68518, 48.7281], [16.67008, 48.77699], [16.46134, 48.80865], [16.40915, 48.74576], [16.37345, 48.729], [16.06034, 48.75436], [15.84404, 48.86921], [15.78087, 48.87644], [15.75341, 48.8516], [15.6921, 48.85973], [15.61622, 48.89541], [15.51357, 48.91549], [15.48027, 48.94481], [15.34823, 48.98444]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AU", + iso1A3: "AUS", + iso1N3: "036", + wikidata: "Q408", + nameEn: "Australia" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "AW", + iso1A3: "ABW", + iso1N3: "533", + wikidata: "Q21203", + nameEn: "Aruba", + aliases: ["NL-AW"], + country: "NL", + groups: ["Q1451600", "029", "003", "419", "019", "UN"], + callingCodes: ["297"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-70.00823, 12.98375], [-70.35625, 12.58277], [-69.60231, 12.17], [-70.00823, 12.98375]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AX", + iso1A3: "ALA", + iso1N3: "248", + wikidata: "Q5689", + nameEn: "\xC5land Islands", + country: "FI", + groups: ["EU", "154", "150", "UN"], + callingCodes: ["358 18", "358 457"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[19.08191, 60.19152], [20.5104, 59.15546], [21.35468, 59.67511], [21.02509, 60.12142], [21.08159, 60.20167], [21.15143, 60.54555], [20.96741, 60.71528], [19.23413, 60.61414], [19.08191, 60.19152]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "AZ", + iso1A3: "AZE", + iso1N3: "031", + wikidata: "Q227", + nameEn: "Azerbaijan", + groups: ["145", "142", "UN"], + callingCodes: ["994"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[46.42738, 41.91323], [46.3984, 41.84399], [46.30863, 41.79133], [46.23962, 41.75811], [46.20538, 41.77205], [46.17891, 41.72094], [46.19759, 41.62327], [46.24429, 41.59883], [46.26531, 41.63339], [46.28182, 41.60089], [46.3253, 41.60912], [46.34039, 41.5947], [46.34126, 41.57454], [46.29794, 41.5724], [46.33925, 41.4963], [46.40307, 41.48464], [46.4669, 41.43331], [46.63658, 41.37727], [46.72375, 41.28609], [46.66148, 41.20533], [46.63969, 41.09515], [46.55096, 41.1104], [46.48558, 41.0576], [46.456, 41.09984], [46.37661, 41.10805], [46.27698, 41.19011], [46.13221, 41.19479], [45.95786, 41.17956], [45.80842, 41.2229], [45.69946, 41.29545], [45.75705, 41.35157], [45.71035, 41.36208], [45.68389, 41.3539], [45.45973, 41.45898], [45.4006, 41.42402], [45.31352, 41.47168], [45.26285, 41.46433], [45.1797, 41.42231], [45.09867, 41.34065], [45.0133, 41.29747], [45.05497, 41.2464], [45.02932, 41.2101], [45.05201, 41.19211], [45.11811, 41.19967], [45.1969, 41.168], [45.19942, 41.13299], [45.08028, 41.10917], [45.06784, 41.05379], [45.12923, 41.06059], [45.1313, 41.09369], [45.1634, 41.08082], [45.16493, 41.05068], [45.1994, 41.04518], [45.25897, 41.0027], [45.26162, 41.0228], [45.28859, 41.03757], [45.35677, 40.99784], [45.39725, 41.02603], [45.44083, 41.01663], [45.40814, 40.97904], [45.60584, 40.87436], [45.55914, 40.78366], [45.4206, 40.7424], [45.35366, 40.65979], [45.45484, 40.57707], [45.42994, 40.53804], [45.65098, 40.37696], [45.95609, 40.27846], [45.97944, 40.181], [45.83779, 39.98925], [45.78642, 40.03218], [45.59806, 40.0131], [45.60895, 39.97733], [45.7833, 39.9475], [45.82533, 39.82925], [45.96543, 39.78859], [46.18493, 39.60533], [46.40286, 39.63651], [46.42465, 39.57534], [46.52117, 39.58734], [46.57098, 39.56694], [46.57721, 39.54414], [46.51027, 39.52373], [46.53051, 39.47809], [46.4013, 39.45405], [46.37795, 39.42039], [46.43244, 39.35181], [46.50093, 39.33736], [46.56476, 39.24942], [46.63481, 39.23013], [46.58032, 39.21204], [46.54141, 39.15895], [46.52584, 39.18912], [46.44022, 39.19636], [46.54296, 39.07078], [46.51805, 38.94982], [46.53497, 38.86548], [46.75752, 39.03231], [46.83822, 39.13143], [46.92539, 39.16644], [46.95341, 39.13505], [47.05771, 39.20143], [47.05927, 39.24846], [47.31301, 39.37492], [47.38978, 39.45999], [47.50099, 39.49615], [47.84774, 39.66285], [47.98977, 39.70999], [48.34264, 39.42935], [48.37385, 39.37584], [48.15984, 39.30028], [48.12404, 39.25208], [48.15361, 39.19419], [48.31239, 39.09278], [48.33884, 39.03022], [48.28437, 38.97186], [48.08627, 38.94434], [48.07734, 38.91616], [48.01409, 38.90333], [48.02581, 38.82705], [48.24773, 38.71883], [48.3146, 38.59958], [48.45084, 38.61013], [48.58793, 38.45076], [48.62217, 38.40198], [48.70001, 38.40564], [48.78979, 38.45026], [48.81072, 38.44853], [48.84969, 38.45015], [48.88288, 38.43975], [52.39847, 39.43556], [48.80971, 41.95365], [48.5867, 41.84306], [48.55078, 41.77917], [48.42301, 41.65444], [48.40277, 41.60441], [48.2878, 41.56221], [48.22064, 41.51472], [48.07587, 41.49957], [47.87973, 41.21798], [47.75831, 41.19455], [47.62288, 41.22969], [47.54504, 41.20275], [47.49004, 41.26366], [47.34579, 41.27884], [47.10762, 41.59044], [47.03757, 41.55434], [46.99554, 41.59743], [47.00955, 41.63583], [46.8134, 41.76252], [46.75269, 41.8623], [46.58924, 41.80547], [46.5332, 41.87389], [46.42738, 41.91323]], [[45.50279, 40.58424], [45.47927, 40.65023], [45.51825, 40.67382], [45.56071, 40.64765], [45.50279, 40.58424]]], [[[45.00864, 41.03411], [45.03792, 41.03938], [45.04517, 41.06653], [45.03406, 41.07931], [45.00864, 41.09407], [44.97169, 41.09176], [44.95383, 41.07553], [44.96031, 41.06345], [44.9903, 41.05657], [45.00864, 41.03411]]], [[[45.21324, 40.9817], [45.23095, 40.97828], [45.23487, 41.00226], [45.20625, 41.01484], [45.18382, 41.0066], [45.19312, 40.98998], [45.20518, 40.99348], [45.21219, 40.99001], [45.21324, 40.9817]]], [[[45.46992, 39.49888], [45.29606, 39.57654], [45.30385, 39.61373], [45.23535, 39.61373], [45.21784, 39.58074], [45.17464, 39.58614], [45.18554, 39.67846], [45.06604, 39.79277], [44.92869, 39.72157], [44.88354, 39.74432], [44.75779, 39.7148], [44.80977, 39.65768], [44.81043, 39.62677], [44.88916, 39.59653], [44.96746, 39.42998], [45.05932, 39.36435], [45.08751, 39.35052], [45.16168, 39.21952], [45.30489, 39.18333], [45.40148, 39.09007], [45.40452, 39.07224], [45.44811, 39.04927], [45.44966, 38.99243], [45.6131, 38.964], [45.6155, 38.94304], [45.65172, 38.95199], [45.83883, 38.90768], [45.90266, 38.87739], [45.94624, 38.89072], [46.00228, 38.87376], [46.06766, 38.87861], [46.14785, 38.84206], [46.06973, 39.0744], [46.02303, 39.09978], [45.99774, 39.28931], [45.79225, 39.3695], [45.83, 39.46487], [45.80804, 39.56716], [45.70547, 39.60174], [45.46992, 39.49888]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BA", + iso1A3: "BIH", + iso1N3: "070", + wikidata: "Q225", + nameEn: "Bosnia and Herzegovina", + groups: ["039", "150", "UN"], + callingCodes: ["387"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[17.84826, 45.04489], [17.66571, 45.13408], [17.59104, 45.10816], [17.51469, 45.10791], [17.47589, 45.12656], [17.45615, 45.12523], [17.4498, 45.16119], [17.41229, 45.13335], [17.33573, 45.14521], [17.32092, 45.16246], [17.26815, 45.18444], [17.25131, 45.14957], [17.24325, 45.146], [17.18438, 45.14764], [17.0415, 45.20759], [16.9385, 45.22742], [16.92405, 45.27607], [16.83804, 45.18951], [16.81137, 45.18434], [16.78219, 45.19002], [16.74845, 45.20393], [16.64962, 45.20714], [16.60194, 45.23042], [16.56559, 45.22307], [16.5501, 45.2212], [16.52982, 45.22713], [16.49155, 45.21153], [16.4634, 45.14522], [16.40023, 45.1147], [16.38309, 45.05955], [16.38219, 45.05139], [16.3749, 45.05206], [16.35863, 45.03529], [16.35404, 45.00241], [16.29036, 44.99732], [16.12153, 45.09616], [15.98412, 45.23088], [15.83512, 45.22459], [15.76371, 45.16508], [15.78842, 45.11519], [15.74585, 45.0638], [15.78568, 44.97401], [15.74723, 44.96818], [15.76096, 44.87045], [15.79472, 44.8455], [15.72584, 44.82334], [15.8255, 44.71501], [15.89348, 44.74964], [16.05828, 44.61538], [16.00884, 44.58605], [16.03012, 44.55572], [16.10566, 44.52586], [16.16814, 44.40679], [16.12969, 44.38275], [16.21346, 44.35231], [16.18688, 44.27012], [16.36864, 44.08263], [16.43662, 44.07523], [16.43629, 44.02826], [16.50528, 44.0244], [16.55472, 43.95326], [16.70922, 43.84887], [16.75316, 43.77157], [16.80736, 43.76011], [17.00585, 43.58037], [17.15828, 43.49376], [17.24411, 43.49376], [17.29699, 43.44542], [17.25579, 43.40353], [17.286, 43.33065], [17.46986, 43.16559], [17.64268, 43.08595], [17.70879, 42.97223], [17.5392, 42.92787], [17.6444, 42.88641], [17.68151, 42.92725], [17.7948, 42.89556], [17.80854, 42.9182], [17.88201, 42.83668], [18.24318, 42.6112], [18.36197, 42.61423], [18.43735, 42.55921], [18.49778, 42.58409], [18.53751, 42.57376], [18.55504, 42.58409], [18.52232, 42.62279], [18.57373, 42.64429], [18.54841, 42.68328], [18.54603, 42.69171], [18.55221, 42.69045], [18.56789, 42.72074], [18.47324, 42.74992], [18.45921, 42.81682], [18.47633, 42.85829], [18.4935, 42.86433], [18.49661, 42.89306], [18.49076, 42.95553], [18.52232, 43.01451], [18.66254, 43.03928], [18.64735, 43.14766], [18.66605, 43.2056], [18.71747, 43.2286], [18.6976, 43.25243], [18.76538, 43.29838], [18.85342, 43.32426], [18.84794, 43.33735], [18.83912, 43.34795], [18.90911, 43.36383], [18.95819, 43.32899], [18.95001, 43.29327], [19.00844, 43.24988], [19.04233, 43.30008], [19.08206, 43.29668], [19.08673, 43.31453], [19.04071, 43.397], [19.01078, 43.43854], [18.96053, 43.45042], [18.95469, 43.49367], [18.91379, 43.50299], [19.01078, 43.55806], [19.04934, 43.50384], [19.13933, 43.5282], [19.15685, 43.53943], [19.22807, 43.5264], [19.24774, 43.53061], [19.2553, 43.5938], [19.33426, 43.58833], [19.36653, 43.60921], [19.41941, 43.54056], [19.42696, 43.57987], [19.50455, 43.58385], [19.5176, 43.71403], [19.3986, 43.79668], [19.23465, 43.98764], [19.24363, 44.01502], [19.38439, 43.96611], [19.52515, 43.95573], [19.56498, 43.99922], [19.61836, 44.01464], [19.61991, 44.05254], [19.57467, 44.04716], [19.55999, 44.06894], [19.51167, 44.08158], [19.47321, 44.1193], [19.48386, 44.14332], [19.47338, 44.15034], [19.43905, 44.13088], [19.40927, 44.16722], [19.3588, 44.18353], [19.34773, 44.23244], [19.32464, 44.27185], [19.26945, 44.26957], [19.23306, 44.26097], [19.20508, 44.2917], [19.18328, 44.28383], [19.16741, 44.28648], [19.13332, 44.31492], [19.13556, 44.338], [19.11547, 44.34218], [19.1083, 44.3558], [19.11865, 44.36712], [19.10298, 44.36924], [19.10365, 44.37795], [19.10704, 44.38249], [19.10749, 44.39421], [19.11785, 44.40313], [19.14681, 44.41463], [19.14837, 44.45253], [19.12278, 44.50132], [19.13369, 44.52521], [19.16699, 44.52197], [19.26388, 44.65412], [19.32543, 44.74058], [19.36722, 44.88164], [19.18183, 44.92055], [19.01994, 44.85493], [18.8704, 44.85097], [18.76347, 44.90669], [18.76369, 44.93707], [18.80661, 44.93561], [18.78357, 44.97741], [18.65723, 45.07544], [18.47939, 45.05871], [18.41896, 45.11083], [18.32077, 45.1021], [18.24387, 45.13699], [18.1624, 45.07654], [18.03121, 45.12632], [18.01594, 45.15163], [17.99479, 45.14958], [17.97834, 45.13831], [17.97336, 45.12245], [17.93706, 45.08016], [17.87148, 45.04645], [17.84826, 45.04489]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BB", + iso1A3: "BRB", + iso1N3: "052", + wikidata: "Q244", + nameEn: "Barbados", + groups: ["029", "003", "419", "019", "UN"], + driveSide: "left", + callingCodes: ["1 246"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-58.56442, 13.24471], [-59.80731, 13.87556], [-59.82929, 12.70644], [-58.56442, 13.24471]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BD", + iso1A3: "BGD", + iso1N3: "050", + wikidata: "Q902", + nameEn: "Bangladesh", + groups: ["034", "142", "UN"], + driveSide: "left", + callingCodes: ["880"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[89.15869, 26.13708], [89.08899, 26.38845], [88.95612, 26.4564], [88.92357, 26.40711], [88.91321, 26.37984], [89.05328, 26.2469], [88.85004, 26.23211], [88.78961, 26.31093], [88.67837, 26.26291], [88.69485, 26.38353], [88.62144, 26.46783], [88.4298, 26.54489], [88.41196, 26.63837], [88.33093, 26.48929], [88.35153, 26.45241], [88.36938, 26.48683], [88.48749, 26.45855], [88.51649, 26.35923], [88.35153, 26.29123], [88.34757, 26.22216], [88.1844, 26.14417], [88.16581, 26.0238], [88.08804, 25.91334], [88.13138, 25.78773], [88.242, 25.80811], [88.45103, 25.66245], [88.4559, 25.59227], [88.677, 25.46959], [88.81296, 25.51546], [88.85278, 25.34679], [89.01105, 25.30303], [89.00463, 25.26583], [88.94067, 25.18534], [88.44766, 25.20149], [88.46277, 25.07468], [88.33917, 24.86803], [88.27325, 24.88796], [88.21832, 24.96642], [88.14004, 24.93529], [88.15515, 24.85806], [88.00683, 24.66477], [88.08786, 24.63232], [88.12296, 24.51301], [88.50934, 24.32474], [88.68801, 24.31464], [88.74841, 24.1959], [88.6976, 24.14703], [88.73743, 23.91751], [88.66189, 23.87607], [88.58087, 23.87105], [88.56507, 23.64044], [88.74841, 23.47361], [88.79351, 23.50535], [88.79254, 23.46028], [88.71133, 23.2492], [88.99148, 23.21134], [88.86377, 23.08759], [88.88327, 23.03885], [88.87063, 22.95235], [88.96713, 22.83346], [88.9151, 22.75228], [88.94614, 22.66941], [88.9367, 22.58527], [89.07114, 22.15335], [89.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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BE", + iso1A3: "BEL", + iso1N3: "056", + wikidata: "Q31", + nameEn: "Belgium", + groups: ["EU", "155", "150", "UN"], + callingCodes: ["32"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[4.93295, 51.44945], [4.93909, 51.44632], [4.9524, 51.45014], [4.95244, 51.45207], [4.93295, 51.44945]]], [[[4.91493, 51.4353], [4.92652, 51.43329], [4.92952, 51.42984], [4.93986, 51.43064], [4.94265, 51.44003], [4.93471, 51.43861], [4.93416, 51.44185], [4.94025, 51.44193], [4.93544, 51.44634], [4.92879, 51.44161], [4.92815, 51.43856], [4.92566, 51.44273], [4.92811, 51.4437], [4.92287, 51.44741], [4.91811, 51.44621], [4.92227, 51.44252], [4.91935, 51.43634], [4.91493, 51.4353]]], [[[4.82946, 51.4213], [4.82409, 51.44736], [4.84139, 51.4799], [4.78803, 51.50284], [4.77321, 51.50529], [4.74578, 51.48937], [4.72935, 51.48424], [4.65442, 51.42352], [4.57489, 51.4324], [4.53521, 51.4243], [4.52846, 51.45002], [4.54675, 51.47265], [4.5388, 51.48184], [4.47736, 51.4778], [4.38122, 51.44905], [4.39747, 51.43316], [4.38064, 51.41965], [4.43777, 51.36989], [4.39292, 51.35547], [4.34086, 51.35738], [4.33265, 51.37687], [4.21923, 51.37443], [4.24024, 51.35371], [4.16721, 51.29348], [4.05165, 51.24171], [4.01957, 51.24504], [3.97889, 51.22537], [3.90125, 51.20371], [3.78783, 51.2151], [3.78999, 51.25766], [3.58939, 51.30064], [3.51502, 51.28697], [3.52698, 51.2458], [3.43488, 51.24135], [3.41704, 51.25933], [3.38289, 51.27331], [3.35847, 51.31572], [3.38696, 51.33436], [3.36263, 51.37112], [2.56575, 51.85301], [2.18458, 51.52087], [2.55904, 51.07014], [2.57551, 51.00326], [2.63074, 50.94746], [2.59093, 50.91751], [2.63331, 50.81457], [2.71165, 50.81295], [2.81056, 50.71773], [2.8483, 50.72276], [2.86985, 50.7033], [2.87937, 50.70298], [2.88504, 50.70656], [2.90069, 50.69263], [2.91036, 50.6939], [2.90873, 50.702], [2.95019, 50.75138], [2.96778, 50.75242], [3.00537, 50.76588], [3.04314, 50.77674], [3.09163, 50.77717], [3.10614, 50.78303], [3.11206, 50.79416], [3.11987, 50.79188], [3.1257, 50.78603], [3.15017, 50.79031], [3.16476, 50.76843], [3.18339, 50.74981], [3.18811, 50.74025], [3.20064, 50.73547], [3.19017, 50.72569], [3.20845, 50.71662], [3.22042, 50.71019], [3.24593, 50.71389], [3.26063, 50.70086], [3.26141, 50.69151], [3.2536, 50.68977], [3.264, 50.67668], [3.23951, 50.6585], [3.2729, 50.60718], [3.28575, 50.52724], [3.37693, 50.49538], [3.44629, 50.51009], [3.47385, 50.53397], [3.51564, 50.5256], [3.49509, 50.48885], [3.5683, 50.50192], [3.58361, 50.49049], [3.61014, 50.49568], [3.64426, 50.46275], [3.66153, 50.45165], [3.67494, 50.40239], [3.67262, 50.38663], [3.65709, 50.36873], [3.66976, 50.34563], [3.71009, 50.30305], [3.70987, 50.3191], [3.73911, 50.34809], [3.84314, 50.35219], [3.90781, 50.32814], [3.96771, 50.34989], [4.0268, 50.35793], [4.0689, 50.3254], [4.10237, 50.31247], [4.10957, 50.30234], [4.11954, 50.30425], [4.13665, 50.25609], [4.16808, 50.25786], [4.15524, 50.2833], [4.17347, 50.28838], [4.17861, 50.27443], [4.20651, 50.27333], [4.21945, 50.25539], [4.15524, 50.21103], [4.16014, 50.19239], [4.13561, 50.13078], [4.20147, 50.13535], [4.23101, 50.06945], [4.16294, 50.04719], [4.13508, 50.01976], [4.14239, 49.98034], [4.20532, 49.95803], [4.31963, 49.97043], [4.35051, 49.95315], [4.43488, 49.94122], [4.51098, 49.94659], [4.5414, 49.96911], [4.68695, 49.99685], [4.70064, 50.09384], [4.75237, 50.11314], [4.82438, 50.16878], [4.83279, 50.15331], [4.88602, 50.15182], [4.8382, 50.06738], [4.78827, 49.95609], [4.88529, 49.9236], [4.85134, 49.86457], [4.86965, 49.82271], [4.85464, 49.78995], [4.96714, 49.79872], [5.09249, 49.76193], [5.14545, 49.70287], [5.26232, 49.69456], [5.31465, 49.66846], [5.33039, 49.6555], [5.30214, 49.63055], [5.3137, 49.61225], [5.33851, 49.61599], [5.34837, 49.62889], [5.3974, 49.61596], [5.43713, 49.5707], [5.46734, 49.52648], [5.46541, 49.49825], [5.55001, 49.52729], [5.60909, 49.51228], [5.64505, 49.55146], [5.75649, 49.54321], [5.7577, 49.55915], [5.77435, 49.56298], [5.79195, 49.55228], [5.81838, 49.54777], [5.84143, 49.5533], [5.84692, 49.55663], [5.8424, 49.56082], [5.87256, 49.57539], [5.86986, 49.58756], [5.84971, 49.58674], [5.84826, 49.5969], [5.8762, 49.60898], [5.87609, 49.62047], [5.88393, 49.62802], [5.88552, 49.63507], [5.90599, 49.63853], [5.90164, 49.6511], [5.9069, 49.66377], [5.86175, 49.67862], [5.86527, 49.69291], [5.88677, 49.70951], [5.86503, 49.72739], [5.84193, 49.72161], [5.82562, 49.72395], [5.83149, 49.74729], [5.82245, 49.75048], [5.78871, 49.7962], [5.75409, 49.79239], [5.74953, 49.81428], [5.74364, 49.82058], [5.74844, 49.82435], [5.7404, 49.83452], [5.74076, 49.83823], [5.74975, 49.83933], [5.74953, 49.84709], [5.75884, 49.84811], [5.74567, 49.85368], [5.75861, 49.85631], [5.75269, 49.8711], [5.78415, 49.87922], [5.73621, 49.89796], [5.77314, 49.93646], [5.77291, 49.96056], [5.80833, 49.96451], [5.81163, 49.97142], [5.83467, 49.97823], [5.83968, 49.9892], [5.82331, 49.99662], [5.81866, 50.01286], [5.8551, 50.02683], [5.86904, 50.04614], [5.85474, 50.06342], [5.8857, 50.07824], [5.89488, 50.11476], [5.95929, 50.13295], [5.96453, 50.17259], [6.02488, 50.18283], [6.03093, 50.16362], [6.06406, 50.15344], [6.08577, 50.17246], [6.12028, 50.16374], [6.1137, 50.13668], [6.1379, 50.12964], [6.15298, 50.14126], [6.14132, 50.14971], [6.14588, 50.17106], [6.18739, 50.1822], [6.18364, 50.20815], [6.16853, 50.2234], [6.208, 50.25179], [6.28797, 50.27458], [6.29949, 50.30887], [6.32488, 50.32333], [6.35701, 50.31139], [6.40641, 50.32425], [6.40785, 50.33557], [6.3688, 50.35898], [6.34406, 50.37994], [6.36852, 50.40776], [6.37219, 50.45397], [6.34005, 50.46083], [6.3465, 50.48833], [6.30809, 50.50058], [6.26637, 50.50272], [6.22335, 50.49578], [6.20599, 50.52089], [6.19193, 50.5212], [6.18716, 50.52653], [6.19579, 50.5313], [6.19735, 50.53576], [6.17802, 50.54179], [6.17739, 50.55875], [6.20281, 50.56952], [6.22581, 50.5907], [6.24005, 50.58732], [6.24888, 50.59869], [6.2476, 50.60392], [6.26957, 50.62444], [6.17852, 50.6245], [6.11707, 50.72231], [6.04428, 50.72861], [6.0406, 50.71848], [6.0326, 50.72647], [6.03889, 50.74618], [6.01976, 50.75398], [5.97545, 50.75441], [5.95942, 50.7622], [5.89132, 50.75124], [5.89129, 50.75125], [5.88734, 50.77092], [5.84888, 50.75448], [5.84548, 50.76542], [5.80673, 50.7558], [5.77513, 50.78308], [5.76533, 50.78159], [5.74356, 50.7691], [5.73904, 50.75674], [5.72216, 50.76398], [5.69469, 50.75529], [5.68091, 50.75804], [5.70107, 50.7827], [5.68995, 50.79641], [5.70118, 50.80764], [5.65259, 50.82309], [5.64009, 50.84742], [5.64504, 50.87107], [5.67886, 50.88142], [5.69858, 50.91046], [5.71626, 50.90796], [5.72644, 50.91167], [5.72545, 50.92312], [5.74644, 50.94723], [5.75927, 50.95601], [5.74752, 50.96202], [5.72875, 50.95428], [5.71864, 50.96092], [5.76242, 50.99703], [5.77688, 51.02483], [5.75961, 51.03113], [5.77258, 51.06196], [5.79835, 51.05834], [5.79903, 51.09371], [5.82921, 51.09328], [5.83226, 51.10585], [5.8109, 51.10861], [5.80798, 51.11661], [5.85508, 51.14445], [5.82564, 51.16753], [5.77697, 51.1522], [5.77735, 51.17845], [5.74617, 51.18928], [5.70344, 51.1829], [5.65528, 51.18736], [5.65145, 51.19788], [5.5603, 51.22249], [5.5569, 51.26544], [5.515, 51.29462], [5.48476, 51.30053], [5.46519, 51.2849], [5.4407, 51.28169], [5.41672, 51.26248], [5.347, 51.27502], [5.33886, 51.26314], [5.29716, 51.26104], [5.26461, 51.26693], [5.23814, 51.26064], [5.22542, 51.26888], [5.24244, 51.30495], [5.2002, 51.32243], [5.16222, 51.31035], [5.13377, 51.31592], [5.13105, 51.34791], [5.07102, 51.39469], [5.10456, 51.43163], [5.07891, 51.4715], [5.04774, 51.47022], [5.03281, 51.48679], [5.0106, 51.47167], [5.00393, 51.44406], [4.92152, 51.39487], [4.90016, 51.41404], [4.84988, 51.41502], [4.78941, 51.41102], [4.77229, 51.41337], [4.76577, 51.43046], [4.78314, 51.43319], [4.82946, 51.4213]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BF", + iso1A3: "BFA", + iso1N3: "854", + wikidata: "Q965", + nameEn: "Burkina Faso", + groups: ["011", "202", "002", "UN"], + callingCodes: ["226"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[0.23859, 15.00135], [0.06588, 14.96961], [-0.24673, 15.07805], [-0.72004, 15.08655], [-1.05875, 14.7921], [-1.32166, 14.72774], [-1.68083, 14.50023], [-1.97945, 14.47709], [-1.9992, 14.19011], [-2.10223, 14.14878], [-2.47587, 14.29671], [-2.66175, 14.14713], [-2.84667, 14.05532], [-2.90831, 13.81174], [-2.88189, 13.64921], [-3.26407, 13.70699], [-3.28396, 13.5422], [-3.23599, 13.29035], [-3.43507, 13.27272], [-3.4313, 13.1588], [-3.54454, 13.1781], [-3.7911, 13.36665], [-3.96282, 13.38164], [-3.90558, 13.44375], [-3.96501, 13.49778], [-4.34477, 13.12927], [-4.21819, 12.95722], [-4.238, 12.71467], [-4.47356, 12.71252], [-4.41412, 12.31922], [-4.57703, 12.19875], [-4.54841, 12.1385], [-4.62546, 12.13204], [-4.62987, 12.06531], [-4.70692, 12.06746], [-4.72893, 12.01579], [-5.07897, 11.97918], [-5.26389, 11.84778], [-5.40258, 11.8327], [-5.26389, 11.75728], [-5.29251, 11.61715], [-5.22867, 11.60421], [-5.20665, 11.43811], [-5.25509, 11.36905], [-5.25949, 11.24816], [-5.32553, 11.21578], [-5.32994, 11.13371], [-5.49284, 11.07538], [-5.41579, 10.84628], [-5.47083, 10.75329], [-5.46643, 10.56074], [-5.51058, 10.43177], [-5.39602, 10.2929], [-5.12465, 10.29788], [-4.96453, 9.99923], [-4.96621, 9.89132], [-4.6426, 9.70696], [-4.31392, 9.60062], [-4.25999, 9.76012], [-3.69703, 9.94279], [-3.31779, 9.91125], [-3.27228, 9.84981], [-3.19306, 9.93781], [-3.16609, 9.85147], [-3.00765, 9.74019], [-2.93012, 9.57403], [-2.76494, 9.40778], [-2.68802, 9.49343], [-2.76534, 9.56589], [-2.74174, 9.83172], [-2.83108, 10.40252], [-2.94232, 10.64281], [-2.83373, 11.0067], [-0.67143, 10.99811], [-0.61937, 10.91305], [-0.44298, 11.04292], [-0.42391, 11.11661], [-0.38219, 11.12596], [-0.35955, 11.07801], [-0.28566, 11.12713], [-0.27374, 11.17157], [-0.13493, 11.14075], [0.50388, 11.01011], [0.48852, 10.98561], [0.50521, 10.98035], [0.4958, 10.93269], [0.66104, 10.99964], [0.91245, 10.99597], [0.9813, 11.08876], [1.03409, 11.04719], [1.42823, 11.46822], [2.00988, 11.42227], [2.29983, 11.68254], [2.39723, 11.89473], [2.05785, 12.35539], [2.26349, 12.41915], [0.99167, 13.10727], [0.99253, 13.37515], [1.18873, 13.31771], [1.21217, 13.37853], [1.24516, 13.33968], [1.28509, 13.35488], [1.24429, 13.39373], [1.20088, 13.38951], [1.02813, 13.46635], [0.99514, 13.5668], [0.77637, 13.64442], [0.77377, 13.6866], [0.61924, 13.68491], [0.38051, 14.05575], [0.16936, 14.51654], [0.23859, 15.00135]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BG", + iso1A3: "BGR", + iso1N3: "100", + wikidata: "Q219", + nameEn: "Bulgaria", + groups: ["EU", "151", "150", "UN"], + callingCodes: ["359"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[23.05288, 43.79494], [22.85314, 43.84452], [22.83753, 43.88055], [22.87873, 43.9844], [23.01674, 44.01946], [23.04988, 44.07694], [22.67173, 44.21564], [22.61711, 44.16938], [22.61688, 44.06534], [22.41449, 44.00514], [22.35558, 43.81281], [22.41043, 43.69566], [22.47582, 43.6558], [22.53397, 43.47225], [22.82036, 43.33665], [22.89727, 43.22417], [23.00806, 43.19279], [22.98104, 43.11199], [22.89521, 43.03625], [22.78397, 42.98253], [22.74826, 42.88701], [22.54302, 42.87774], [22.43309, 42.82057], [22.4997, 42.74144], [22.43983, 42.56851], [22.55669, 42.50144], [22.51961, 42.3991], [22.47498, 42.3915], [22.45919, 42.33822], [22.34773, 42.31725], [22.38136, 42.30339], [22.47251, 42.20393], [22.50289, 42.19527], [22.51224, 42.15457], [22.67701, 42.06614], [22.86749, 42.02275], [22.90254, 41.87587], [22.96682, 41.77137], [23.01239, 41.76527], [23.03342, 41.71034], [22.95513, 41.63265], [22.96331, 41.35782], [22.93334, 41.34104], [23.1833, 41.31755], [23.21953, 41.33773], [23.22771, 41.37106], [23.31301, 41.40525], [23.33639, 41.36317], [23.40416, 41.39999], [23.52453, 41.40262], [23.63203, 41.37632], [23.67644, 41.41139], [23.76525, 41.40175], [23.80148, 41.43943], [23.89613, 41.45257], [23.91483, 41.47971], [23.96975, 41.44118], [24.06908, 41.46132], [24.06323, 41.53222], [24.10063, 41.54796], [24.18126, 41.51735], [24.27124, 41.57682], [24.30513, 41.51297], [24.52599, 41.56808], [24.61129, 41.42278], [24.71529, 41.41928], [24.8041, 41.34913], [24.82514, 41.4035], [24.86136, 41.39298], [24.90928, 41.40876], [24.942, 41.38685], [25.11611, 41.34212], [25.28322, 41.23411], [25.48187, 41.28506], [25.52394, 41.2798], [25.55082, 41.31667], [25.61042, 41.30614], [25.66183, 41.31316], [25.70507, 41.29209], [25.8266, 41.34563], [25.87919, 41.30526], [26.12926, 41.35878], [26.16548, 41.42278], [26.20288, 41.43943], [26.14796, 41.47533], [26.176, 41.50072], [26.17951, 41.55409], [26.14328, 41.55496], [26.15146, 41.60828], [26.07083, 41.64584], [26.06148, 41.70345], [26.16841, 41.74858], [26.21325, 41.73223], [26.22888, 41.74139], [26.2654, 41.71544], [26.30255, 41.70925], [26.35957, 41.71149], [26.32952, 41.73637], [26.33589, 41.76802], [26.36952, 41.82265], [26.53968, 41.82653], [26.57961, 41.90024], [26.56051, 41.92995], [26.62996, 41.97644], [26.79143, 41.97386], [26.95638, 42.00741], [27.03277, 42.0809], [27.08486, 42.08735], [27.19251, 42.06028], [27.22376, 42.10152], [27.27411, 42.10409], [27.45478, 41.96591], [27.52379, 41.93756], [27.55191, 41.90928], [27.69949, 41.97515], [27.81235, 41.94803], [27.83492, 41.99709], [27.91479, 41.97902], [28.02971, 41.98066], [28.32297, 41.98371], [29.24336, 43.70874], [28.23293, 43.76], [27.99558, 43.84193], [27.92008, 44.00761], [27.73468, 43.95326], [27.64542, 44.04958], [27.60834, 44.01206], [27.39757, 44.0141], [27.26845, 44.12602], [26.95141, 44.13555], [26.62712, 44.05698], [26.38764, 44.04356], [26.10115, 43.96908], [26.05584, 43.90925], [25.94911, 43.85745], [25.72792, 43.69263], [25.39528, 43.61866], [25.17144, 43.70261], [25.10718, 43.6831], [24.96682, 43.72693], [24.73542, 43.68523], [24.62281, 43.74082], [24.50264, 43.76314], [24.35364, 43.70211], [24.18149, 43.68218], [23.73978, 43.80627], [23.61687, 43.79289], [23.4507, 43.84936], [23.26772, 43.84843], [23.05288, 43.79494]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BH", + iso1A3: "BHR", + iso1N3: "048", + wikidata: "Q398", + nameEn: "Bahrain", + groups: ["145", "142", "UN"], + callingCodes: ["973"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[50.93865, 26.30758], [50.71771, 26.73086], [50.38162, 26.53976], [50.26923, 26.08243], [50.302, 25.87592], [50.57069, 25.57887], [50.80824, 25.54641], [50.7801, 25.595], [50.86149, 25.6965], [50.81266, 25.88946], [50.93865, 26.30758]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BI", + iso1A3: "BDI", + iso1N3: "108", + wikidata: "Q967", + nameEn: "Burundi", + groups: ["014", "202", "002", "UN"], + callingCodes: ["257"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[30.54501, -2.41404], [30.42933, -2.31064], [30.14034, -2.43626], [29.95911, -2.33348], [29.88237, -2.75105], [29.36805, -2.82933], [29.32234, -2.6483], [29.0562, -2.58632], [29.04081, -2.7416], [29.00167, -2.78523], [29.00404, -2.81978], [29.0505, -2.81774], [29.09119, -2.87871], [29.09797, -2.91935], [29.16037, -2.95457], [29.17258, -2.99385], [29.25633, -3.05471], [29.21463, -3.3514], [29.23708, -3.75856], [29.43673, -4.44845], [29.63827, -4.44681], [29.75109, -4.45836], [29.77289, -4.41733], [29.82885, -4.36153], [29.88172, -4.35743], [30.03323, -4.26631], [30.22042, -4.01738], [30.45915, -3.56532], [30.84165, -3.25152], [30.83823, -2.97837], [30.6675, -2.98987], [30.57926, -2.89791], [30.4987, -2.9573], [30.40662, -2.86151], [30.52747, -2.65841], [30.41789, -2.66266], [30.54501, -2.41404]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BJ", + iso1A3: "BEN", + iso1N3: "204", + wikidata: "Q962", + nameEn: "Benin", + aliases: ["DY"], + groups: ["011", "202", "002", "UN"], + callingCodes: ["229"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[3.59375, 11.70269], [3.48187, 11.86092], [3.31613, 11.88495], [3.25352, 12.01467], [2.83978, 12.40585], [2.6593, 12.30631], [2.37783, 12.24804], [2.39657, 12.10952], [2.45824, 11.98672], [2.39723, 11.89473], [2.29983, 11.68254], [2.00988, 11.42227], [1.42823, 11.46822], [1.03409, 11.04719], [0.9813, 11.08876], [0.91245, 10.99597], [0.8804, 10.803], [0.80358, 10.71459], [0.77666, 10.37665], [1.35507, 9.99525], [1.36624, 9.5951], [1.33675, 9.54765], [1.41746, 9.3226], [1.5649, 9.16941], [1.61838, 9.0527], [1.64249, 6.99562], [1.55877, 6.99737], [1.61812, 6.74843], [1.58105, 6.68619], [1.76906, 6.43189], [1.79826, 6.28221], [1.62913, 6.24075], [1.67336, 6.02702], [2.74181, 6.13349], [2.70566, 6.38038], [2.70464, 6.50831], [2.74334, 6.57291], [2.7325, 6.64057], [2.78204, 6.70514], [2.78823, 6.76356], [2.73405, 6.78508], [2.74024, 6.92802], [2.71702, 6.95722], [2.76965, 7.13543], [2.74489, 7.42565], [2.79442, 7.43486], [2.78668, 7.5116], [2.73405, 7.5423], [2.73095, 7.7755], [2.67523, 7.87825], [2.77907, 9.06924], [3.08017, 9.10006], [3.14147, 9.28375], [3.13928, 9.47167], [3.25093, 9.61632], [3.34726, 9.70696], [3.32099, 9.78032], [3.35383, 9.83641], [3.54429, 9.87739], [3.66908, 10.18136], [3.57275, 10.27185], [3.6844, 10.46351], [3.78292, 10.40538], [3.84243, 10.59316], [3.71505, 11.13015], [3.49175, 11.29765], [3.59375, 11.70269]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BL", + iso1A3: "BLM", + iso1N3: "652", + wikidata: "Q25362", + nameEn: "Saint-Barth\xE9lemy", + country: "FR", + groups: ["Q1451600", "029", "003", "419", "019", "UN"], + callingCodes: ["590"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-62.62718, 18.26185], [-63.1055, 17.86651], [-62.34423, 17.49165], [-62.62718, 18.26185]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BM", + iso1A3: "BMU", + iso1N3: "060", + wikidata: "Q23635", + nameEn: "Bermuda", + country: "GB", + groups: ["BOTS", "021", "003", "019", "UN"], + driveSide: "left", + callingCodes: ["1 441"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-63.20987, 32.6953], [-65.31453, 32.68437], [-65.63955, 31.43417], [-63.20987, 32.6953]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BN", + iso1A3: "BRN", + iso1N3: "096", + wikidata: "Q921", + nameEn: "Brunei", + groups: ["Q36117", "035", "142", "UN"], + driveSide: "left", + callingCodes: ["673"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BO", + iso1A3: "BOL", + iso1N3: "068", + wikidata: "Q750", + nameEn: "Bolivia", + groups: ["005", "419", "019", "UN"], + callingCodes: ["591"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-63.90248, -12.52544], [-64.22539, -12.45267], [-64.30708, -12.46398], [-64.99778, -11.98604], [-65.30027, -11.48749], [-65.28141, -10.86289], [-65.35402, -10.78685], [-65.37923, -10.35141], [-65.29019, -9.86253], [-65.40615, -9.63894], [-65.56244, -9.84266], [-65.68343, -9.75323], [-67.17784, -10.34016], [-68.71533, -11.14749], [-68.7651, -11.0496], [-68.75179, -11.03688], [-68.75265, -11.02383], [-68.74802, -11.00891], [-69.42792, -10.93451], [-69.47839, -10.95254], [-69.57156, -10.94555], [-68.98115, -11.8979], [-68.65044, -12.50689], [-68.85615, -12.87769], [-68.8864, -13.40792], [-69.05265, -13.68546], [-68.88135, -14.18639], [-69.36254, -14.94634], [-69.14856, -15.23478], [-69.40336, -15.61358], [-69.20291, -16.16668], [-69.09986, -16.22693], [-68.96238, -16.194], [-68.79464, -16.33272], [-68.98358, -16.42165], [-69.04027, -16.57214], [-69.00853, -16.66769], [-69.16896, -16.72233], [-69.62883, -17.28142], [-69.46863, -17.37466], [-69.46897, -17.4988], [-69.46623, -17.60518], [-69.34126, -17.72753], [-69.28671, -17.94844], [-69.07496, -18.03715], [-69.14807, -18.16893], [-69.07432, -18.28259], [-68.94987, -18.93302], [-68.87082, -19.06003], [-68.80602, -19.08355], [-68.61989, -19.27584], [-68.41218, -19.40499], [-68.66761, -19.72118], [-68.54611, -19.84651], [-68.57132, -20.03134], [-68.74273, -20.08817], [-68.7276, -20.46178], [-68.44023, -20.62701], [-68.55383, -20.7355], [-68.53957, -20.91542], [-68.40403, -20.94562], [-68.18816, -21.28614], [-67.85114, -22.87076], [-67.54284, -22.89771], [-67.18382, -22.81525], [-66.7298, -22.23644], [-66.29714, -22.08741], [-66.24077, -21.77837], [-66.03836, -21.84829], [-66.04832, -21.9187], [-65.9261, -21.93335], [-65.7467, -22.10105], [-65.61166, -22.09504], [-65.58694, -22.09794], [-65.57743, -22.07675], [-65.47435, -22.08908], [-64.99524, -22.08255], [-64.90014, -22.12136], [-64.67174, -22.18957], [-64.58888, -22.25035], [-64.4176, -22.67692], [-64.35108, -22.73282], [-64.31489, -22.88824], [-64.22918, -22.55807], [-63.93287, -21.99934], [-63.70963, -21.99934], [-63.68113, -22.0544], [-63.66482, -21.99918], [-62.81124, -21.9987], [-62.8078, -22.12534], [-62.64455, -22.25091], [-62.2757, -21.06657], [-62.26883, -20.55311], [-61.93912, -20.10053], [-61.73723, -19.63958], [-60.00638, -19.2981], [-59.06965, -19.29148], [-58.23216, -19.80058], [-58.16225, -20.16193], [-57.8496, -19.98346], [-58.14215, -19.76276], [-57.78463, -19.03259], [-57.71113, -19.03161], [-57.69134, -19.00544], [-57.71995, -18.97546], [-57.71995, -18.89573], [-57.76764, -18.90087], [-57.56807, -18.25655], [-57.48237, -18.24219], [-57.69877, -17.8431], [-57.73949, -17.56095], [-57.90082, -17.44555], [-57.99661, -17.5273], [-58.32935, -17.28195], [-58.5058, -16.80958], [-58.30918, -16.3699], [-58.32431, -16.25861], [-58.41506, -16.32636], [-60.16069, -16.26479], [-60.23797, -15.50267], [-60.58224, -15.09887], [-60.23968, -15.09515], [-60.27887, -14.63021], [-60.46037, -14.22496], [-60.48053, -13.77981], [-61.05527, -13.50054], [-61.81151, -13.49564], [-63.76259, -12.42952], [-63.90248, -12.52544]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BQ", + iso1A3: "BES", + iso1N3: "535", + wikidata: "Q27561", + nameEn: "Caribbean Netherlands", + country: "NL" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "BR", + iso1A3: "BRA", + iso1N3: "076", + wikidata: "Q155", + nameEn: "Brazil", + groups: ["005", "419", "019", "UN"], + callingCodes: ["55"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-59.69361, 4.34069], [-59.78878, 4.45637], [-60.15953, 4.53456], [-60.04189, 4.69801], [-59.98129, 5.07097], [-60.20944, 5.28754], [-60.32352, 5.21299], [-60.73204, 5.20931], [-60.5802, 4.94312], [-60.86539, 4.70512], [-60.98303, 4.54167], [-61.15703, 4.49839], [-61.31457, 4.54167], [-61.29675, 4.44216], [-61.48569, 4.43149], [-61.54629, 4.2822], [-62.13094, 4.08309], [-62.44822, 4.18621], [-62.57656, 4.04754], [-62.74411, 4.03331], [-62.7655, 3.73099], [-62.98296, 3.59935], [-63.21111, 3.96219], [-63.4464, 3.9693], [-63.42233, 3.89995], [-63.50611, 3.83592], [-63.67099, 4.01731], [-63.70218, 3.91417], [-63.86082, 3.94796], [-63.99183, 3.90172], [-64.14512, 4.12932], [-64.57648, 4.12576], [-64.72977, 4.28931], [-64.84028, 4.24665], [-64.48379, 3.7879], [-64.02908, 2.79797], [-64.0257, 2.48156], [-63.39114, 2.4317], [-63.39827, 2.16098], [-64.06135, 1.94722], [-64.08274, 1.64792], [-64.34654, 1.35569], [-64.38932, 1.5125], [-65.11657, 1.12046], [-65.57288, 0.62856], [-65.50158, 0.92086], [-65.6727, 1.01353], [-66.28507, 0.74585], [-66.85795, 1.22998], [-67.08222, 1.17441], [-67.15784, 1.80439], [-67.299, 1.87494], [-67.40488, 2.22258], [-67.9292, 1.82455], [-68.18632, 2.00091], [-68.26699, 1.83463], [-68.18128, 1.72881], [-69.38621, 1.70865], [-69.53746, 1.76408], [-69.83491, 1.69353], [-69.82987, 1.07864], [-69.26017, 1.06856], [-69.14422, 0.84172], [-69.20976, 0.57958], [-69.47696, 0.71065], [-70.04162, 0.55437], [-70.03658, -0.19681], [-69.603, -0.51947], [-69.59796, -0.75136], [-69.4215, -1.01853], [-69.43395, -1.42219], [-69.94708, -4.2431], [-70.00888, -4.37833], [-70.11305, -4.27281], [-70.19582, -4.3607], [-70.33236, -4.15214], [-70.77601, -4.15717], [-70.96814, -4.36915], [-71.87003, -4.51661], [-72.64391, -5.0391], [-72.83973, -5.14765], [-73.24579, -6.05764], [-73.12983, -6.43852], [-73.73986, -6.87919], [-73.77011, -7.28944], [-73.96938, -7.58465], [-73.65485, -7.77897], [-73.76576, -7.89884], [-72.92886, -9.04074], [-73.21498, -9.40904], [-72.72216, -9.41397], [-72.31883, -9.5184], [-72.14742, -9.98049], [-71.23394, -9.9668], [-70.53373, -9.42628], [-70.58453, -9.58303], [-70.55429, -9.76692], [-70.62487, -9.80666], [-70.64134, -11.0108], [-70.51395, -10.92249], [-70.38791, -11.07096], [-69.90896, -10.92744], [-69.57835, -10.94051], [-69.57156, -10.94555], [-69.47839, -10.95254], [-69.42792, -10.93451], [-68.74802, -11.00891], [-68.75265, -11.02383], [-68.75179, -11.03688], [-68.7651, -11.0496], [-68.71533, -11.14749], [-67.17784, -10.34016], [-65.68343, -9.75323], [-65.56244, -9.84266], [-65.40615, -9.63894], [-65.29019, -9.86253], [-65.37923, -10.35141], [-65.35402, -10.78685], [-65.28141, -10.86289], [-65.30027, -11.48749], [-64.99778, -11.98604], [-64.30708, -12.46398], [-64.22539, -12.45267], [-63.90248, -12.52544], [-63.76259, -12.42952], [-61.81151, -13.49564], [-61.05527, -13.50054], [-60.48053, -13.77981], [-60.46037, -14.22496], [-60.27887, -14.63021], [-60.23968, -15.09515], [-60.58224, -15.09887], [-60.23797, -15.50267], [-60.16069, -16.26479], [-58.41506, -16.32636], [-58.32431, -16.25861], [-58.30918, -16.3699], [-58.5058, -16.80958], [-58.32935, -17.28195], [-57.99661, -17.5273], [-57.90082, -17.44555], [-57.73949, -17.56095], [-57.69877, -17.8431], [-57.48237, -18.24219], [-57.56807, -18.25655], [-57.76764, -18.90087], [-57.71995, -18.89573], [-57.71995, -18.97546], [-57.69134, -19.00544], [-57.71113, -19.03161], [-57.78463, -19.03259], [-58.14215, -19.76276], [-57.8496, -19.98346], [-58.16225, -20.16193], [-57.84536, -20.93155], [-57.93492, -21.65505], [-57.88239, -21.6868], [-57.94642, -21.73799], [-57.98625, -22.09157], [-56.6508, -22.28387], [-56.5212, -22.11556], [-56.45893, -22.08072], [-56.23206, -22.25347], [-55.8331, -22.29008], [-55.74941, -22.46436], [-55.741, -22.52018], [-55.72366, -22.5519], [-55.6986, -22.56268], [-55.68742, -22.58407], [-55.62493, -22.62765], [-55.63849, -22.95122], [-55.5446, -23.22811], [-55.52288, -23.2595], [-55.5555, -23.28237], [-55.43585, -23.87157], [-55.44117, -23.9185], [-55.41784, -23.9657], [-55.12292, -23.99669], [-55.0518, -23.98666], [-55.02691, -23.97317], [-54.6238, -23.83078], [-54.32807, -24.01865], [-54.28207, -24.07305], [-54.4423, -25.13381], [-54.62033, -25.46026], [-54.60196, -25.48397], [-54.59509, -25.53696], [-54.59398, -25.59224], [-54.5502, -25.58915], [-54.52926, -25.62846], [-53.90831, -25.55513], [-53.83691, -25.94849], [-53.73511, -26.04211], [-53.73086, -26.05842], [-53.7264, -26.0664], [-53.73391, -26.07006], [-53.73968, -26.10012], [-53.65018, -26.19501], [-53.65237, -26.23289], [-53.63739, -26.2496], [-53.63881, -26.25075], [-53.64632, -26.24798], [-53.64186, -26.25976], [-53.64505, -26.28089], [-53.68269, -26.33359], [-53.73372, -26.6131], [-53.80144, -27.09844], [-54.15978, -27.2889], [-54.19062, -27.27639], [-54.19268, -27.30751], [-54.41888, -27.40882], [-54.50416, -27.48232], [-54.67657, -27.57214], [-54.90159, -27.63132], [-54.90805, -27.73149], [-55.1349, -27.89759], [-55.16872, -27.86224], [-55.33303, -27.94661], [-55.6262, -28.17124], [-55.65418, -28.18304], [-56.01729, -28.51223], [-56.00458, -28.60421], [-56.05265, -28.62651], [-56.54171, -29.11447], [-56.57295, -29.11357], [-56.62789, -29.18073], [-56.81251, -29.48154], [-57.09386, -29.74211], [-57.65132, -30.19229], [-57.22502, -30.26121], [-56.90236, -30.02578], [-56.49267, -30.39471], [-56.4795, -30.3899], [-56.4619, -30.38457], [-55.87388, -31.05053], [-55.58866, -30.84117], [-55.5634, -30.8686], [-55.55373, -30.8732], [-55.55218, -30.88193], [-55.54572, -30.89051], [-55.53431, -30.89714], [-55.53276, -30.90218], [-55.52712, -30.89997], [-55.51862, -30.89828], [-55.50841, -30.9027], [-55.50821, -30.91349], [-54.17384, -31.86168], [-53.76024, -32.0751], [-53.39572, -32.58596], [-53.37671, -32.57005], [-53.1111, -32.71147], [-53.53459, -33.16843], [-53.52794, -33.68908], [-53.44031, -33.69344], [-53.39593, -33.75169], [-53.37138, -33.74313], [-52.83257, -34.01481], [-28.34015, -20.99094], [-28.99601, 1.86593], [-51.35485, 4.8383], [-51.63798, 4.51394], [-51.61983, 4.14596], [-51.79599, 3.89336], [-51.82312, 3.85825], [-51.85573, 3.83427], [-52.31787, 3.17896], [-52.6906, 2.37298], [-52.96539, 2.1881], [-53.78743, 2.34412], [-54.16286, 2.10779], [-54.6084, 2.32856], [-55.01919, 2.564], [-55.71493, 2.40342], [-55.96292, 2.53188], [-56.13054, 2.27723], [-55.92159, 2.05236], [-55.89863, 1.89861], [-55.99278, 1.83137], [-56.47045, 1.95135], [-56.7659, 1.89509], [-57.07092, 1.95304], [-57.09109, 2.01854], [-57.23981, 1.95808], [-57.35073, 1.98327], [-57.55743, 1.69605], [-57.77281, 1.73344], [-57.97336, 1.64566], [-58.01873, 1.51966], [-58.33887, 1.58014], [-58.4858, 1.48399], [-58.53571, 1.29154], [-58.84229, 1.17749], [-58.92072, 1.31293], [-59.25583, 1.40559], [-59.74066, 1.87596], [-59.7264, 2.27497], [-59.91177, 2.36759], [-59.99733, 2.92312], [-59.79769, 3.37162], [-59.86899, 3.57089], [-59.51963, 3.91951], [-59.73353, 4.20399], [-59.69361, 4.34069]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BS", + iso1A3: "BHS", + iso1N3: "044", + wikidata: "Q778", + nameEn: "The Bahamas", + groups: ["029", "003", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + callingCodes: ["1 242"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-72.98446, 20.4801], [-71.70065, 25.7637], [-78.91214, 27.76553], [-80.65727, 23.71953], [-72.98446, 20.4801]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BT", + iso1A3: "BTN", + iso1N3: "064", + wikidata: "Q917", + nameEn: "Bhutan", + groups: ["034", "142", "UN"], + driveSide: "left", + callingCodes: ["975"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[91.6469, 27.76358], [91.5629, 27.84823], [91.48973, 27.93903], [91.46327, 28.0064], [91.25779, 28.07509], [91.20019, 27.98715], [90.69894, 28.07784], [90.58842, 28.02838], [90.13387, 28.19178], [89.79762, 28.23979], [89.59525, 28.16433], [89.12825, 27.62502], [89.0582, 27.60985], [88.97213, 27.51671], [88.95355, 27.4106], [89.00216, 27.32532], [88.96947, 27.30319], [88.93678, 27.33777], [88.91901, 27.32483], [88.74219, 27.144], [88.86984, 27.10937], [88.8714, 26.97488], [88.92301, 26.99286], [88.95807, 26.92668], [89.09554, 26.89089], [89.12825, 26.81661], [89.1926, 26.81329], [89.37913, 26.86224], [89.38319, 26.85963], [89.3901, 26.84225], [89.42349, 26.83727], [89.63369, 26.74402], [89.86124, 26.73307], [90.04535, 26.72422], [90.30402, 26.85098], [90.39271, 26.90704], [90.48504, 26.8594], [90.67715, 26.77215], [91.50067, 26.79223], [91.83181, 26.87318], [92.05523, 26.8692], [92.11863, 26.893], [92.03457, 27.07334], [92.04702, 27.26861], [92.12019, 27.27829], [92.01132, 27.47352], [91.65007, 27.48287], [91.55819, 27.6144], [91.6469, 27.76358]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BV", + iso1A3: "BVT", + iso1N3: "074", + wikidata: "Q23408", + nameEn: "Bouvet Island", + country: "NO", + groups: ["005", "419", "019", "UN"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[4.54042, -54.0949], [2.28941, -54.13089], [3.35353, -55.17558], [4.54042, -54.0949]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BW", + iso1A3: "BWA", + iso1N3: "072", + wikidata: "Q963", + nameEn: "Botswana", + groups: ["018", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["267"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[25.26433, -17.79571], [25.16882, -17.78253], [25.05895, -17.84452], [24.95586, -17.79674], [24.73364, -17.89338], [24.71887, -17.9218], [24.6303, -17.9863], [24.57485, -18.07151], [24.40577, -17.95726], [24.19416, -18.01919], [23.61088, -18.4881], [23.29618, -17.99855], [23.0996, -18.00075], [21.45556, -18.31795], [20.99904, -18.31743], [20.99751, -22.00026], [19.99912, -21.99991], [19.99817, -24.76768], [20.02809, -24.78725], [20.03678, -24.81004], [20.29826, -24.94869], [20.64795, -25.47827], [20.86081, -26.14892], [20.61754, -26.4692], [20.63275, -26.78181], [20.68596, -26.9039], [20.87031, -26.80047], [21.13353, -26.86661], [21.37869, -26.82083], [21.69322, -26.86152], [21.7854, -26.79199], [21.77114, -26.69015], [21.83291, -26.65959], [21.90703, -26.66808], [22.06192, -26.61882], [22.21206, -26.3773], [22.41921, -26.23078], [22.56365, -26.19668], [22.70808, -25.99186], [22.86012, -25.50572], [23.03497, -25.29971], [23.47588, -25.29971], [23.9244, -25.64286], [24.18287, -25.62916], [24.36531, -25.773], [24.44703, -25.73021], [24.67319, -25.81749], [24.8946, -25.80723], [25.01718, -25.72507], [25.12266, -25.75931], [25.33076, -25.76616], [25.58543, -25.6343], [25.6643, -25.4491], [25.69661, -25.29284], [25.72702, -25.25503], [25.88571, -24.87802], [25.84295, -24.78661], [25.8515, -24.75727], [26.39409, -24.63468], [26.46346, -24.60358], [26.51667, -24.47219], [26.84165, -24.24885], [26.99749, -23.65486], [27.33768, -23.40917], [27.52393, -23.37952], [27.6066, -23.21894], [27.74154, -23.2137], [27.93539, -23.04941], [27.93729, -22.96194], [28.04752, -22.90243], [28.04562, -22.8394], [28.34874, -22.5694], [28.63287, -22.55887], [28.91889, -22.44299], [29.0151, -22.22907], [29.10881, -22.21202], [29.15268, -22.21399], [29.18974, -22.18599], [29.21955, -22.17771], [29.37703, -22.19581], [29.3533, -22.18363], [29.24648, -22.05967], [29.1974, -22.07472], [29.14501, -22.07275], [29.08495, -22.04867], [29.04108, -22.00563], [29.02191, -21.95665], [29.02191, -21.90647], [29.04023, -21.85864], [29.07763, -21.81877], [28.58114, -21.63455], [28.49942, -21.66634], [28.29416, -21.59037], [28.01669, -21.57624], [27.91407, -21.31621], [27.69171, -21.08409], [27.72972, -20.51735], [27.69361, -20.48531], [27.28865, -20.49873], [27.29831, -20.28935], [27.21278, -20.08244], [26.72246, -19.92707], [26.17227, -19.53709], [25.96226, -19.08152], [25.99837, -19.02943], [25.94326, -18.90362], [25.82353, -18.82808], [25.79217, -18.6355], [25.68859, -18.56165], [25.53465, -18.39041], [25.39972, -18.12691], [25.31799, -18.07091], [25.23909, -17.90832], [25.26433, -17.79571]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BY", + iso1A3: "BLR", + iso1N3: "112", + wikidata: "Q184", + nameEn: "Belarus", + groups: ["151", "150", "UN"], + callingCodes: ["375"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[28.15217, 56.16964], [27.97865, 56.11849], [27.63065, 55.89687], [27.61683, 55.78558], [27.3541, 55.8089], [27.27804, 55.78299], [27.1559, 55.85032], [26.97153, 55.8102], [26.87448, 55.7172], [26.76872, 55.67658], [26.71802, 55.70645], [26.64888, 55.70515], [26.63231, 55.67968], [26.63167, 55.57887], [26.55094, 55.5093], [26.5522, 55.40277], [26.44937, 55.34832], [26.5709, 55.32572], [26.6714, 55.33902], [26.80929, 55.31642], [26.83266, 55.30444], [26.835, 55.28182], [26.73017, 55.24226], [26.72983, 55.21788], [26.68075, 55.19787], [26.69243, 55.16718], [26.54753, 55.14181], [26.51481, 55.16051], [26.46249, 55.12814], [26.35121, 55.1525], [26.30628, 55.12536], [26.23202, 55.10439], [26.26941, 55.08032], [26.20397, 54.99729], [26.13386, 54.98924], [26.05907, 54.94631], [25.99129, 54.95705], [25.89462, 54.93438], [25.74122, 54.80108], [25.75977, 54.57252], [25.68045, 54.5321], [25.64813, 54.48704], [25.62203, 54.4656], [25.63371, 54.42075], [25.5376, 54.33158], [25.55425, 54.31591], [25.68513, 54.31727], [25.78553, 54.23327], [25.78563, 54.15747], [25.71084, 54.16704], [25.64875, 54.1259], [25.54724, 54.14925], [25.51452, 54.17799], [25.56823, 54.25212], [25.509, 54.30267], [25.35559, 54.26544], [25.22705, 54.26271], [25.19199, 54.219], [25.0728, 54.13419], [24.991, 54.14241], [24.96894, 54.17589], [24.77131, 54.11091], [24.85311, 54.02862], [24.74279, 53.96663], [24.69185, 53.96543], [24.69652, 54.01901], [24.62275, 54.00217], [24.44411, 53.90076], [24.34128, 53.90076], [24.19638, 53.96405], [23.98837, 53.92554], [23.95098, 53.9613], [23.81309, 53.94205], [23.80543, 53.89558], [23.71726, 53.93379], [23.61677, 53.92691], [23.51284, 53.95052], [23.62004, 53.60942], [23.81995, 53.24131], [23.85657, 53.22923], [23.91393, 53.16469], [23.87548, 53.0831], [23.92184, 53.02079], [23.94689, 52.95919], [23.91805, 52.94016], [23.93763, 52.71332], [23.73615, 52.6149], [23.58296, 52.59868], [23.45112, 52.53774], [23.34141, 52.44845], [23.18196, 52.28812], [23.20071, 52.22848], [23.47859, 52.18215], [23.54314, 52.12148], [23.61, 52.11264], [23.64066, 52.07626], [23.68733, 51.9906], [23.61523, 51.92066], [23.62691, 51.78208], [23.53198, 51.74298], [23.57053, 51.55938], [23.56236, 51.53673], [23.62751, 51.50512], [23.6736, 51.50255], [23.60906, 51.62122], [23.7766, 51.66809], [23.91118, 51.63316], [23.8741, 51.59734], [23.99907, 51.58369], [24.13075, 51.66979], [24.3163, 51.75063], [24.29021, 51.80841], [24.37123, 51.88222], [24.98784, 51.91273], [25.20228, 51.97143], [25.46163, 51.92205], [25.73673, 51.91973], [25.80574, 51.94556], [25.83217, 51.92587], [26.00408, 51.92967], [26.19084, 51.86781], [26.39367, 51.87315], [26.46962, 51.80501], [26.69759, 51.82284], [26.80043, 51.75777], [26.9489, 51.73788], [26.99422, 51.76933], [27.20602, 51.77291], [27.20948, 51.66713], [27.26613, 51.65957], [27.24828, 51.60161], [27.47212, 51.61184], [27.51058, 51.5854], [27.55727, 51.63486], [27.71932, 51.60672], [27.67125, 51.50854], [27.76052, 51.47604], [27.85253, 51.62293], [27.91844, 51.61952], [27.95827, 51.56065], [28.10658, 51.57857], [28.23452, 51.66988], [28.37592, 51.54505], [28.47051, 51.59734], [28.64429, 51.5664], [28.69161, 51.44695], [28.73143, 51.46236], [28.75615, 51.41442], [28.78224, 51.45294], [28.76027, 51.48802], [28.81795, 51.55552], [28.95528, 51.59222], [28.99098, 51.56833], [29.1187, 51.65872], [29.16402, 51.64679], [29.20659, 51.56918], [29.25603, 51.57089], [29.25191, 51.49828], [29.32881, 51.37843], [29.42357, 51.4187], [29.49773, 51.39814], [29.54372, 51.48372], [29.7408, 51.53417], [29.77376, 51.4461], [30.17888, 51.51025], [30.34642, 51.42555], [30.36153, 51.33984], [30.56203, 51.25655], [30.64992, 51.35014], [30.51946, 51.59649], [30.68804, 51.82806], [30.76443, 51.89739], [30.90897, 52.00699], [30.95589, 52.07775], [31.13332, 52.1004], [31.25142, 52.04131], [31.38326, 52.12991], [31.7822, 52.11406], [31.77877, 52.18636], [31.6895, 52.1973], [31.70735, 52.26711], [31.57971, 52.32146], [31.62084, 52.33849], [31.61397, 52.48843], [31.56316, 52.51518], [31.63869, 52.55361], [31.50406, 52.69707], [31.57277, 52.71613], [31.592, 52.79011], [31.35667, 52.97854], [31.24147, 53.031], [31.32283, 53.04101], [31.33519, 53.08805], [31.3915, 53.09712], [31.36403, 53.13504], [31.40523, 53.21406], [31.56316, 53.19432], [31.62496, 53.22886], [31.787, 53.18033], [31.82373, 53.10042], [32.15368, 53.07594], [32.40773, 53.18856], [32.51725, 53.28431], [32.73257, 53.33494], [32.74968, 53.45597], [32.47777, 53.5548], [32.40499, 53.6656], [32.50112, 53.68594], [32.45717, 53.74039], [32.36663, 53.7166], [32.12621, 53.81586], [31.89137, 53.78099], [31.77028, 53.80015], [31.85019, 53.91801], [31.88744, 54.03653], [31.89599, 54.0837], [31.57002, 54.14535], [31.30791, 54.25315], [31.3177, 54.34067], [31.22945, 54.46585], [31.08543, 54.50361], [31.21399, 54.63113], [31.19339, 54.66947], [30.99187, 54.67046], [30.98226, 54.68872], [31.0262, 54.70698], [30.97127, 54.71967], [30.95479, 54.74346], [30.75165, 54.80699], [30.8264, 54.90062], [30.81759, 54.94064], [30.93144, 54.9585], [30.95754, 54.98609], [30.9081, 55.02232], [30.94243, 55.03964], [31.00972, 55.02783], [31.02071, 55.06167], [30.97369, 55.17134], [30.87944, 55.28223], [30.81946, 55.27931], [30.8257, 55.3313], [30.93144, 55.3914], [30.90123, 55.46621], [30.95204, 55.50667], [30.93419, 55.6185], [30.86003, 55.63169], [30.7845, 55.58514], [30.72957, 55.66268], [30.67464, 55.64176], [30.63344, 55.73079], [30.51037, 55.76568], [30.51346, 55.78982], [30.48257, 55.81066], [30.30987, 55.83592], [30.27776, 55.86819], [30.12136, 55.8358], [29.97975, 55.87281], [29.80672, 55.79569], [29.61446, 55.77716], [29.51283, 55.70294], [29.3604, 55.75862], [29.44692, 55.95978], [29.21717, 55.98971], [29.08299, 56.03427], [28.73418, 55.97131], [28.63668, 56.07262], [28.68337, 56.10173], [28.5529, 56.11705], [28.43068, 56.09407], [28.37987, 56.11399], [28.36888, 56.05805], [28.30571, 56.06035], [28.15217, 56.16964]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "BZ", + iso1A3: "BLZ", + iso1N3: "084", + wikidata: "Q242", + nameEn: "Belize", + groups: ["013", "003", "419", "019", "UN"], + roadSpeedUnit: "mph", + callingCodes: ["501"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-88.3268, 18.49048], [-88.48242, 18.49164], [-88.71505, 18.0707], [-88.8716, 17.89535], [-89.03839, 18.0067], [-89.15105, 17.95104], [-89.14985, 17.81563], [-89.15025, 17.04813], [-89.22683, 15.88619], [-89.17418, 15.90898], [-89.02415, 15.9063], [-88.95358, 15.88698], [-88.40779, 16.09624], [-86.92368, 17.61462], [-87.84815, 18.18511], [-87.85693, 18.18266], [-87.86657, 18.19971], [-87.87604, 18.18313], [-87.90671, 18.15213], [-88.03165, 18.16657], [-88.03238, 18.41778], [-88.26593, 18.47617], [-88.29909, 18.47591], [-88.3268, 18.49048]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CA", + iso1A3: "CAN", + iso1N3: "124", + wikidata: "Q16", + nameEn: "Canada", + groups: ["021", "003", "019", "UN"], + callingCodes: ["1"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-67.20349, 45.1722], [-67.19603, 45.16771], [-67.15965, 45.16179], [-67.11316, 45.11176], [-67.0216, 44.95333], [-66.96824, 44.90965], [-66.98249, 44.87071], [-66.96824, 44.83078], [-66.93432, 44.82597], [-67.16117, 44.20069], [-61.98255, 37.34815], [-56.27503, 47.39728], [-53.12387, 41.40385], [-46.37635, 57.3249], [-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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CC", + iso1A3: "CCK", + iso1N3: "166", + wikidata: "Q36004", + nameEn: "Cocos (Keeling) Islands", + country: "AU", + groups: ["053", "009", "UN"], + driveSide: "left", + callingCodes: ["61"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[96.61846, -10.82438], [96.02343, -12.68334], [97.93979, -12.33309], [96.61846, -10.82438]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CD", + iso1A3: "COD", + iso1N3: "180", + wikidata: "Q974", + nameEn: "Democratic Republic of the Congo", + aliases: ["ZR"], + groups: ["017", "202", "002", "UN"], + callingCodes: ["243"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[27.44012, 5.07349], [27.09575, 5.22305], [26.93064, 5.13535], [26.85579, 5.03887], [26.74572, 5.10685], [26.48595, 5.04984], [26.13371, 5.25594], [25.86073, 5.19455], [25.53271, 5.37431], [25.34558, 5.29101], [25.31256, 5.03668], [24.71816, 4.90509], [24.46719, 5.0915], [23.38847, 4.60013], [22.94817, 4.82392], [22.89094, 4.79321], [22.84691, 4.69887], [22.78526, 4.71423], [22.6928, 4.47285], [22.60915, 4.48821], [22.5431, 4.22041], [22.45504, 4.13039], [22.27682, 4.11347], [22.10721, 4.20723], [21.6405, 4.317], [21.55904, 4.25553], [21.25744, 4.33676], [21.21341, 4.29285], [21.11214, 4.33895], [21.08793, 4.39603], [20.90383, 4.44877], [20.60184, 4.42394], [18.62755, 3.47564], [18.63857, 3.19342], [18.10683, 2.26876], [18.08034, 1.58553], [17.85887, 1.04327], [17.86989, 0.58873], [17.95255, 0.48128], [17.93877, 0.32424], [17.81204, 0.23884], [17.66051, -0.26535], [17.72112, -0.52707], [17.32438, -0.99265], [16.97999, -1.12762], [16.70724, -1.45815], [16.50336, -1.8795], [16.16173, -2.16586], [16.22785, -2.59528], [16.1755, -3.25014], [16.21407, -3.2969], [15.89448, -3.9513], [15.53081, -4.042], [15.48121, -4.22062], [15.41785, -4.28381], [15.32693, -4.27282], [15.25411, -4.31121], [15.1978, -4.32388], [14.83101, -4.80838], [14.67948, -4.92093], [14.5059, -4.84956], [14.41499, -4.8825], [14.37366, -4.56125], [14.47284, -4.42941], [14.3957, -4.36623], [14.40672, -4.28381], [13.9108, -4.50906], [13.81162, -4.41842], [13.71794, -4.44864], [13.70417, -4.72601], [13.50305, -4.77818], [13.41764, -4.89897], [13.11182, -4.5942], [13.09648, -4.63739], [13.11195, -4.67745], [12.8733, -4.74346], [12.70868, -4.95505], [12.63465, -4.94632], [12.60251, -5.01715], [12.46297, -5.09408], [12.49815, -5.14058], [12.51589, -5.1332], [12.53586, -5.14658], [12.53599, -5.1618], [12.52301, -5.17481], [12.52318, -5.74353], [12.26557, -5.74031], [12.20376, -5.76338], [11.95767, -5.94705], [12.42245, -6.07585], [13.04371, -5.87078], [16.55507, -5.85631], [16.96282, -7.21787], [17.5828, -8.13784], [18.33635, -8.00126], [19.33698, -7.99743], [19.5469, -7.00195], [20.30218, -6.98955], [20.31846, -6.91953], [20.61689, -6.90876], [20.56263, -7.28566], [21.79824, -7.29628], [21.84856, -9.59871], [22.19039, -9.94628], [22.32604, -10.76291], [22.17954, -10.85884], [22.25951, -11.24911], [22.54205, -11.05784], [23.16602, -11.10577], [23.45631, -10.946], [23.86868, -11.02856], [24.00027, -10.89356], [24.34528, -11.06816], [24.42612, -11.44975], [25.34069, -11.19707], [25.33058, -11.65767], [26.01777, -11.91488], [26.88687, -12.01868], [27.04351, -11.61312], [27.22541, -11.60323], [27.21025, -11.76157], [27.59932, -12.22123], [28.33199, -12.41375], [29.01918, -13.41353], [29.60531, -13.21685], [29.65078, -13.41844], [29.81551, -13.44683], [29.8139, -12.14898], [29.48404, -12.23604], [29.4992, -12.43843], [29.18592, -12.37921], [28.48357, -11.87532], [28.37241, -11.57848], [28.65032, -10.65133], [28.62795, -9.92942], [28.68532, -9.78], [28.56208, -9.49122], [28.51627, -9.44726], [28.52636, -9.35379], [28.36562, -9.30091], [28.38526, -9.23393], [28.9711, -8.66935], [28.88917, -8.4831], [30.79243, -8.27382], [30.2567, -7.14121], [29.52552, -6.2731], [29.43673, -4.44845], [29.23708, -3.75856], [29.21463, -3.3514], [29.25633, -3.05471], [29.17258, -2.99385], [29.16037, -2.95457], [29.09797, -2.91935], [29.09119, -2.87871], [29.0505, -2.81774], [29.00404, -2.81978], [29.00167, -2.78523], [29.04081, -2.7416], [29.00357, -2.70596], [28.94346, -2.69124], [28.89793, -2.66111], [28.90226, -2.62385], [28.89288, -2.55848], [28.87943, -2.55165], [28.86193, -2.53185], [28.86209, -2.5231], [28.87497, -2.50887], [28.88846, -2.50493], [28.89342, -2.49017], [28.89132, -2.47557], [28.86846, -2.44866], [28.86826, -2.41888], [28.89601, -2.37321], [28.95642, -2.37321], [29.00051, -2.29001], [29.105, -2.27043], [29.17562, -2.12278], [29.11847, -1.90576], [29.24458, -1.69663], [29.24323, -1.66826], [29.36322, -1.50887], [29.45038, -1.5054], [29.53062, -1.40499], [29.59061, -1.39016], [29.58388, -0.89821], [29.63006, -0.8997], [29.62708, -0.71055], [29.67176, -0.55714], [29.67474, -0.47969], [29.65091, -0.46777], [29.72687, -0.08051], [29.7224, 0.07291], [29.77454, 0.16675], [29.81922, 0.16824], [29.87284, 0.39166], [29.97413, 0.52124], [29.95477, 0.64486], [29.98307, 0.84295], [30.1484, 0.89805], [30.22139, 0.99635], [30.24671, 1.14974], [30.48503, 1.21675], [31.30127, 2.11006], [31.28042, 2.17853], [31.20148, 2.2217], [31.1985, 2.29462], [31.12104, 2.27676], [31.07934, 2.30207], [31.06593, 2.35862], [30.96911, 2.41071], [30.91102, 2.33332], [30.83059, 2.42559], [30.74271, 2.43601], [30.75612, 2.5863], [30.8857, 2.83923], [30.8574, 2.9508], [30.77101, 3.04897], [30.84251, 3.26908], [30.93486, 3.40737], [30.94081, 3.50847], [30.85153, 3.48867], [30.85997, 3.5743], [30.80713, 3.60506], [30.78512, 3.67097], [30.56277, 3.62703], [30.57378, 3.74567], [30.55396, 3.84451], [30.47691, 3.83353], [30.27658, 3.95653], [30.22374, 3.93896], [30.1621, 4.10586], [30.06964, 4.13221], [29.79666, 4.37809], [29.82087, 4.56246], [29.49726, 4.7007], [29.43341, 4.50101], [29.22207, 4.34297], [29.03054, 4.48784], [28.8126, 4.48784], [28.6651, 4.42638], [28.20719, 4.35614], [27.79551, 4.59976], [27.76469, 4.79284], [27.65462, 4.89375], [27.56656, 4.89375], [27.44012, 5.07349]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CF", + iso1A3: "CAF", + iso1N3: "140", + wikidata: "Q929", + nameEn: "Central African Republic", + groups: ["017", "202", "002", "UN"], + callingCodes: ["236"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[22.87758, 10.91915], [22.45889, 11.00246], [21.72139, 10.64136], [21.71479, 10.29932], [21.63553, 10.217], [21.52766, 10.2105], [21.34934, 9.95907], [21.26348, 9.97642], [20.82979, 9.44696], [20.36748, 9.11019], [19.06421, 9.00367], [18.86388, 8.87971], [19.11044, 8.68172], [18.79783, 8.25929], [18.67455, 8.22226], [18.62612, 8.14163], [18.64153, 8.08714], [18.6085, 8.05009], [18.02731, 8.01085], [17.93926, 7.95853], [17.67288, 7.98905], [16.8143, 7.53971], [16.6668, 7.67281], [16.658, 7.75353], [16.59415, 7.76444], [16.58315, 7.88657], [16.41583, 7.77971], [16.40703, 7.68809], [15.79942, 7.44149], [15.73118, 7.52006], [15.49743, 7.52179], [15.23397, 7.25135], [15.04717, 6.77085], [14.96311, 6.75693], [14.79966, 6.39043], [14.80122, 6.34866], [14.74206, 6.26356], [14.56149, 6.18928], [14.43073, 6.08867], [14.42917, 6.00508], [14.49455, 5.91683], [14.60974, 5.91838], [14.62375, 5.70466], [14.58951, 5.59777], [14.62531, 5.51411], [14.52724, 5.28319], [14.57083, 5.23979], [14.65489, 5.21343], [14.73383, 4.6135], [15.00825, 4.41458], [15.08609, 4.30282], [15.10644, 4.1362], [15.17482, 4.05131], [15.07686, 4.01805], [15.73522, 3.24348], [15.77725, 3.26835], [16.05449, 3.02306], [16.08252, 2.45708], [16.19357, 2.21537], [16.50126, 2.84739], [16.46701, 2.92512], [16.57598, 3.47999], [16.68283, 3.54257], [17.01746, 3.55136], [17.35649, 3.63045], [17.46876, 3.70515], [17.60966, 3.63705], [17.83421, 3.61068], [17.85842, 3.53378], [18.05656, 3.56893], [18.14902, 3.54476], [18.17323, 3.47665], [18.24148, 3.50302], [18.2723, 3.57992], [18.39558, 3.58212], [18.49245, 3.63924], [18.58711, 3.49423], [18.62755, 3.47564], [20.60184, 4.42394], [20.90383, 4.44877], [21.08793, 4.39603], [21.11214, 4.33895], [21.21341, 4.29285], [21.25744, 4.33676], [21.55904, 4.25553], [21.6405, 4.317], [22.10721, 4.20723], [22.27682, 4.11347], [22.45504, 4.13039], [22.5431, 4.22041], [22.60915, 4.48821], [22.6928, 4.47285], [22.78526, 4.71423], [22.84691, 4.69887], [22.89094, 4.79321], [22.94817, 4.82392], [23.38847, 4.60013], [24.46719, 5.0915], [24.71816, 4.90509], [25.31256, 5.03668], [25.34558, 5.29101], [25.53271, 5.37431], [25.86073, 5.19455], [26.13371, 5.25594], [26.48595, 5.04984], [26.74572, 5.10685], [26.85579, 5.03887], [26.93064, 5.13535], [27.09575, 5.22305], [27.44012, 5.07349], [27.26886, 5.25876], [27.23017, 5.37167], [27.28621, 5.56382], [27.22705, 5.62889], [27.22705, 5.71254], [26.51721, 6.09655], [26.58259, 6.1987], [26.32729, 6.36272], [26.38022, 6.63493], [25.90076, 7.09549], [25.37461, 7.33024], [25.35281, 7.42595], [25.20337, 7.50312], [25.20649, 7.61115], [25.29214, 7.66675], [25.25319, 7.8487], [24.98855, 7.96588], [24.85156, 8.16933], [24.35965, 8.26177], [24.13238, 8.36959], [24.25691, 8.69288], [23.51905, 8.71749], [23.59065, 8.99743], [23.44744, 8.99128], [23.4848, 9.16959], [23.56263, 9.19418], [23.64358, 9.28637], [23.64981, 9.44303], [23.62179, 9.53823], [23.69155, 9.67566], [23.67164, 9.86923], [23.3128, 10.45214], [23.02221, 10.69235], [22.87758, 10.91915]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CG", + iso1A3: "COG", + iso1N3: "178", + wikidata: "Q971", + nameEn: "Republic of the Congo", + groups: ["017", "202", "002", "UN"], + callingCodes: ["242"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[18.62755, 3.47564], [18.58711, 3.49423], [18.49245, 3.63924], [18.39558, 3.58212], [18.2723, 3.57992], [18.24148, 3.50302], [18.17323, 3.47665], [18.14902, 3.54476], [18.05656, 3.56893], [17.85842, 3.53378], [17.83421, 3.61068], [17.60966, 3.63705], [17.46876, 3.70515], [17.35649, 3.63045], [17.01746, 3.55136], [16.68283, 3.54257], [16.57598, 3.47999], [16.46701, 2.92512], [16.50126, 2.84739], [16.19357, 2.21537], [16.15568, 2.18955], [16.08563, 2.19733], [16.05294, 1.9811], [16.14634, 1.70259], [16.02647, 1.65591], [16.02959, 1.76483], [15.48942, 1.98265], [15.34776, 1.91264], [15.22634, 2.03243], [15.00996, 1.98887], [14.61145, 2.17866], [13.29457, 2.16106], [13.13461, 1.57238], [13.25447, 1.32339], [13.15519, 1.23368], [13.89582, 1.4261], [14.25186, 1.39842], [14.48179, 0.9152], [14.26066, 0.57255], [14.10909, 0.58563], [13.88648, 0.26652], [13.90632, -0.2287], [14.06862, -0.20826], [14.2165, -0.38261], [14.41887, -0.44799], [14.52569, -0.57818], [14.41838, -1.89412], [14.25932, -1.97624], [14.23518, -2.15671], [14.16202, -2.23916], [14.23829, -2.33715], [14.10442, -2.49268], [13.85846, -2.46935], [13.92073, -2.35581], [13.75884, -2.09293], [13.47977, -2.43224], [13.02759, -2.33098], [12.82172, -1.91091], [12.61312, -1.8129], [12.44656, -1.92025], [12.47925, -2.32626], [12.04895, -2.41704], [11.96866, -2.33559], [11.74605, -2.39936], [11.57637, -2.33379], [11.64487, -2.61865], [11.5359, -2.85654], [11.64798, -2.81146], [11.80365, -3.00424], [11.70558, -3.0773], [11.70227, -3.17465], [11.96554, -3.30267], [11.8318, -3.5812], [11.92719, -3.62768], [11.87083, -3.71571], [11.68608, -3.68942], [11.57949, -3.52798], [11.48764, -3.51089], [11.22301, -3.69888], [11.12647, -3.94169], [10.75913, -4.39519], [11.50888, -5.33417], [12.00924, -5.02627], [12.16068, -4.90089], [12.20901, -4.75642], [12.25587, -4.79437], [12.32324, -4.78415], [12.40964, -4.60609], [12.64835, -4.55937], [12.76844, -4.38709], [12.87096, -4.40315], [12.91489, -4.47907], [13.09648, -4.63739], [13.11182, -4.5942], [13.41764, -4.89897], [13.50305, -4.77818], [13.70417, -4.72601], [13.71794, -4.44864], [13.81162, -4.41842], [13.9108, -4.50906], [14.40672, -4.28381], [14.3957, -4.36623], [14.47284, -4.42941], [14.37366, -4.56125], [14.41499, -4.8825], [14.5059, -4.84956], [14.67948, -4.92093], [14.83101, -4.80838], [15.1978, -4.32388], [15.25411, -4.31121], [15.32693, -4.27282], [15.41785, -4.28381], [15.48121, -4.22062], [15.53081, -4.042], [15.89448, -3.9513], [16.21407, -3.2969], [16.1755, -3.25014], [16.22785, -2.59528], [16.16173, -2.16586], [16.50336, -1.8795], [16.70724, -1.45815], [16.97999, -1.12762], [17.32438, -0.99265], [17.72112, -0.52707], [17.66051, -0.26535], [17.81204, 0.23884], [17.93877, 0.32424], [17.95255, 0.48128], [17.86989, 0.58873], [17.85887, 1.04327], [18.08034, 1.58553], [18.10683, 2.26876], [18.63857, 3.19342], [18.62755, 3.47564]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CH", + iso1A3: "CHE", + iso1N3: "756", + wikidata: "Q39", + nameEn: "Switzerland", + groups: ["155", "150", "UN"], + callingCodes: ["41"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[8.72809, 47.69282], [8.72617, 47.69651], [8.73671, 47.7169], [8.70543, 47.73121], [8.74251, 47.75168], [8.71778, 47.76571], [8.68985, 47.75686], [8.68022, 47.78599], [8.65292, 47.80066], [8.64425, 47.76398], [8.62408, 47.7626], [8.61657, 47.79998], [8.56415, 47.80633], [8.56814, 47.78001], [8.48868, 47.77215], [8.45771, 47.7493], [8.44807, 47.72426], [8.40569, 47.69855], [8.4211, 47.68407], [8.40473, 47.67499], [8.41346, 47.66676], [8.42264, 47.66667], [8.44711, 47.65379], [8.4667, 47.65747], [8.46605, 47.64103], [8.49656, 47.64709], [8.5322, 47.64687], [8.52801, 47.66059], [8.56141, 47.67088], [8.57683, 47.66158], [8.6052, 47.67258], [8.61113, 47.66332], [8.62884, 47.65098], [8.62049, 47.63757], [8.60412, 47.63735], [8.61471, 47.64514], [8.60701, 47.65271], [8.59545, 47.64298], [8.60348, 47.61204], [8.57586, 47.59537], [8.55756, 47.62394], [8.51686, 47.63476], [8.50747, 47.61897], [8.45578, 47.60121], [8.46637, 47.58389], [8.48949, 47.588], [8.49431, 47.58107], [8.43235, 47.56617], [8.39477, 47.57826], [8.38273, 47.56608], [8.32735, 47.57133], [8.30277, 47.58607], [8.29524, 47.5919], [8.29722, 47.60603], [8.2824, 47.61225], [8.26313, 47.6103], [8.25863, 47.61571], [8.23809, 47.61204], [8.22577, 47.60385], [8.22011, 47.6181], [8.20617, 47.62141], [8.19378, 47.61636], [8.1652, 47.5945], [8.14947, 47.59558], [8.13823, 47.59147], [8.13662, 47.58432], [8.11543, 47.5841], [8.10395, 47.57918], [8.10002, 47.56504], [8.08557, 47.55768], [8.06663, 47.56374], [8.04383, 47.55443], [8.02136, 47.55096], [8.00113, 47.55616], [7.97581, 47.55493], [7.95682, 47.55789], [7.94494, 47.54511], [7.91251, 47.55031], [7.90673, 47.57674], [7.88664, 47.58854], [7.84412, 47.5841], [7.81901, 47.58798], [7.79486, 47.55691], [7.75261, 47.54599], [7.71961, 47.54219], [7.69642, 47.53297], [7.68101, 47.53232], [7.6656, 47.53752], [7.66174, 47.54554], [7.65083, 47.54662], [7.63338, 47.56256], [7.67655, 47.56435], [7.68904, 47.57133], [7.67115, 47.5871], [7.68486, 47.59601], [7.69385, 47.60099], [7.68229, 47.59905], [7.67395, 47.59212], [7.64599, 47.59695], [7.64213, 47.5944], [7.64309, 47.59151], [7.61929, 47.57683], [7.60459, 47.57869], [7.60523, 47.58519], [7.58945, 47.59017], [7.58386, 47.57536], [7.56684, 47.57785], [7.56548, 47.57617], [7.55689, 47.57232], [7.55652, 47.56779], [7.53634, 47.55553], [7.52831, 47.55347], [7.51723, 47.54578], [7.50873, 47.54546], [7.49691, 47.53821], [7.50588, 47.52856], [7.51904, 47.53515], [7.53199, 47.5284], [7.5229, 47.51644], [7.49804, 47.51798], [7.51076, 47.49651], [7.47534, 47.47932], [7.43356, 47.49712], [7.42923, 47.48628], [7.4583, 47.47216], [7.4462, 47.46264], [7.43088, 47.45846], [7.40308, 47.43638], [7.35603, 47.43432], [7.33526, 47.44186], [7.24669, 47.4205], [7.17026, 47.44312], [7.19583, 47.49455], [7.16249, 47.49025], [7.12781, 47.50371], [7.07425, 47.48863], [7.0231, 47.50522], [6.98425, 47.49432], [7.0024, 47.45264], [6.93953, 47.43388], [6.93744, 47.40714], [6.88542, 47.37262], [6.87959, 47.35335], [7.03125, 47.36996], [7.0564, 47.35134], [7.05305, 47.33304], [6.94316, 47.28747], [6.95108, 47.26428], [6.9508, 47.24338], [6.8489, 47.15933], [6.76788, 47.1208], [6.68823, 47.06616], [6.71531, 47.0494], [6.43341, 46.92703], [6.46456, 46.88865], [6.43216, 46.80336], [6.45209, 46.77502], [6.38351, 46.73171], [6.27135, 46.68251], [6.11084, 46.57649], [6.1567, 46.54402], [6.07269, 46.46244], [6.08427, 46.44305], [6.06407, 46.41676], [6.09926, 46.40768], [6.15016, 46.3778], [6.15985, 46.37721], [6.16987, 46.36759], [6.15738, 46.3491], [6.13876, 46.33844], [6.1198, 46.31157], [6.11697, 46.29547], [6.1013, 46.28512], [6.11926, 46.2634], [6.12446, 46.25059], [6.10071, 46.23772], [6.08563, 46.24651], [6.07072, 46.24085], [6.0633, 46.24583], [6.05029, 46.23518], [6.04602, 46.23127], [6.03342, 46.2383], [6.02461, 46.23313], [5.97542, 46.21525], [5.96515, 46.19638], [5.99573, 46.18587], [5.98846, 46.17046], [5.98188, 46.17392], [5.97508, 46.15863], [5.9641, 46.14412], [5.95781, 46.12925], [5.97893, 46.13303], [5.9871, 46.14499], [6.01791, 46.14228], [6.03614, 46.13712], [6.04564, 46.14031], [6.05203, 46.15191], [6.07491, 46.14879], [6.09199, 46.15191], [6.09926, 46.14373], [6.13397, 46.1406], [6.15305, 46.15194], [6.18116, 46.16187], [6.18871, 46.16644], [6.18707, 46.17999], [6.19552, 46.18401], [6.19807, 46.18369], [6.20539, 46.19163], [6.21114, 46.1927], [6.21273, 46.19409], [6.21603, 46.19507], [6.21844, 46.19837], [6.22222, 46.19888], [6.22175, 46.20045], [6.23544, 46.20714], [6.23913, 46.20511], [6.24821, 46.20531], [6.26007, 46.21165], [6.27694, 46.21566], [6.29663, 46.22688], [6.31041, 46.24417], [6.29474, 46.26221], [6.26749, 46.24745], [6.24952, 46.26255], [6.23775, 46.27822], [6.25137, 46.29014], [6.24826, 46.30175], [6.21981, 46.31304], [6.25432, 46.3632], [6.53358, 46.45431], [6.82312, 46.42661], [6.8024, 46.39171], [6.77152, 46.34784], [6.86052, 46.28512], [6.78968, 46.14058], [6.89321, 46.12548], [6.87868, 46.03855], [6.93862, 46.06502], [7.00946, 45.9944], [7.04151, 45.92435], [7.10685, 45.85653], [7.56343, 45.97421], [7.85949, 45.91485], [7.9049, 45.99945], [7.98881, 45.99867], [8.02906, 46.10331], [8.11383, 46.11577], [8.16866, 46.17817], [8.08814, 46.26692], [8.31162, 46.38044], [8.30648, 46.41587], [8.42464, 46.46367], [8.46317, 46.43712], [8.45032, 46.26869], [8.62242, 46.12112], [8.75697, 46.10395], [8.80778, 46.10085], [8.85617, 46.0748], [8.79414, 46.00913], [8.78585, 45.98973], [8.79362, 45.99207], [8.8319, 45.9879], [8.85121, 45.97239], [8.86688, 45.96135], [8.88904, 45.95465], [8.93649, 45.86775], [8.94372, 45.86587], [8.93504, 45.86245], [8.91129, 45.8388], [8.94737, 45.84285], [8.9621, 45.83707], [8.99663, 45.83466], [9.00324, 45.82055], [9.0298, 45.82127], [9.03279, 45.82865], [9.03793, 45.83548], [9.03505, 45.83976], [9.04059, 45.8464], [9.04546, 45.84968], [9.06642, 45.8761], [9.09065, 45.89906], [8.99257, 45.9698], [9.01618, 46.04928], [9.24503, 46.23616], [9.29226, 46.32717], [9.25502, 46.43743], [9.28136, 46.49685], [9.36128, 46.5081], [9.40487, 46.46621], [9.45936, 46.50873], [9.46117, 46.37481], [9.57015, 46.2958], [9.71273, 46.29266], [9.73086, 46.35071], [9.95249, 46.38045], [10.07055, 46.21668], [10.14439, 46.22992], [10.17862, 46.25626], [10.10506, 46.3372], [10.165, 46.41051], [10.03715, 46.44479], [10.10307, 46.61003], [10.23674, 46.63484], [10.25309, 46.57432], [10.46136, 46.53164], [10.49375, 46.62049], [10.44686, 46.64162], [10.40475, 46.63671], [10.38659, 46.67847], [10.47197, 46.85698], [10.48376, 46.93891], [10.36933, 47.00212], [10.30031, 46.92093], [10.24128, 46.93147], [10.22675, 46.86942], [10.10715, 46.84296], [9.98058, 46.91434], [9.88266, 46.93343], [9.87935, 47.01337], [9.60717, 47.06091], [9.55721, 47.04762], [9.54041, 47.06495], [9.47548, 47.05257], [9.47139, 47.06402], [9.51362, 47.08505], [9.52089, 47.10019], [9.51044, 47.13727], [9.48774, 47.17402], [9.4891, 47.19346], [9.50318, 47.22153], [9.52406, 47.24959], [9.53116, 47.27029], [9.54773, 47.2809], [9.55857, 47.29919], [9.58513, 47.31334], [9.59978, 47.34671], [9.62476, 47.36639], [9.65427, 47.36824], [9.66243, 47.37136], [9.6711, 47.37824], [9.67445, 47.38429], [9.67334, 47.39191], [9.6629, 47.39591], [9.65136, 47.40504], [9.65043, 47.41937], [9.6446, 47.43233], [9.64483, 47.43842], [9.65863, 47.44847], [9.65728, 47.45383], [9.6423, 47.45599], [9.62475, 47.45685], [9.62158, 47.45858], [9.60841, 47.47178], [9.60484, 47.46358], [9.60205, 47.46165], [9.59482, 47.46305], [9.58208, 47.48344], [9.56312, 47.49495], [9.55125, 47.53629], [9.25619, 47.65939], [9.18203, 47.65598], [9.17593, 47.65399], [9.1755, 47.65584], [9.1705, 47.65513], [9.15181, 47.66904], [9.13845, 47.66389], [9.09891, 47.67801], [9.02093, 47.6868], [8.94093, 47.65596], [8.89946, 47.64769], [8.87625, 47.65441], [8.87383, 47.67045], [8.85065, 47.68209], [8.86989, 47.70504], [8.82002, 47.71458], [8.80663, 47.73821], [8.77309, 47.72059], [8.76965, 47.7075], [8.79966, 47.70222], [8.79511, 47.67462], [8.75856, 47.68969], [8.72809, 47.69282]], [[8.95861, 45.96485], [8.96668, 45.98436], [8.97741, 45.98317], [8.97604, 45.96151], [8.95861, 45.96485]], [[8.70847, 47.68904], [8.68985, 47.69552], [8.66837, 47.68437], [8.65769, 47.68928], [8.67508, 47.6979], [8.66416, 47.71367], [8.70237, 47.71453], [8.71773, 47.69088], [8.70847, 47.68904]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CI", + iso1A3: "CIV", + iso1N3: "384", + wikidata: "Q1008", + nameEn: "C\xF4te d'Ivoire", + groups: ["011", "202", "002", "UN"], + callingCodes: ["225"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-7.52774, 3.7105], [-3.34019, 4.17519], [-3.10675, 5.08515], [-3.11073, 5.12675], [-3.063, 5.13665], [-2.96554, 5.10397], [-2.95261, 5.12477], [-2.75502, 5.10657], [-2.73074, 5.1364], [-2.77625, 5.34621], [-2.72737, 5.34789], [-2.76614, 5.60963], [-2.85378, 5.65156], [-2.93132, 5.62137], [-2.96671, 5.6415], [-2.95323, 5.71865], [-3.01896, 5.71697], [-3.25999, 6.62521], [-3.21954, 6.74407], [-3.23327, 6.81744], [-2.95438, 7.23737], [-2.97822, 7.27165], [-2.92339, 7.60847], [-2.79467, 7.86002], [-2.78395, 7.94974], [-2.74819, 7.92613], [-2.67787, 8.02055], [-2.61232, 8.02645], [-2.62901, 8.11495], [-2.49037, 8.20872], [-2.58243, 8.7789], [-2.66357, 9.01771], [-2.77799, 9.04949], [-2.69814, 9.22717], [-2.68802, 9.49343], [-2.76494, 9.40778], [-2.93012, 9.57403], [-3.00765, 9.74019], [-3.16609, 9.85147], [-3.19306, 9.93781], [-3.27228, 9.84981], [-3.31779, 9.91125], [-3.69703, 9.94279], [-4.25999, 9.76012], [-4.31392, 9.60062], [-4.6426, 9.70696], [-4.96621, 9.89132], [-4.96453, 9.99923], [-5.12465, 10.29788], [-5.39602, 10.2929], [-5.51058, 10.43177], [-5.65135, 10.46767], [-5.78124, 10.43952], [-5.99478, 10.19694], [-6.18851, 10.24244], [-6.1731, 10.46983], [-6.24795, 10.74248], [-6.325, 10.68624], [-6.40646, 10.69922], [-6.42847, 10.5694], [-6.52974, 10.59104], [-6.63541, 10.66893], [-6.68164, 10.35074], [-6.93921, 10.35291], [-7.01186, 10.25111], [-6.97444, 10.21644], [-7.00966, 10.15794], [-7.0603, 10.14711], [-7.13331, 10.24877], [-7.3707, 10.24677], [-7.44555, 10.44602], [-7.52261, 10.4655], [-7.54462, 10.40921], [-7.63048, 10.46334], [-7.92107, 10.15577], [-7.97971, 10.17117], [-8.01225, 10.1021], [-8.11921, 10.04577], [-8.15652, 9.94288], [-8.09434, 9.86936], [-8.14657, 9.55062], [-8.03463, 9.39604], [-7.85056, 9.41812], [-7.90777, 9.20456], [-7.73862, 9.08422], [-7.92518, 8.99332], [-7.95503, 8.81146], [-7.69882, 8.66148], [-7.65653, 8.36873], [-7.92518, 8.50652], [-8.22991, 8.48438], [-8.2411, 8.24196], [-8.062, 8.16071], [-7.98675, 8.20134], [-7.99919, 8.11023], [-7.94695, 8.00925], [-8.06449, 8.04989], [-8.13414, 7.87991], [-8.09931, 7.78626], [-8.21374, 7.54466], [-8.4003, 7.6285], [-8.47114, 7.55676], [-8.41935, 7.51203], [-8.37458, 7.25794], [-8.29249, 7.1691], [-8.31736, 6.82837], [-8.59456, 6.50612], [-8.48652, 6.43797], [-8.45666, 6.49977], [-8.38453, 6.35887], [-8.3298, 6.36381], [-8.17557, 6.28222], [-8.00642, 6.31684], [-7.90692, 6.27728], [-7.83478, 6.20309], [-7.8497, 6.08932], [-7.79747, 6.07696], [-7.78254, 5.99037], [-7.70294, 5.90625], [-7.67309, 5.94337], [-7.48155, 5.80974], [-7.46165, 5.84934], [-7.43677, 5.84687], [-7.43926, 5.74787], [-7.37209, 5.61173], [-7.43428, 5.42355], [-7.36463, 5.32944], [-7.46165, 5.26256], [-7.48901, 5.14118], [-7.55369, 5.08667], [-7.53876, 4.94294], [-7.59349, 4.8909], [-7.53259, 4.35145], [-7.52774, 3.7105]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CK", + iso1A3: "COK", + iso1N3: "184", + wikidata: "Q26988", + nameEn: "Cook Islands", + country: "NZ", + groups: ["061", "009", "UN"], + driveSide: "left", + callingCodes: ["682"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-168.15106, -10.26955], [-156.45576, -31.75456], [-156.48634, -15.52824], [-156.50903, -7.4975], [-168.15106, -10.26955]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CL", + iso1A3: "CHL", + iso1N3: "152", + wikidata: "Q298", + nameEn: "Chile", + groups: ["005", "419", "019", "UN"], + callingCodes: ["56"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-68.60702, -52.65781], [-68.41683, -52.33516], [-69.97824, -52.00845], [-71.99889, -51.98018], [-72.33873, -51.59954], [-72.31343, -50.58411], [-73.15765, -50.78337], [-73.55259, -49.92488], [-73.45156, -49.79461], [-73.09655, -49.14342], [-72.56894, -48.81116], [-72.54042, -48.52392], [-72.27662, -48.28727], [-72.50478, -47.80586], [-71.94152, -47.13595], [-71.68577, -46.55385], [-71.75614, -45.61611], [-71.35687, -45.22075], [-72.06985, -44.81756], [-71.26418, -44.75684], [-71.16436, -44.46244], [-71.81318, -44.38097], [-71.64206, -43.64774], [-72.14828, -42.85321], [-72.15541, -42.15941], [-71.74901, -42.11711], [-71.92726, -40.72714], [-71.37826, -38.91474], [-70.89532, -38.6923], [-71.24279, -37.20264], [-70.95047, -36.4321], [-70.38008, -36.02375], [-70.49416, -35.24145], [-69.87386, -34.13344], [-69.88099, -33.34489], [-70.55832, -31.51559], [-70.14479, -30.36595], [-69.8596, -30.26131], [-69.99507, -29.28351], [-69.80969, -29.07185], [-69.66709, -28.44055], [-69.22504, -27.95042], [-68.77586, -27.16029], [-68.43363, -27.08414], [-68.27677, -26.90626], [-68.59048, -26.49861], [-68.56909, -26.28146], [-68.38372, -26.15353], [-68.57622, -25.32505], [-68.38372, -25.08636], [-68.56909, -24.69831], [-68.24825, -24.42596], [-67.33563, -24.04237], [-66.99632, -22.99839], [-67.18382, -22.81525], [-67.54284, -22.89771], [-67.85114, -22.87076], [-68.18816, -21.28614], [-68.40403, -20.94562], [-68.53957, -20.91542], [-68.55383, -20.7355], [-68.44023, -20.62701], [-68.7276, -20.46178], [-68.74273, -20.08817], [-68.57132, -20.03134], [-68.54611, -19.84651], [-68.66761, -19.72118], [-68.41218, -19.40499], [-68.61989, -19.27584], [-68.80602, -19.08355], [-68.87082, -19.06003], [-68.94987, -18.93302], [-69.07432, -18.28259], [-69.14807, -18.16893], [-69.07496, -18.03715], [-69.28671, -17.94844], [-69.34126, -17.72753], [-69.46623, -17.60518], [-69.46897, -17.4988], [-69.66483, -17.65083], [-69.79087, -17.65563], [-69.82868, -17.72048], [-69.75305, -17.94605], [-69.81607, -18.12582], [-69.96732, -18.25992], [-70.16394, -18.31737], [-70.31267, -18.31258], [-70.378, -18.3495], [-70.59118, -18.35072], [-113.52687, -26.52828], [-68.11646, -58.14883], [-66.07313, -55.19618], [-67.11046, -54.94199], [-67.46182, -54.92205], [-68.01394, -54.8753], [-68.60733, -54.9125], [-68.60702, -52.65781]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CM", + iso1A3: "CMR", + iso1N3: "120", + wikidata: "Q1009", + nameEn: "Cameroon", + groups: ["017", "202", "002", "UN"], + callingCodes: ["237"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[14.83314, 12.62963], [14.55058, 12.78256], [14.56101, 12.91036], [14.46881, 13.08259], [14.08251, 13.0797], [14.20204, 12.53405], [14.17523, 12.41916], [14.22215, 12.36533], [14.4843, 12.35223], [14.6474, 12.17466], [14.61612, 11.7798], [14.55207, 11.72001], [14.64591, 11.66166], [14.6124, 11.51283], [14.17821, 11.23831], [13.97489, 11.30258], [13.78945, 11.00154], [13.7403, 11.00593], [13.70753, 10.94451], [13.73434, 10.9255], [13.54964, 10.61236], [13.5705, 10.53183], [13.43644, 10.13326], [13.34111, 10.12299], [13.25025, 10.03647], [13.25323, 10.00127], [13.286, 9.9822], [13.27409, 9.93232], [13.24132, 9.91031], [13.25025, 9.86042], [13.29941, 9.8296], [13.25472, 9.76795], [13.22642, 9.57266], [13.02385, 9.49334], [12.85628, 9.36698], [12.91958, 9.33905], [12.90022, 9.11411], [12.81085, 8.91992], [12.79, 8.75361], [12.71701, 8.7595], [12.68722, 8.65938], [12.44146, 8.6152], [12.4489, 8.52536], [12.26123, 8.43696], [12.24782, 8.17904], [12.19271, 8.10826], [12.20909, 7.97553], [11.99908, 7.67302], [12.01844, 7.52981], [11.93205, 7.47812], [11.84864, 7.26098], [11.87396, 7.09398], [11.63117, 6.9905], [11.55818, 6.86186], [11.57755, 6.74059], [11.51499, 6.60892], [11.42264, 6.5882], [11.42041, 6.53789], [11.09495, 6.51717], [11.09644, 6.68437], [10.94302, 6.69325], [10.8179, 6.83377], [10.83727, 6.9358], [10.60789, 7.06885], [10.59746, 7.14719], [10.57214, 7.16345], [10.53639, 6.93432], [10.21466, 6.88996], [10.15135, 7.03781], [9.86314, 6.77756], [9.77824, 6.79088], [9.70674, 6.51717], [9.51757, 6.43874], [8.84209, 5.82562], [8.88156, 5.78857], [8.83687, 5.68483], [8.92029, 5.58403], [8.78027, 5.1243], [8.60302, 4.87353], [8.34397, 4.30689], [9.22018, 3.72052], [9.81162, 2.33797], [9.82123, 2.35097], [9.83754, 2.32428], [9.83238, 2.29079], [9.84716, 2.24676], [9.89012, 2.20457], [9.90749, 2.20049], [9.991, 2.16561], [11.3561, 2.17217], [11.37116, 2.29975], [13.28534, 2.25716], [13.29457, 2.16106], [14.61145, 2.17866], [15.00996, 1.98887], [15.22634, 2.03243], [15.34776, 1.91264], [15.48942, 1.98265], [16.02959, 1.76483], [16.02647, 1.65591], [16.14634, 1.70259], [16.05294, 1.9811], [16.08563, 2.19733], [16.15568, 2.18955], [16.19357, 2.21537], [16.08252, 2.45708], [16.05449, 3.02306], [15.77725, 3.26835], [15.73522, 3.24348], [15.07686, 4.01805], [15.17482, 4.05131], [15.10644, 4.1362], [15.08609, 4.30282], [15.00825, 4.41458], [14.73383, 4.6135], [14.65489, 5.21343], [14.57083, 5.23979], [14.52724, 5.28319], [14.62531, 5.51411], [14.58951, 5.59777], [14.62375, 5.70466], [14.60974, 5.91838], [14.49455, 5.91683], [14.42917, 6.00508], [14.43073, 6.08867], [14.56149, 6.18928], [14.74206, 6.26356], [14.80122, 6.34866], [14.79966, 6.39043], [14.96311, 6.75693], [15.04717, 6.77085], [15.23397, 7.25135], [15.49743, 7.52179], [15.56964, 7.58936], [15.59272, 7.7696], [15.50743, 7.79302], [15.20426, 8.50892], [15.09484, 8.65982], [14.83566, 8.80557], [14.35707, 9.19611], [14.37094, 9.2954], [13.97544, 9.6365], [14.01793, 9.73169], [14.1317, 9.82413], [14.20411, 10.00055], [14.4673, 10.00264], [14.80082, 9.93818], [14.95722, 9.97926], [15.05999, 9.94845], [15.14043, 9.99246], [15.24618, 9.99246], [15.41408, 9.92876], [15.68761, 9.99344], [15.50535, 10.1098], [15.30874, 10.31063], [15.23724, 10.47764], [15.14936, 10.53915], [15.15532, 10.62846], [15.06737, 10.80921], [15.09127, 10.87431], [15.04957, 11.02347], [15.10021, 11.04101], [15.0585, 11.40481], [15.13149, 11.5537], [15.06595, 11.71126], [15.11579, 11.79313], [15.04808, 11.8731], [15.05786, 12.0608], [15.0349, 12.10698], [15.00146, 12.1223], [14.96952, 12.0925], [14.89019, 12.16593], [14.90827, 12.3269], [14.83314, 12.62963]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CN", + iso1A3: "CHN", + iso1N3: "156", + wikidata: "Q148", + nameEn: "People's Republic of China" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "CO", + iso1A3: "COL", + iso1N3: "170", + wikidata: "Q739", + nameEn: "Colombia", + groups: ["005", "419", "019", "UN"], + callingCodes: ["57"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-71.19849, 12.65801], [-81.58685, 18.0025], [-82.06974, 14.49418], [-82.56142, 11.91792], [-78.79327, 9.93766], [-77.58292, 9.22278], [-77.32389, 8.81247], [-77.45064, 8.49991], [-77.17257, 7.97422], [-77.57185, 7.51147], [-77.72514, 7.72348], [-77.72157, 7.47612], [-77.81426, 7.48319], [-77.89178, 7.22681], [-78.06168, 7.07793], [-82.12561, 4.00341], [-78.87137, 1.47457], [-78.42749, 1.15389], [-77.85677, 0.80197], [-77.7148, 0.85003], [-77.68613, 0.83029], [-77.66416, 0.81604], [-77.67815, 0.73863], [-77.49984, 0.64476], [-77.52001, 0.40782], [-76.89177, 0.24736], [-76.4094, 0.24015], [-76.41215, 0.38228], [-76.23441, 0.42294], [-75.82927, 0.09578], [-75.25764, -0.11943], [-75.18513, -0.0308], [-74.42701, -0.50218], [-74.26675, -0.97229], [-73.65312, -1.26222], [-72.92587, -2.44514], [-71.75223, -2.15058], [-70.94377, -2.23142], [-70.04609, -2.73906], [-70.71396, -3.7921], [-70.52393, -3.87553], [-70.3374, -3.79505], [-69.94708, -4.2431], [-69.43395, -1.42219], [-69.4215, -1.01853], [-69.59796, -0.75136], [-69.603, -0.51947], [-70.03658, -0.19681], [-70.04162, 0.55437], [-69.47696, 0.71065], [-69.20976, 0.57958], [-69.14422, 0.84172], [-69.26017, 1.06856], [-69.82987, 1.07864], [-69.83491, 1.69353], [-69.53746, 1.76408], [-69.38621, 1.70865], [-68.18128, 1.72881], [-68.26699, 1.83463], [-68.18632, 2.00091], [-67.9292, 1.82455], [-67.40488, 2.22258], [-67.299, 1.87494], [-67.15784, 1.80439], [-67.08222, 1.17441], [-66.85795, 1.22998], [-67.21967, 2.35778], [-67.65696, 2.81691], [-67.85862, 2.79173], [-67.85862, 2.86727], [-67.30945, 3.38393], [-67.50067, 3.75812], [-67.62671, 3.74303], [-67.85358, 4.53249], [-67.83341, 5.31104], [-67.59141, 5.5369], [-67.63914, 5.64963], [-67.58558, 5.84537], [-67.43513, 5.98835], [-67.4625, 6.20625], [-67.60654, 6.2891], [-69.41843, 6.1072], [-70.10716, 6.96516], [-70.7596, 7.09799], [-71.03941, 6.98163], [-71.37234, 7.01588], [-71.42212, 7.03854], [-71.44118, 7.02116], [-71.82441, 7.04314], [-72.04895, 7.03837], [-72.19437, 7.37034], [-72.43132, 7.40034], [-72.47415, 7.48928], [-72.45321, 7.57232], [-72.47827, 7.65604], [-72.46763, 7.79518], [-72.44454, 7.86031], [-72.46183, 7.90682], [-72.45806, 7.91141], [-72.47042, 7.92306], [-72.48183, 7.92909], [-72.48801, 7.94329], [-72.47213, 7.96106], [-72.39137, 8.03534], [-72.35163, 8.01163], [-72.36987, 8.19976], [-72.4042, 8.36513], [-72.65474, 8.61428], [-72.77415, 9.10165], [-72.94052, 9.10663], [-73.02119, 9.27584], [-73.36905, 9.16636], [-72.98085, 9.85253], [-72.88002, 10.44309], [-72.4767, 11.1117], [-72.24983, 11.14138], [-71.9675, 11.65536], [-71.3275, 11.85], [-70.92579, 11.96275], [-71.19849, 12.65801]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CP", + iso1A3: "CPT", + wikidata: "Q161258", + nameEn: "Clipperton Island", + country: "FR", + groups: ["013", "003", "019", "UN"], + isoStatus: "excRes" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-110.36279, 9.79626], [-108.755, 9.84085], [-109.04145, 11.13245], [-110.36279, 9.79626]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CR", + iso1A3: "CRI", + iso1N3: "188", + wikidata: "Q800", + nameEn: "Costa Rica", + groups: ["013", "003", "419", "019", "UN"], + callingCodes: ["506"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-83.68276, 11.01562], [-83.66597, 10.79916], [-83.90838, 10.71161], [-84.68197, 11.07568], [-84.92439, 10.9497], [-85.60529, 11.22607], [-85.71223, 11.06868], [-86.14524, 11.09059], [-87.41779, 5.02401], [-82.94503, 7.93865], [-82.89978, 8.04083], [-82.89137, 8.05755], [-82.88641, 8.10219], [-82.9388, 8.26634], [-83.05209, 8.33394], [-82.93056, 8.43465], [-82.8679, 8.44042], [-82.8382, 8.48117], [-82.83322, 8.52464], [-82.83975, 8.54755], [-82.82739, 8.60153], [-82.8794, 8.6981], [-82.92068, 8.74832], [-82.91377, 8.774], [-82.88253, 8.83331], [-82.72126, 8.97125], [-82.93516, 9.07687], [-82.93516, 9.46741], [-82.84871, 9.4973], [-82.87919, 9.62645], [-82.77206, 9.59573], [-82.66667, 9.49746], [-82.61345, 9.49881], [-82.56507, 9.57279], [-82.51044, 9.65379], [-83.54024, 10.96805], [-83.68276, 11.01562]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CU", + iso1A3: "CUB", + iso1N3: "192", + wikidata: "Q241", + nameEn: "Cuba", + groups: ["029", "003", "419", "019", "UN"], + callingCodes: ["53"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-73.62304, 20.6935], [-82.02215, 24.23074], [-85.77883, 21.92705], [-74.81171, 18.82201], [-73.62304, 20.6935]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CV", + iso1A3: "CPV", + iso1N3: "132", + wikidata: "Q1011", + nameEn: "Cape Verde", + groups: ["Q105472", "011", "202", "002", "UN"], + callingCodes: ["238"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-28.81604, 14.57305], [-20.39702, 14.12816], [-23.37101, 19.134], [-28.81604, 14.57305]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CW", + iso1A3: "CUW", + iso1N3: "531", + wikidata: "Q25279", + nameEn: "Cura\xE7ao", + aliases: ["NL-CW"], + country: "NL", + groups: ["Q1451600", "029", "003", "419", "019", "UN"], + callingCodes: ["599"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-68.90012, 12.62309], [-69.59009, 12.46019], [-68.99639, 11.79035], [-68.33524, 11.78151], [-68.90012, 12.62309]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CX", + iso1A3: "CXR", + iso1N3: "162", + wikidata: "Q31063", + nameEn: "Christmas Island", + country: "AU", + groups: ["053", "009", "UN"], + driveSide: "left", + callingCodes: ["61"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[105.66835, -9.31927], [104.67494, -11.2566], [106.66176, -11.14349], [105.66835, -9.31927]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CY", + iso1A3: "CYP", + iso1N3: "196", + wikidata: "Q229", + nameEn: "Republic of Cyprus", + groups: ["Q644636", "EU", "145", "142", "UN"], + driveSide: "left", + callingCodes: ["357"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "CZ", + iso1A3: "CZE", + iso1N3: "203", + wikidata: "Q213", + nameEn: "Czechia", + groups: ["EU", "151", "150", "UN"], + callingCodes: ["420"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[14.82803, 50.86966], [14.79139, 50.81438], [14.70661, 50.84096], [14.61993, 50.86049], [14.63434, 50.8883], [14.65259, 50.90513], [14.64802, 50.93241], [14.58024, 50.91443], [14.56374, 50.922], [14.59702, 50.96148], [14.59908, 50.98685], [14.58215, 50.99306], [14.56432, 51.01008], [14.53438, 51.00374], [14.53321, 51.01679], [14.49873, 51.02242], [14.50809, 51.0427], [14.49991, 51.04692], [14.49154, 51.04382], [14.49202, 51.02286], [14.45827, 51.03712], [14.41335, 51.02086], [14.30098, 51.05515], [14.25665, 50.98935], [14.28776, 50.97718], [14.32353, 50.98556], [14.32793, 50.97379], [14.30251, 50.96606], [14.31422, 50.95243], [14.39848, 50.93866], [14.38691, 50.89907], [14.30098, 50.88448], [14.27123, 50.89386], [14.24314, 50.88761], [14.22331, 50.86049], [14.02982, 50.80662], [13.98864, 50.8177], [13.89113, 50.78533], [13.89444, 50.74142], [13.82942, 50.7251], [13.76316, 50.73487], [13.70204, 50.71771], [13.65977, 50.73096], [13.52474, 50.70394], [13.53748, 50.67654], [13.5226, 50.64721], [13.49742, 50.63133], [13.46413, 50.60102], [13.42189, 50.61243], [13.37485, 50.64931], [13.37805, 50.627], [13.32264, 50.60317], [13.32594, 50.58009], [13.29454, 50.57904], [13.25158, 50.59268], [13.19043, 50.50237], [13.13424, 50.51709], [13.08301, 50.50132], [13.0312, 50.50944], [13.02038, 50.4734], [13.02147, 50.44763], [12.98433, 50.42016], [12.94058, 50.40944], [12.82465, 50.45738], [12.73476, 50.43237], [12.73044, 50.42268], [12.70731, 50.39948], [12.67261, 50.41949], [12.51356, 50.39694], [12.48747, 50.37278], [12.49214, 50.35228], [12.48256, 50.34784], [12.46643, 50.35527], [12.43722, 50.33774], [12.43371, 50.32506], [12.39924, 50.32302], [12.40158, 50.29521], [12.36594, 50.28289], [12.35425, 50.23993], [12.33263, 50.24367], [12.32445, 50.20442], [12.33847, 50.19432], [12.32596, 50.17146], [12.29232, 50.17524], [12.28063, 50.19544], [12.28755, 50.22429], [12.23943, 50.24594], [12.24791, 50.25525], [12.26953, 50.25189], [12.25119, 50.27079], [12.20823, 50.2729], [12.18013, 50.32146], [12.10907, 50.32041], [12.13716, 50.27396], [12.09287, 50.25032], [12.19335, 50.19997], [12.21484, 50.16399], [12.1917, 50.13434], [12.2073, 50.10315], [12.23709, 50.10213], [12.27433, 50.0771], [12.26111, 50.06331], [12.30798, 50.05719], [12.49908, 49.97305], [12.47264, 49.94222], [12.55197, 49.92094], [12.48256, 49.83575], [12.46603, 49.78882], [12.40489, 49.76321], [12.4462, 49.70233], [12.52553, 49.68415], [12.53544, 49.61888], [12.56188, 49.6146], [12.60155, 49.52887], [12.64782, 49.52565], [12.64121, 49.47628], [12.669, 49.42935], [12.71227, 49.42363], [12.75854, 49.3989], [12.78168, 49.34618], [12.88414, 49.33541], [12.88249, 49.35479], [12.94859, 49.34079], [13.03618, 49.30417], [13.02957, 49.27399], [13.05883, 49.26259], [13.17665, 49.16713], [13.17019, 49.14339], [13.20405, 49.12303], [13.23689, 49.11412], [13.28242, 49.1228], [13.39479, 49.04812], [13.40802, 48.98851], [13.50221, 48.93752], [13.50552, 48.97441], [13.58319, 48.96899], [13.61624, 48.9462], [13.67739, 48.87886], [13.73854, 48.88538], [13.76994, 48.83537], [13.78977, 48.83319], [13.8096, 48.77877], [13.84023, 48.76988], [14.06151, 48.66873], [14.01482, 48.63788], [14.09104, 48.5943], [14.20691, 48.5898], [14.33909, 48.55852], [14.43076, 48.58855], [14.4587, 48.64695], [14.56139, 48.60429], [14.60808, 48.62881], [14.66762, 48.58215], [14.71794, 48.59794], [14.72756, 48.69502], [14.80584, 48.73489], [14.80821, 48.77711], [14.81545, 48.7874], [14.94773, 48.76268], [14.95641, 48.75915], [14.9758, 48.76857], [14.98112, 48.77524], [14.9782, 48.7766], [14.98032, 48.77959], [14.95072, 48.79101], [14.98917, 48.90082], [14.97612, 48.96983], [14.99878, 49.01444], [15.15534, 48.99056], [15.16358, 48.94278], [15.26177, 48.95766], [15.28305, 48.98831], [15.34823, 48.98444], [15.48027, 48.94481], [15.51357, 48.91549], [15.61622, 48.89541], [15.6921, 48.85973], [15.75341, 48.8516], [15.78087, 48.87644], [15.84404, 48.86921], [16.06034, 48.75436], [16.37345, 48.729], [16.40915, 48.74576], [16.46134, 48.80865], [16.67008, 48.77699], [16.68518, 48.7281], [16.71883, 48.73806], [16.79779, 48.70998], [16.90354, 48.71541], [16.93955, 48.60371], [17.00215, 48.70887], [17.11202, 48.82925], [17.19355, 48.87602], [17.29054, 48.85546], [17.3853, 48.80936], [17.45671, 48.85004], [17.5295, 48.81117], [17.7094, 48.86721], [17.73126, 48.87885], [17.77944, 48.92318], [17.87831, 48.92679], [17.91814, 49.01784], [18.06885, 49.03157], [18.1104, 49.08624], [18.15022, 49.24518], [18.18456, 49.28909], [18.36446, 49.3267], [18.4139, 49.36517], [18.4084, 49.40003], [18.44686, 49.39467], [18.54848, 49.47059], [18.53063, 49.49022], [18.57183, 49.51162], [18.6144, 49.49824], [18.67757, 49.50895], [18.74761, 49.492], [18.84521, 49.51672], [18.84786, 49.5446], [18.80479, 49.6815], [18.72838, 49.68163], [18.69817, 49.70473], [18.62676, 49.71983], [18.62943, 49.74603], [18.62645, 49.75002], [18.61368, 49.75426], [18.61278, 49.7618], [18.57183, 49.83334], [18.60341, 49.86256], [18.57045, 49.87849], [18.57697, 49.91565], [18.54299, 49.92537], [18.54495, 49.9079], [18.53423, 49.89906], [18.41604, 49.93498], [18.33562, 49.94747], [18.33278, 49.92415], [18.31914, 49.91565], [18.27794, 49.93863], [18.27107, 49.96779], [18.21752, 49.97309], [18.20241, 49.99958], [18.10628, 50.00223], [18.07898, 50.04535], [18.03212, 50.06574], [18.00396, 50.04954], [18.04585, 50.03311], [18.04585, 50.01194], [18.00191, 50.01723], [17.86886, 49.97452], [17.77669, 50.02253], [17.7506, 50.07896], [17.6888, 50.12037], [17.66683, 50.10275], [17.59404, 50.16437], [17.70528, 50.18812], [17.76296, 50.23382], [17.72176, 50.25665], [17.74648, 50.29966], [17.69292, 50.32859], [17.67764, 50.28977], [17.58889, 50.27837], [17.3702, 50.28123], [17.34548, 50.2628], [17.34273, 50.32947], [17.27681, 50.32246], [17.19991, 50.3654], [17.19579, 50.38817], [17.14498, 50.38117], [17.1224, 50.39494], [16.89229, 50.45117], [16.85933, 50.41093], [16.90877, 50.38642], [16.94448, 50.31281], [16.99803, 50.30316], [17.02138, 50.27772], [16.99803, 50.25753], [17.02825, 50.23118], [17.00353, 50.21449], [16.98018, 50.24172], [16.8456, 50.20834], [16.7014, 50.09659], [16.63137, 50.1142], [16.55446, 50.16613], [16.56407, 50.21009], [16.42674, 50.32509], [16.39379, 50.3207], [16.3622, 50.34875], [16.36495, 50.37679], [16.30289, 50.38292], [16.28118, 50.36891], [16.22821, 50.41054], [16.21585, 50.40627], [16.19526, 50.43291], [16.31413, 50.50274], [16.34572, 50.49575], [16.44597, 50.58041], [16.33611, 50.66579], [16.23174, 50.67101], [16.20839, 50.63096], [16.10265, 50.66405], [16.02437, 50.60046], [15.98317, 50.61528], [16.0175, 50.63009], [15.97219, 50.69799], [15.87331, 50.67188], [15.81683, 50.75666], [15.73186, 50.73885], [15.43798, 50.80833], [15.3803, 50.77187], [15.36656, 50.83956], [15.2773, 50.8907], [15.27043, 50.97724], [15.2361, 50.99886], [15.1743, 50.9833], [15.16744, 51.01959], [15.11937, 50.99021], [15.10152, 51.01095], [15.06218, 51.02269], [15.03895, 51.0123], [15.02433, 51.0242], [14.96419, 50.99108], [15.01088, 50.97984], [14.99852, 50.86817], [14.82803, 50.86966]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "DE", + iso1A3: "DEU", + iso1N3: "276", + wikidata: "Q183", + nameEn: "Germany", + groups: ["EU", "155", "150", "UN"], + callingCodes: ["49"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[8.70847, 47.68904], [8.71773, 47.69088], [8.70237, 47.71453], [8.66416, 47.71367], [8.67508, 47.6979], [8.65769, 47.68928], [8.66837, 47.68437], [8.68985, 47.69552], [8.70847, 47.68904]]], [[[8.72617, 47.69651], [8.72809, 47.69282], [8.75856, 47.68969], [8.79511, 47.67462], [8.79966, 47.70222], [8.76965, 47.7075], [8.77309, 47.72059], [8.80663, 47.73821], [8.82002, 47.71458], [8.86989, 47.70504], [8.85065, 47.68209], [8.87383, 47.67045], [8.87625, 47.65441], [8.89946, 47.64769], [8.94093, 47.65596], [9.02093, 47.6868], [9.09891, 47.67801], [9.13845, 47.66389], [9.15181, 47.66904], [9.1705, 47.65513], [9.1755, 47.65584], [9.17593, 47.65399], [9.18203, 47.65598], [9.25619, 47.65939], [9.55125, 47.53629], [9.72736, 47.53457], [9.76748, 47.5934], [9.80254, 47.59419], [9.82591, 47.58158], [9.8189, 47.54688], [9.87499, 47.52953], [9.87733, 47.54688], [9.92407, 47.53111], [9.96029, 47.53899], [10.00003, 47.48216], [10.03859, 47.48927], [10.07131, 47.45531], [10.09001, 47.46005], [10.1052, 47.4316], [10.06897, 47.40709], [10.09819, 47.35724], [10.11805, 47.37228], [10.16362, 47.36674], [10.17648, 47.38889], [10.2127, 47.38019], [10.22774, 47.38904], [10.23757, 47.37609], [10.19998, 47.32832], [10.2147, 47.31014], [10.17648, 47.29149], [10.17531, 47.27167], [10.23257, 47.27088], [10.33424, 47.30813], [10.39851, 47.37623], [10.4324, 47.38494], [10.4359, 47.41183], [10.47446, 47.43318], [10.46278, 47.47901], [10.44291, 47.48453], [10.4324, 47.50111], [10.44992, 47.5524], [10.43473, 47.58394], [10.47329, 47.58552], [10.48849, 47.54057], [10.56912, 47.53584], [10.60337, 47.56755], [10.63456, 47.5591], [10.68832, 47.55752], [10.6965, 47.54253], [10.7596, 47.53228], [10.77596, 47.51729], [10.88814, 47.53701], [10.91268, 47.51334], [10.86945, 47.5015], [10.87061, 47.4786], [10.90918, 47.48571], [10.93839, 47.48018], [10.92437, 47.46991], [10.98513, 47.42882], [10.97111, 47.41617], [10.97111, 47.39561], [11.11835, 47.39719], [11.12536, 47.41222], [11.20482, 47.43198], [11.25157, 47.43277], [11.22002, 47.3964], [11.27844, 47.39956], [11.29597, 47.42566], [11.33804, 47.44937], [11.4175, 47.44621], [11.38128, 47.47465], [11.4362, 47.51413], [11.52618, 47.50939], [11.58578, 47.52281], [11.58811, 47.55515], [11.60681, 47.57881], [11.63934, 47.59202], [11.84052, 47.58354], [11.85572, 47.60166], [12.0088, 47.62451], [12.02282, 47.61033], [12.05788, 47.61742], [12.13734, 47.60639], [12.17824, 47.61506], [12.18145, 47.61019], [12.17737, 47.60121], [12.18568, 47.6049], [12.20398, 47.60667], [12.20801, 47.61082], [12.19895, 47.64085], [12.18507, 47.65984], [12.18347, 47.66663], [12.16769, 47.68167], [12.16217, 47.70105], [12.18303, 47.70065], [12.22571, 47.71776], [12.2542, 47.7433], [12.26238, 47.73544], [12.24017, 47.69534], [12.26004, 47.67725], [12.27991, 47.68827], [12.336, 47.69534], [12.37222, 47.68433], [12.43883, 47.6977], [12.44117, 47.6741], [12.50076, 47.62293], [12.53816, 47.63553], [12.57438, 47.63238], [12.6071, 47.6741], [12.7357, 47.6787], [12.77777, 47.66689], [12.76492, 47.64485], [12.82101, 47.61493], [12.77427, 47.58025], [12.80699, 47.54477], [12.84672, 47.54556], [12.85256, 47.52741], [12.9624, 47.47452], [12.98344, 47.48716], [12.9998, 47.46267], [13.04537, 47.49426], [13.03252, 47.53373], [13.05355, 47.56291], [13.04537, 47.58183], [13.06641, 47.58577], [13.06407, 47.60075], [13.09562, 47.63304], [13.07692, 47.68814], [13.01382, 47.72116], [12.98578, 47.7078], [12.92969, 47.71094], [12.91333, 47.7178], [12.90274, 47.72513], [12.91711, 47.74026], [12.9353, 47.74788], [12.94371, 47.76281], [12.93202, 47.77302], [12.96311, 47.79957], [12.98543, 47.82896], [13.00588, 47.84374], [12.94163, 47.92927], [12.93886, 47.94046], [12.93642, 47.94436], [12.93419, 47.94063], [12.92668, 47.93879], [12.91985, 47.94069], [12.9211, 47.95135], [12.91683, 47.95647], [12.87476, 47.96195], [12.8549, 48.01122], [12.76141, 48.07373], [12.74973, 48.10885], [12.7617, 48.12796], [12.78595, 48.12445], [12.80676, 48.14979], [12.82673, 48.15245], [12.8362, 48.15876], [12.836, 48.1647], [12.84475, 48.16556], [12.87126, 48.20318], [12.95306, 48.20629], [13.02083, 48.25689], [13.0851, 48.27711], [13.126, 48.27867], [13.18093, 48.29577], [13.26039, 48.29422], [13.30897, 48.31575], [13.40709, 48.37292], [13.43929, 48.43386], [13.42527, 48.45711], [13.45727, 48.51092], [13.43695, 48.55776], [13.45214, 48.56472], [13.46967, 48.55157], [13.50663, 48.57506], [13.50131, 48.58091], [13.51291, 48.59023], [13.57535, 48.55912], [13.59705, 48.57013], [13.62508, 48.55501], [13.65186, 48.55092], [13.66113, 48.53558], [13.72802, 48.51208], [13.74816, 48.53058], [13.7513, 48.5624], [13.76921, 48.55324], [13.80519, 48.58026], [13.80038, 48.59487], [13.82609, 48.62345], [13.81901, 48.6761], [13.81283, 48.68426], [13.81791, 48.69832], [13.79337, 48.71375], [13.81863, 48.73257], [13.82266, 48.75544], [13.84023, 48.76988], [13.8096, 48.77877], [13.78977, 48.83319], [13.76994, 48.83537], [13.73854, 48.88538], [13.67739, 48.87886], [13.61624, 48.9462], [13.58319, 48.96899], [13.50552, 48.97441], [13.50221, 48.93752], [13.40802, 48.98851], [13.39479, 49.04812], [13.28242, 49.1228], [13.23689, 49.11412], [13.20405, 49.12303], [13.17019, 49.14339], [13.17665, 49.16713], [13.05883, 49.26259], [13.02957, 49.27399], [13.03618, 49.30417], [12.94859, 49.34079], [12.88249, 49.35479], [12.88414, 49.33541], [12.78168, 49.34618], [12.75854, 49.3989], [12.71227, 49.42363], [12.669, 49.42935], [12.64121, 49.47628], [12.64782, 49.52565], [12.60155, 49.52887], [12.56188, 49.6146], [12.53544, 49.61888], [12.52553, 49.68415], [12.4462, 49.70233], [12.40489, 49.76321], [12.46603, 49.78882], [12.48256, 49.83575], [12.55197, 49.92094], [12.47264, 49.94222], [12.49908, 49.97305], [12.30798, 50.05719], [12.26111, 50.06331], [12.27433, 50.0771], [12.23709, 50.10213], [12.2073, 50.10315], [12.1917, 50.13434], [12.21484, 50.16399], [12.19335, 50.19997], [12.09287, 50.25032], [12.13716, 50.27396], [12.10907, 50.32041], [12.18013, 50.32146], [12.20823, 50.2729], [12.25119, 50.27079], [12.26953, 50.25189], [12.24791, 50.25525], [12.23943, 50.24594], [12.28755, 50.22429], [12.28063, 50.19544], [12.29232, 50.17524], [12.32596, 50.17146], [12.33847, 50.19432], [12.32445, 50.20442], [12.33263, 50.24367], [12.35425, 50.23993], [12.36594, 50.28289], [12.40158, 50.29521], [12.39924, 50.32302], [12.43371, 50.32506], [12.43722, 50.33774], [12.46643, 50.35527], [12.48256, 50.34784], [12.49214, 50.35228], [12.48747, 50.37278], [12.51356, 50.39694], [12.67261, 50.41949], [12.70731, 50.39948], [12.73044, 50.42268], [12.73476, 50.43237], [12.82465, 50.45738], [12.94058, 50.40944], [12.98433, 50.42016], [13.02147, 50.44763], [13.02038, 50.4734], [13.0312, 50.50944], [13.08301, 50.50132], [13.13424, 50.51709], [13.19043, 50.50237], [13.25158, 50.59268], [13.29454, 50.57904], [13.32594, 50.58009], [13.32264, 50.60317], [13.37805, 50.627], [13.37485, 50.64931], [13.42189, 50.61243], [13.46413, 50.60102], [13.49742, 50.63133], [13.5226, 50.64721], [13.53748, 50.67654], [13.52474, 50.70394], [13.65977, 50.73096], [13.70204, 50.71771], [13.76316, 50.73487], [13.82942, 50.7251], [13.89444, 50.74142], [13.89113, 50.78533], [13.98864, 50.8177], [14.02982, 50.80662], [14.22331, 50.86049], [14.24314, 50.88761], [14.27123, 50.89386], [14.30098, 50.88448], [14.38691, 50.89907], [14.39848, 50.93866], [14.31422, 50.95243], [14.30251, 50.96606], [14.32793, 50.97379], [14.32353, 50.98556], [14.28776, 50.97718], [14.25665, 50.98935], [14.30098, 51.05515], [14.41335, 51.02086], [14.45827, 51.03712], [14.49202, 51.02286], [14.49154, 51.04382], [14.49991, 51.04692], [14.50809, 51.0427], [14.49873, 51.02242], [14.53321, 51.01679], [14.53438, 51.00374], [14.56432, 51.01008], [14.58215, 50.99306], [14.59908, 50.98685], [14.59702, 50.96148], [14.56374, 50.922], [14.58024, 50.91443], [14.64802, 50.93241], [14.65259, 50.90513], [14.63434, 50.8883], [14.61993, 50.86049], [14.70661, 50.84096], [14.79139, 50.81438], [14.82803, 50.86966], [14.81664, 50.88148], [14.89681, 50.9422], [14.89252, 50.94999], [14.92942, 50.99744], [14.95529, 51.04552], [14.97938, 51.07742], [14.98229, 51.11354], [14.99689, 51.12205], [14.99079, 51.14284], [14.99646, 51.14365], [15.00083, 51.14974], [14.99414, 51.15813], [14.99311, 51.16249], [15.0047, 51.16874], [15.01242, 51.21285], [15.04288, 51.28387], [14.98008, 51.33449], [14.96899, 51.38367], [14.9652, 51.44793], [14.94749, 51.47155], [14.73219, 51.52922], [14.72652, 51.53902], [14.73047, 51.54606], [14.71125, 51.56209], [14.7727, 51.61263], [14.75759, 51.62318], [14.75392, 51.67445], [14.69065, 51.70842], [14.66386, 51.73282], [14.64625, 51.79472], [14.60493, 51.80473], [14.59089, 51.83302], [14.6588, 51.88359], [14.6933, 51.9044], [14.70601, 51.92944], [14.7177, 51.94048], [14.72163, 51.95188], [14.71836, 51.95606], [14.7139, 51.95643], [14.70488, 51.97679], [14.71339, 52.00337], [14.76026, 52.06624], [14.72971, 52.09167], [14.6917, 52.10283], [14.67683, 52.13936], [14.70616, 52.16927], [14.68344, 52.19612], [14.71319, 52.22144], [14.70139, 52.25038], [14.58149, 52.28007], [14.56378, 52.33838], [14.55228, 52.35264], [14.54423, 52.42568], [14.63056, 52.48993], [14.60081, 52.53116], [14.6289, 52.57136], [14.61073, 52.59847], [14.22071, 52.81175], [14.13806, 52.82392], [14.12256, 52.84311], [14.15873, 52.87715], [14.14056, 52.95786], [14.25954, 53.00264], [14.35044, 53.05829], [14.38679, 53.13669], [14.36696, 53.16444], [14.37853, 53.20405], [14.40662, 53.21098], [14.45125, 53.26241], [14.44133, 53.27427], [14.4215, 53.27724], [14.35209, 53.49506], [14.3273, 53.50587], [14.30416, 53.55499], [14.31904, 53.61581], [14.2853, 53.63392], [14.28477, 53.65955], [14.27133, 53.66613], [14.2836, 53.67721], [14.26782, 53.69866], [14.27249, 53.74464], [14.21323, 53.8664], [14.20823, 53.90776], [14.18544, 53.91258], [14.20647, 53.91671], [14.22634, 53.9291], [14.20811, 54.12784], [13.93395, 54.84044], [12.85844, 54.82438], [11.90309, 54.38543], [11.00303, 54.63689], [10.31111, 54.65968], [10.16755, 54.73883], [9.89314, 54.84171], [9.73563, 54.8247], [9.61187, 54.85548], [9.62734, 54.88057], [9.58937, 54.88785], [9.4659, 54.83131], [9.43155, 54.82586], [9.41213, 54.84254], [9.38532, 54.83968], [9.36496, 54.81749], [9.33849, 54.80233], [9.32771, 54.80602], [9.2474, 54.8112], [9.23445, 54.83432], [9.24631, 54.84726], [9.20571, 54.85841], [9.14275, 54.87421], [9.04629, 54.87249], [8.92795, 54.90452], [8.81178, 54.90518], [8.76387, 54.8948], [8.63979, 54.91069], [8.55769, 54.91837], [8.45719, 55.06747], [8.02459, 55.09613], [5.45168, 54.20039], [6.91025, 53.44221], [7.00198, 53.32672], [7.19052, 53.31866], [7.21679, 53.20058], [7.22681, 53.18165], [7.17898, 53.13817], [7.21694, 53.00742], [7.07253, 52.81083], [7.04557, 52.63318], [6.77307, 52.65375], [6.71641, 52.62905], [6.69507, 52.488], [6.94293, 52.43597], [6.99041, 52.47235], [7.03417, 52.40237], [7.07044, 52.37805], [7.02703, 52.27941], [7.06365, 52.23789], [7.03729, 52.22695], [6.9897, 52.2271], [6.97189, 52.20329], [6.83984, 52.11728], [6.76117, 52.11895], [6.68128, 52.05052], [6.83035, 51.9905], [6.82357, 51.96711], [6.72319, 51.89518], [6.68386, 51.91861], [6.58556, 51.89386], [6.50231, 51.86313], [6.47179, 51.85395], [6.38815, 51.87257], [6.40704, 51.82771], [6.30593, 51.84998], [6.29872, 51.86801], [6.21443, 51.86801], [6.15349, 51.90439], [6.11551, 51.89769], [6.16902, 51.84094], [6.10337, 51.84829], [6.06705, 51.86136], [5.99848, 51.83195], [5.94568, 51.82786], [5.98665, 51.76944], [5.95003, 51.7493], [6.04091, 51.71821], [6.02767, 51.6742], [6.11759, 51.65609], [6.09055, 51.60564], [6.18017, 51.54096], [6.21724, 51.48568], [6.20654, 51.40049], [6.22641, 51.39948], [6.22674, 51.36135], [6.16977, 51.33169], [6.07889, 51.24432], [6.07889, 51.17038], [6.17384, 51.19589], [6.16706, 51.15677], [5.98292, 51.07469], [5.9541, 51.03496], [5.9134, 51.06736], [5.86735, 51.05182], [5.87849, 51.01969], [5.90493, 51.00198], [5.90296, 50.97356], [5.95282, 50.98728], [6.02697, 50.98303], [6.01615, 50.93367], [6.09297, 50.92066], [6.07486, 50.89307], [6.08805, 50.87223], [6.07693, 50.86025], [6.07431, 50.84674], [6.05702, 50.85179], [6.05623, 50.8572], [6.01921, 50.84435], [6.02328, 50.81694], [6.00462, 50.80065], [5.98404, 50.80988], [5.97497, 50.79992], [6.02624, 50.77453], [6.01976, 50.75398], [6.03889, 50.74618], [6.0326, 50.72647], [6.0406, 50.71848], [6.04428, 50.72861], [6.11707, 50.72231], [6.17852, 50.6245], [6.26957, 50.62444], [6.2476, 50.60392], [6.24888, 50.59869], [6.24005, 50.58732], [6.22581, 50.5907], [6.20281, 50.56952], [6.17739, 50.55875], [6.17802, 50.54179], [6.19735, 50.53576], [6.19579, 50.5313], [6.18716, 50.52653], [6.19193, 50.5212], [6.20599, 50.52089], [6.22335, 50.49578], [6.26637, 50.50272], [6.30809, 50.50058], [6.3465, 50.48833], [6.34005, 50.46083], [6.37219, 50.45397], [6.36852, 50.40776], [6.34406, 50.37994], [6.3688, 50.35898], [6.40785, 50.33557], [6.40641, 50.32425], [6.35701, 50.31139], [6.32488, 50.32333], [6.29949, 50.30887], [6.28797, 50.27458], [6.208, 50.25179], [6.16853, 50.2234], [6.18364, 50.20815], [6.18739, 50.1822], [6.14588, 50.17106], [6.14132, 50.14971], [6.15298, 50.14126], [6.1379, 50.12964], [6.12055, 50.09171], [6.11274, 50.05916], [6.13458, 50.04141], [6.13044, 50.02929], [6.14666, 50.02207], [6.13794, 50.01466], [6.13273, 50.02019], [6.1295, 50.01849], [6.13806, 50.01056], [6.14948, 50.00908], [6.14147, 49.99563], [6.1701, 49.98518], [6.16466, 49.97086], [6.17872, 49.9537], [6.18554, 49.95622], [6.18045, 49.96611], [6.19089, 49.96991], [6.19856, 49.95053], [6.22094, 49.94955], [6.22608, 49.929], [6.21882, 49.92403], [6.22926, 49.92096], [6.23496, 49.89972], [6.26146, 49.88203], [6.28874, 49.87592], [6.29692, 49.86685], [6.30963, 49.87021], [6.32303, 49.85133], [6.32098, 49.83728], [6.33585, 49.83785], [6.34267, 49.84974], [6.36576, 49.85032], [6.40022, 49.82029], [6.42521, 49.81591], [6.42905, 49.81091], [6.44131, 49.81443], [6.45425, 49.81164], [6.47111, 49.82263], [6.48718, 49.81267], [6.50647, 49.80916], [6.51215, 49.80124], [6.52121, 49.81338], [6.53122, 49.80666], [6.52169, 49.79787], [6.50534, 49.78952], [6.51669, 49.78336], [6.51056, 49.77515], [6.51828, 49.76855], [6.51646, 49.75961], [6.50174, 49.75292], [6.50193, 49.73291], [6.51805, 49.72425], [6.51397, 49.72058], [6.50261, 49.72718], [6.49535, 49.72645], [6.49694, 49.72205], [6.5042, 49.71808], [6.50647, 49.71353], [6.49785, 49.71118], [6.48014, 49.69767], [6.46048, 49.69092], [6.44654, 49.67799], [6.42937, 49.66857], [6.42726, 49.66078], [6.43768, 49.66021], [6.4413, 49.65722], [6.41861, 49.61723], [6.39822, 49.60081], [6.385, 49.59946], [6.37464, 49.58886], [6.38342, 49.5799], [6.38024, 49.57593], [6.36676, 49.57813], [6.35825, 49.57053], [6.38228, 49.55855], [6.38072, 49.55171], [6.35666, 49.52931], [6.36788, 49.50377], [6.36907, 49.48931], [6.36778, 49.46937], [6.38352, 49.46463], [6.39168, 49.4667], [6.40274, 49.46546], [6.42432, 49.47683], [6.55404, 49.42464], [6.533, 49.40748], [6.60091, 49.36864], [6.58807, 49.35358], [6.572, 49.35027], [6.60186, 49.31055], [6.66583, 49.28065], [6.69274, 49.21661], [6.71843, 49.2208], [6.73256, 49.20486], [6.71137, 49.18808], [6.73765, 49.16375], [6.78265, 49.16793], [6.83385, 49.15162], [6.84703, 49.15734], [6.86225, 49.18185], [6.85016, 49.19354], [6.85119, 49.20038], [6.83555, 49.21249], [6.85939, 49.22376], [6.89298, 49.20863], [6.91875, 49.22261], [6.93831, 49.2223], [6.94028, 49.21641], [6.95963, 49.203], [6.97273, 49.2099], [7.01318, 49.19018], [7.03459, 49.19096], [7.0274, 49.17042], [7.03178, 49.15734], [7.04662, 49.13724], [7.04409, 49.12123], [7.04843, 49.11422], [7.05548, 49.11185], [7.06642, 49.11415], [7.07162, 49.1255], [7.09007, 49.13094], [7.07859, 49.15031], [7.10715, 49.15631], [7.10384, 49.13787], [7.12504, 49.14253], [7.1358, 49.1282], [7.1593, 49.1204], [7.23473, 49.12971], [7.29514, 49.11426], [7.3195, 49.14231], [7.35995, 49.14399], [7.3662, 49.17308], [7.44052, 49.18354], [7.44455, 49.16765], [7.49473, 49.17], [7.49172, 49.13915], [7.53012, 49.09818], [7.56416, 49.08136], [7.62575, 49.07654], [7.63618, 49.05428], [7.75948, 49.04562], [7.79557, 49.06583], [7.86386, 49.03499], [7.93641, 49.05544], [7.97783, 49.03161], [8.14189, 48.97833], [8.22604, 48.97352], [8.20031, 48.95856], [8.19989, 48.95825], [8.12813, 48.87985], [8.10253, 48.81829], [8.06802, 48.78957], [8.0326, 48.79017], [8.01534, 48.76085], [7.96994, 48.75606], [7.96812, 48.72491], [7.89002, 48.66317], [7.84098, 48.64217], [7.80057, 48.5857], [7.80167, 48.54758], [7.80647, 48.51239], [7.76833, 48.48945], [7.73109, 48.39192], [7.74562, 48.32736], [7.69022, 48.30018], [7.6648, 48.22219], [7.57137, 48.12292], [7.56966, 48.03265], [7.62302, 47.97898], [7.55673, 47.87371], [7.52921, 47.77747], [7.54761, 47.72912], [7.53722, 47.71635], [7.51266, 47.70197], [7.51915, 47.68335], [7.52067, 47.66437], [7.53384, 47.65115], [7.5591, 47.63849], [7.57423, 47.61628], [7.58851, 47.60794], [7.59301, 47.60058], [7.58945, 47.59017], [7.60523, 47.58519], [7.60459, 47.57869], [7.61929, 47.57683], [7.64309, 47.59151], [7.64213, 47.5944], [7.64599, 47.59695], [7.67395, 47.59212], [7.68229, 47.59905], [7.69385, 47.60099], [7.68486, 47.59601], [7.67115, 47.5871], [7.68904, 47.57133], [7.67655, 47.56435], [7.63338, 47.56256], [7.65083, 47.54662], [7.66174, 47.54554], [7.6656, 47.53752], [7.68101, 47.53232], [7.69642, 47.53297], [7.71961, 47.54219], [7.75261, 47.54599], [7.79486, 47.55691], [7.81901, 47.58798], [7.84412, 47.5841], [7.88664, 47.58854], [7.90673, 47.57674], [7.91251, 47.55031], [7.94494, 47.54511], [7.95682, 47.55789], [7.97581, 47.55493], [8.00113, 47.55616], [8.02136, 47.55096], [8.04383, 47.55443], [8.06663, 47.56374], [8.08557, 47.55768], [8.10002, 47.56504], [8.10395, 47.57918], [8.11543, 47.5841], [8.13662, 47.58432], [8.13823, 47.59147], [8.14947, 47.59558], [8.1652, 47.5945], [8.19378, 47.61636], [8.20617, 47.62141], [8.22011, 47.6181], [8.22577, 47.60385], [8.23809, 47.61204], [8.25863, 47.61571], [8.26313, 47.6103], [8.2824, 47.61225], [8.29722, 47.60603], [8.29524, 47.5919], [8.30277, 47.58607], [8.32735, 47.57133], [8.35512, 47.57014], [8.38273, 47.56608], [8.39477, 47.57826], [8.43235, 47.56617], [8.49431, 47.58107], [8.48949, 47.588], [8.46637, 47.58389], [8.45578, 47.60121], [8.50747, 47.61897], [8.51686, 47.63476], [8.55756, 47.62394], [8.57586, 47.59537], [8.60348, 47.61204], [8.59545, 47.64298], [8.60701, 47.65271], [8.61471, 47.64514], [8.60412, 47.63735], [8.62049, 47.63757], [8.62884, 47.65098], [8.61113, 47.66332], [8.6052, 47.67258], [8.57683, 47.66158], [8.56141, 47.67088], [8.52801, 47.66059], [8.5322, 47.64687], [8.49656, 47.64709], [8.46605, 47.64103], [8.4667, 47.65747], [8.44711, 47.65379], [8.42264, 47.66667], [8.41346, 47.66676], [8.40473, 47.67499], [8.4211, 47.68407], [8.40569, 47.69855], [8.44807, 47.72426], [8.45771, 47.7493], [8.48868, 47.77215], [8.56814, 47.78001], [8.56415, 47.80633], [8.61657, 47.79998], [8.62408, 47.7626], [8.64425, 47.76398], [8.65292, 47.80066], [8.68022, 47.78599], [8.68985, 47.75686], [8.71778, 47.76571], [8.74251, 47.75168], [8.70543, 47.73121], [8.73671, 47.7169], [8.72617, 47.69651]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "DG", + iso1A3: "DGA", + wikidata: "Q184851", + nameEn: "Diego Garcia", + country: "GB", + groups: ["IO", "BOTS", "014", "202", "002", "UN"], + isoStatus: "excRes", + callingCodes: ["246"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[73.14823, -7.76302], [73.09982, -6.07324], [71.43792, -7.73904], [73.14823, -7.76302]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "DJ", + iso1A3: "DJI", + iso1N3: "262", + wikidata: "Q977", + nameEn: "Djibouti", + groups: ["014", "202", "002", "UN"], + callingCodes: ["253"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "DK", + iso1A3: "DNK", + iso1N3: "208", + wikidata: "Q756617", + nameEn: "Kingdom of Denmark" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "DM", + iso1A3: "DMA", + iso1N3: "212", + wikidata: "Q784", + nameEn: "Dominica", + groups: ["029", "003", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + callingCodes: ["1 767"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-61.32485, 14.91445], [-60.86656, 15.82603], [-61.95646, 15.5094], [-61.32485, 14.91445]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "DO", + iso1A3: "DOM", + iso1N3: "214", + wikidata: "Q786", + nameEn: "Dominican Republic", + groups: ["029", "003", "419", "019", "UN"], + callingCodes: ["1 809", "1 829", "1 849"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-67.87844, 21.7938], [-72.38946, 20.27111], [-71.77419, 19.73128], [-71.75865, 19.70231], [-71.7429, 19.58445], [-71.71449, 19.55364], [-71.71268, 19.53374], [-71.6802, 19.45008], [-71.69448, 19.37866], [-71.77766, 19.33823], [-71.73229, 19.26686], [-71.62642, 19.21212], [-71.65337, 19.11759], [-71.69938, 19.10916], [-71.71088, 19.08353], [-71.74088, 19.0437], [-71.88102, 18.95007], [-71.77766, 18.95007], [-71.72624, 18.87802], [-71.71885, 18.78423], [-71.82556, 18.62551], [-71.95412, 18.64939], [-72.00201, 18.62312], [-71.88102, 18.50125], [-71.90875, 18.45821], [-71.69952, 18.34101], [-71.78271, 18.18302], [-71.75465, 18.14405], [-71.74994, 18.11115], [-71.73783, 18.07177], [-71.75671, 18.03456], [-72.29523, 17.48026], [-68.39466, 16.14167], [-67.87844, 21.7938]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "DZ", + iso1A3: "DZA", + iso1N3: "012", + wikidata: "Q262", + nameEn: "Algeria", + groups: ["015", "002", "UN"], + callingCodes: ["213"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "EA", + wikidata: "Q28868874", + nameEn: "Ceuta, Melilla", + country: "ES", + level: "territory", + isoStatus: "excRes" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "EC", + iso1A3: "ECU", + iso1N3: "218", + wikidata: "Q736", + nameEn: "Ecuador" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "EE", + iso1A3: "EST", + iso1N3: "233", + wikidata: "Q191", + nameEn: "Estonia", + aliases: ["EW"], + groups: ["EU", "154", "150", "UN"], + callingCodes: ["372"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[26.32936, 60.00121], [20.5104, 59.15546], [19.84909, 57.57876], [22.80496, 57.87798], [23.20055, 57.56697], [24.26221, 57.91787], [24.3579, 57.87471], [25.19484, 58.0831], [25.28237, 57.98539], [25.29581, 58.08288], [25.73499, 57.90193], [26.05949, 57.84744], [26.0324, 57.79037], [26.02456, 57.78342], [26.027, 57.78158], [26.0266, 57.77441], [26.02069, 57.77169], [26.02415, 57.76865], [26.03332, 57.7718], [26.0543, 57.76105], [26.08098, 57.76619], [26.2029, 57.7206], [26.1866, 57.6849], [26.29253, 57.59244], [26.46527, 57.56885], [26.54675, 57.51813], [26.90364, 57.62823], [27.34698, 57.52242], [27.31919, 57.57672], [27.40393, 57.62125], [27.3746, 57.66834], [27.52615, 57.72843], [27.50171, 57.78842], [27.56689, 57.83356], [27.78526, 57.83963], [27.81841, 57.89244], [27.67282, 57.92627], [27.62393, 58.09462], [27.48541, 58.22615], [27.55489, 58.39525], [27.36366, 58.78381], [27.74429, 58.98351], [27.80482, 59.1116], [27.87978, 59.18097], [27.90911, 59.24353], [28.00689, 59.28351], [28.14215, 59.28934], [28.19284, 59.35791], [28.20537, 59.36491], [28.21137, 59.38058], [28.19061, 59.39962], [28.04187, 59.47017], [27.85643, 59.58538], [26.90044, 59.63819], [26.32936, 60.00121]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "EG", + iso1A3: "EGY", + iso1N3: "818", + wikidata: "Q79", + nameEn: "Egypt", + groups: ["015", "002", "UN"], + callingCodes: ["20"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "EH", + iso1A3: "ESH", + iso1N3: "732", + wikidata: "Q6250", + nameEn: "Western Sahara", + groups: ["015", "002"], + callingCodes: ["212"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-8.66879, 27.6666], [-8.77527, 27.66663], [-8.71787, 26.9898], [-9.08698, 26.98639], [-9.56957, 26.90042], [-9.81998, 26.71379], [-10.68417, 26.90984], [-11.35695, 26.8505], [-11.23622, 26.72023], [-11.38635, 26.611], [-11.62052, 26.05229], [-12.06001, 26.04442], [-12.12281, 25.13682], [-12.92147, 24.39502], [-13.00628, 24.01923], [-13.75627, 23.77231], [-14.10361, 22.75501], [-14.1291, 22.41636], [-14.48112, 22.00886], [-14.47329, 21.63839], [-14.78487, 21.36587], [-16.44269, 21.39745], [-16.9978, 21.36239], [-17.02707, 21.34022], [-17.21511, 21.34226], [-17.35589, 20.80492], [-17.0471, 20.76408], [-17.0695, 20.85742], [-17.06781, 20.92697], [-17.0396, 20.9961], [-17.0357, 21.05368], [-16.99806, 21.12142], [-16.95474, 21.33997], [-13.01525, 21.33343], [-13.08438, 22.53866], [-13.15313, 22.75649], [-13.10753, 22.89493], [-13.00412, 23.02297], [-12.5741, 23.28975], [-12.36213, 23.3187], [-12.14969, 23.41935], [-12.00251, 23.4538], [-12.0002, 25.9986], [-8.66721, 25.99918], [-8.66674, 27.31569], [-8.66879, 27.6666]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "ER", + iso1A3: "ERI", + iso1N3: "232", + wikidata: "Q986", + nameEn: "Eritrea", + groups: ["014", "202", "002", "UN"], + callingCodes: ["291"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "ES", + iso1A3: "ESP", + iso1N3: "724", + wikidata: "Q29", + nameEn: "Spain" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "ET", + iso1A3: "ETH", + iso1N3: "231", + wikidata: "Q115", + nameEn: "Ethiopia", + groups: ["014", "202", "002", "UN"], + callingCodes: ["251"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[42.4037, 12.46478], [42.2798, 12.6355], [42.21469, 12.75832], [42.05841, 12.80912], [41.62864, 13.38626], [41.25097, 13.60787], [40.9167, 14.11152], [40.25686, 14.41445], [40.21128, 14.39342], [40.14649, 14.53969], [40.07236, 14.54264], [39.9443, 14.41024], [39.76632, 14.54264], [39.58182, 14.60987], [39.50585, 14.55735], [39.52756, 14.49011], [39.37685, 14.54402], [39.2519, 14.40393], [39.2302, 14.44598], [39.26927, 14.48801], [39.23888, 14.56365], [39.19547, 14.56996], [39.14772, 14.61827], [39.16074, 14.65187], [39.02834, 14.63717], [38.98058, 14.54895], [38.78306, 14.4754], [38.45748, 14.41445], [38.3533, 14.51323], [38.25562, 14.67287], [38.0364, 14.72745], [37.91287, 14.89447], [37.528, 14.18413], [37.47319, 14.2149], [37.3106, 14.44657], [37.13206, 14.40746], [37.09486, 14.27155], [37.01622, 14.2561], [36.85787, 14.32201], [36.63364, 14.31172], [36.55659, 14.28237], [36.56536, 14.26177], [36.54376, 14.25597], [36.44653, 13.95666], [36.48824, 13.83954], [36.38993, 13.56459], [36.24545, 13.36759], [36.13374, 12.92665], [36.16651, 12.88019], [36.14268, 12.70879], [36.01458, 12.72478], [35.70476, 12.67101], [35.24302, 11.91132], [35.11492, 11.85156], [35.05832, 11.71158], [35.09556, 11.56278], [34.95704, 11.24448], [35.01215, 11.19626], [34.93172, 10.95946], [34.97789, 10.91559], [34.97491, 10.86147], [34.86916, 10.78832], [34.86618, 10.74588], [34.77532, 10.69027], [34.77383, 10.74588], [34.59062, 10.89072], [34.4372, 10.781], [34.2823, 10.53508], [34.34783, 10.23914], [34.32102, 10.11599], [34.22718, 10.02506], [34.20484, 9.9033], [34.13186, 9.7492], [34.08717, 9.55243], [34.10229, 9.50238], [34.14304, 9.04654], [34.14453, 8.60204], [34.01346, 8.50041], [33.89579, 8.4842], [33.87195, 8.41938], [33.71407, 8.3678], [33.66938, 8.44442], [33.54575, 8.47094], [33.3119, 8.45474], [33.19721, 8.40317], [33.1853, 8.29264], [33.18083, 8.13047], [33.08401, 8.05822], [33.0006, 7.90333], [33.04944, 7.78989], [33.24637, 7.77939], [33.32531, 7.71297], [33.44745, 7.7543], [33.71407, 7.65983], [33.87642, 7.5491], [34.02984, 7.36449], [34.03878, 7.27437], [34.01495, 7.25664], [34.19369, 7.12807], [34.19369, 7.04382], [34.35753, 6.91963], [34.47669, 6.91076], [34.53925, 6.82794], [34.53776, 6.74808], [34.65096, 6.72589], [34.77459, 6.5957], [34.87736, 6.60161], [35.01738, 6.46991], [34.96227, 6.26415], [35.00546, 5.89387], [35.12611, 5.68937], [35.13058, 5.62118], [35.31188, 5.50106], [35.29938, 5.34042], [35.50792, 5.42431], [35.8576, 5.33413], [35.81968, 5.10757], [35.82118, 4.77382], [35.9419, 4.61933], [35.95449, 4.53244], [36.03924, 4.44406], [36.84474, 4.44518], [37.07724, 4.33503], [38.14168, 3.62487], [38.45812, 3.60445], [38.52336, 3.62551], [38.91938, 3.51198], [39.07736, 3.5267], [39.19954, 3.47834], [39.49444, 3.45521], [39.51551, 3.40895], [39.55132, 3.39634], [39.58339, 3.47434], [39.76808, 3.67058], [39.86043, 3.86974], [40.77498, 4.27683], [41.1754, 3.94079], [41.89488, 3.97375], [42.07619, 4.17667], [42.55853, 4.20518], [42.84526, 4.28357], [42.97746, 4.44032], [43.04177, 4.57923], [43.40263, 4.79289], [44.02436, 4.9451], [44.98104, 4.91821], [47.97917, 8.00124], [47.92477, 8.00111], [46.99339, 7.9989], [44.19222, 8.93028], [43.32613, 9.59205], [43.23518, 9.84605], [43.0937, 9.90579], [42.87643, 10.18441], [42.69452, 10.62672], [42.95776, 10.98533], [42.79037, 10.98493], [42.75111, 11.06992], [42.62989, 11.09711], [42.42669, 10.98493], [42.13691, 10.97586], [42.06302, 10.92599], [41.80056, 10.97127], [41.8096, 11.33606], [41.77727, 11.49902], [41.82878, 11.72361], [41.95461, 11.81157], [42.4037, 12.46478]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "EU", + iso1A3: "EUE", + wikidata: "Q458", + nameEn: "European Union", + level: "union", + isoStatus: "excRes" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "FI", + iso1A3: "FIN", + iso1N3: "246", + wikidata: "Q33", + nameEn: "Finland", + aliases: ["SF"] + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "FJ", + iso1A3: "FJI", + iso1N3: "242", + wikidata: "Q712", + nameEn: "Fiji", + groups: ["054", "009", "UN"], + driveSide: "left", + callingCodes: ["679"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "FK", + iso1A3: "FLK", + iso1N3: "238", + wikidata: "Q9648", + nameEn: "Falkland Islands", + country: "GB", + groups: ["BOTS", "005", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["500"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-63.67376, -55.11859], [-54.56126, -51.26248], [-61.26735, -50.63919], [-63.67376, -55.11859]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "FM", + iso1A3: "FSM", + iso1N3: "583", + wikidata: "Q702", + nameEn: "Federated States of Micronesia", + groups: ["057", "009", "UN"], + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["691"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[138.20583, 13.3783], [136.27107, 6.73747], [156.88247, -1.39237], [165.19726, 6.22546], [138.20583, 13.3783]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "FO", + iso1A3: "FRO", + iso1N3: "234", + wikidata: "Q4628", + nameEn: "Faroe Islands", + country: "DK", + groups: ["154", "150", "UN"], + callingCodes: ["298"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-8.51774, 62.35338], [-6.51083, 60.95272], [-5.70102, 62.77194], [-8.51774, 62.35338]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "FR", + iso1A3: "FRA", + iso1N3: "250", + wikidata: "Q142", + nameEn: "France" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "FX", + iso1A3: "FXX", + iso1N3: "249", + wikidata: "Q212429", + nameEn: "Metropolitan France", + country: "FR", + groups: ["EU", "155", "150", "UN"], + isoStatus: "excRes", + callingCodes: ["33"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[2.55904, 51.07014], [2.18458, 51.52087], [1.17405, 50.74239], [-2.02963, 49.91866], [-2.09454, 49.46288], [-1.83944, 49.23037], [-2.00491, 48.86706], [-2.65349, 49.15373], [-6.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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GA", + iso1A3: "GAB", + iso1N3: "266", + wikidata: "Q1000", + nameEn: "Gabon", + groups: ["017", "202", "002", "UN"], + callingCodes: ["241"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[13.29457, 2.16106], [13.28534, 2.25716], [11.37116, 2.29975], [11.3561, 2.17217], [11.35307, 1.00251], [9.79648, 1.0019], [9.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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GB", + iso1A3: "GBR", + iso1N3: "826", + wikidata: "Q145", + ccTLD: ".uk", + nameEn: "United Kingdom", + aliases: ["UK"] + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "GD", + iso1A3: "GRD", + iso1N3: "308", + wikidata: "Q769", + nameEn: "Grenada", + aliases: ["WG"], + groups: ["029", "003", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + callingCodes: ["1 473"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-62.64026, 12.69984], [-61.77886, 11.36496], [-59.94058, 12.34011], [-62.64026, 12.69984]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GE", + iso1A3: "GEO", + iso1N3: "268", + wikidata: "Q230", + nameEn: "Georgia", + groups: ["145", "142", "UN"], + callingCodes: ["995"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[46.42738, 41.91323], [45.61676, 42.20768], [45.78692, 42.48358], [45.36501, 42.55268], [45.15318, 42.70598], [44.88754, 42.74934], [44.80941, 42.61277], [44.70002, 42.74679], [44.54202, 42.75699], [43.95517, 42.55396], [43.73119, 42.62043], [43.81453, 42.74297], [43.0419, 43.02413], [43.03322, 43.08883], [42.75889, 43.19651], [42.66667, 43.13917], [42.40563, 43.23226], [41.64935, 43.22331], [40.65957, 43.56212], [40.10657, 43.57344], [40.04445, 43.47776], [40.03312, 43.44262], [40.01007, 43.42411], [40.01552, 43.42025], [40.00853, 43.40578], [40.0078, 43.38551], [39.81147, 43.06294], [40.89217, 41.72528], [41.54366, 41.52185], [41.7148, 41.4932], [41.7124, 41.47417], [41.81939, 41.43621], [41.95134, 41.52466], [42.26387, 41.49346], [42.51772, 41.43606], [42.59202, 41.58183], [42.72794, 41.59714], [42.84471, 41.58912], [42.78995, 41.50126], [42.84899, 41.47265], [42.8785, 41.50516], [43.02956, 41.37891], [43.21707, 41.30331], [43.13373, 41.25503], [43.1945, 41.25242], [43.23096, 41.17536], [43.36118, 41.2028], [43.44973, 41.17666], [43.4717, 41.12611], [43.67712, 41.13398], [43.74717, 41.1117], [43.84835, 41.16329], [44.16591, 41.19141], [44.18148, 41.24644], [44.32139, 41.2079], [44.34337, 41.20312], [44.34417, 41.2382], [44.46791, 41.18204], [44.59322, 41.1933], [44.61462, 41.24018], [44.72814, 41.20338], [44.82084, 41.21513], [44.87887, 41.20195], [44.89911, 41.21366], [44.84358, 41.23088], [44.81749, 41.23488], [44.80053, 41.25949], [44.81437, 41.30371], [44.93493, 41.25685], [45.0133, 41.29747], [45.09867, 41.34065], [45.1797, 41.42231], [45.26285, 41.46433], [45.31352, 41.47168], [45.4006, 41.42402], [45.45973, 41.45898], [45.68389, 41.3539], [45.71035, 41.36208], [45.75705, 41.35157], [45.69946, 41.29545], [45.80842, 41.2229], [45.95786, 41.17956], [46.13221, 41.19479], [46.27698, 41.19011], [46.37661, 41.10805], [46.456, 41.09984], [46.48558, 41.0576], [46.55096, 41.1104], [46.63969, 41.09515], [46.66148, 41.20533], [46.72375, 41.28609], [46.63658, 41.37727], [46.4669, 41.43331], [46.40307, 41.48464], [46.33925, 41.4963], [46.29794, 41.5724], [46.34126, 41.57454], [46.34039, 41.5947], [46.3253, 41.60912], [46.28182, 41.60089], [46.26531, 41.63339], [46.24429, 41.59883], [46.19759, 41.62327], [46.17891, 41.72094], [46.20538, 41.77205], [46.23962, 41.75811], [46.30863, 41.79133], [46.3984, 41.84399], [46.42738, 41.91323]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GF", + iso1A3: "GUF", + iso1N3: "254", + wikidata: "Q3769", + nameEn: "French Guiana", + country: "FR", + groups: ["Q3320166", "EU", "005", "419", "019", "UN"], + callingCodes: ["594"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-51.35485, 4.8383], [-53.7094, 6.2264], [-54.01074, 5.68785], [-54.01877, 5.52789], [-54.26916, 5.26909], [-54.4717, 4.91964], [-54.38444, 4.13222], [-54.19367, 3.84387], [-54.05128, 3.63557], [-53.98914, 3.627], [-53.9849, 3.58697], [-54.28534, 2.67798], [-54.42864, 2.42442], [-54.6084, 2.32856], [-54.16286, 2.10779], [-53.78743, 2.34412], [-52.96539, 2.1881], [-52.6906, 2.37298], [-52.31787, 3.17896], [-51.85573, 3.83427], [-51.82312, 3.85825], [-51.79599, 3.89336], [-51.61983, 4.14596], [-51.63798, 4.51394], [-51.35485, 4.8383]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GG", + iso1A3: "GGY", + iso1N3: "831", + wikidata: "Q25230", + nameEn: "Bailiwick of Guernsey", + country: "GB" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "GH", + iso1A3: "GHA", + iso1N3: "288", + wikidata: "Q117", + nameEn: "Ghana", + groups: ["011", "202", "002", "UN"], + callingCodes: ["233"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-0.13493, 11.14075], [-0.27374, 11.17157], [-0.28566, 11.12713], [-0.35955, 11.07801], [-0.38219, 11.12596], [-0.42391, 11.11661], [-0.44298, 11.04292], [-0.61937, 10.91305], [-0.67143, 10.99811], [-2.83373, 11.0067], [-2.94232, 10.64281], [-2.83108, 10.40252], [-2.74174, 9.83172], [-2.76534, 9.56589], [-2.68802, 9.49343], [-2.69814, 9.22717], [-2.77799, 9.04949], [-2.66357, 9.01771], [-2.58243, 8.7789], [-2.49037, 8.20872], [-2.62901, 8.11495], [-2.61232, 8.02645], [-2.67787, 8.02055], [-2.74819, 7.92613], [-2.78395, 7.94974], [-2.79467, 7.86002], [-2.92339, 7.60847], [-2.97822, 7.27165], [-2.95438, 7.23737], [-3.23327, 6.81744], [-3.21954, 6.74407], [-3.25999, 6.62521], [-3.01896, 5.71697], [-2.95323, 5.71865], [-2.96671, 5.6415], [-2.93132, 5.62137], [-2.85378, 5.65156], [-2.76614, 5.60963], [-2.72737, 5.34789], [-2.77625, 5.34621], [-2.73074, 5.1364], [-2.75502, 5.10657], [-2.95261, 5.12477], [-2.96554, 5.10397], [-3.063, 5.13665], [-3.11073, 5.12675], [-3.10675, 5.08515], [-3.34019, 4.17519], [1.07031, 5.15655], [1.27574, 5.93551], [1.19771, 6.11522], [1.19966, 6.17069], [1.09187, 6.17074], [1.05969, 6.22998], [1.03108, 6.24064], [0.99652, 6.33779], [0.89283, 6.33779], [0.71048, 6.53083], [0.74862, 6.56517], [0.63659, 6.63857], [0.6497, 6.73682], [0.58176, 6.76049], [0.57406, 6.80348], [0.52853, 6.82921], [0.56508, 6.92971], [0.52098, 6.94391], [0.52217, 6.9723], [0.59606, 7.01252], [0.65327, 7.31643], [0.62943, 7.41099], [0.57223, 7.39326], [0.52455, 7.45354], [0.51979, 7.58706], [0.58295, 7.62368], [0.62943, 7.85751], [0.58891, 8.12779], [0.6056, 8.13959], [0.61156, 8.18324], [0.5913, 8.19622], [0.63897, 8.25873], [0.73432, 8.29529], [0.64731, 8.48866], [0.47211, 8.59945], [0.37319, 8.75262], [0.52455, 8.87746], [0.45424, 9.04581], [0.56388, 9.40697], [0.49118, 9.48339], [0.36485, 9.49749], [0.33148, 9.44812], [0.25758, 9.42696], [0.2254, 9.47869], [0.31241, 9.50337], [0.30406, 9.521], [0.2409, 9.52335], [0.23851, 9.57389], [0.38153, 9.58682], [0.36008, 9.6256], [0.29334, 9.59387], [0.26712, 9.66437], [0.28261, 9.69022], [0.32313, 9.6491], [0.34816, 9.66907], [0.34816, 9.71607], [0.32075, 9.72781], [0.36366, 10.03309], [0.41252, 10.02018], [0.41371, 10.06361], [0.35293, 10.09412], [0.39584, 10.31112], [0.33028, 10.30408], [0.29453, 10.41546], [0.18846, 10.4096], [0.12886, 10.53149], [-0.05945, 10.63458], [-0.09141, 10.7147], [-0.07327, 10.71845], [-0.07183, 10.76794], [-0.0228, 10.81916], [-0.02685, 10.8783], [-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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GI", + iso1A3: "GIB", + iso1N3: "292", + wikidata: "Q1410", + nameEn: "Gibraltar", + country: "GB", + groups: ["Q12837", "BOTS", "039", "150", "UN"], + callingCodes: ["350"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GL", + iso1A3: "GRL", + iso1N3: "304", + wikidata: "Q223", + nameEn: "Greenland", + country: "DK", + groups: ["Q1451600", "021", "003", "019", "UN"], + callingCodes: ["299"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GM", + iso1A3: "GMB", + iso1N3: "270", + wikidata: "Q1005", + nameEn: "The Gambia", + groups: ["011", "202", "002", "UN"], + callingCodes: ["220"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-15.14917, 13.57989], [-14.36795, 13.23033], [-13.79409, 13.34472], [-13.8955, 13.59126], [-14.34721, 13.46578], [-14.93719, 13.80173], [-15.36504, 13.79313], [-15.47902, 13.58758], [-17.43598, 13.59273], [-17.43966, 13.04579], [-16.74676, 13.06025], [-16.69343, 13.16791], [-15.80355, 13.16729], [-15.80478, 13.34832], [-15.26908, 13.37768], [-15.14917, 13.57989]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GN", + iso1A3: "GIN", + iso1N3: "324", + wikidata: "Q1006", + nameEn: "Guinea", + groups: ["011", "202", "002", "UN"], + callingCodes: ["224"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-11.37536, 12.40788], [-11.46267, 12.44559], [-11.91331, 12.42008], [-12.35415, 12.32758], [-12.87336, 12.51892], [-13.06603, 12.49342], [-13.05296, 12.64003], [-13.70523, 12.68013], [-13.7039, 12.60313], [-13.65089, 12.49515], [-13.64168, 12.42764], [-13.70851, 12.24978], [-13.92745, 12.24077], [-13.94589, 12.16869], [-13.7039, 12.00869], [-13.7039, 11.70195], [-14.09799, 11.63649], [-14.26623, 11.67486], [-14.31513, 11.60713], [-14.51173, 11.49708], [-14.66677, 11.51188], [-14.77786, 11.36323], [-14.95993, 10.99244], [-15.07174, 10.89557], [-15.96748, 10.162], [-14.36218, 8.64107], [-13.29911, 9.04245], [-13.18586, 9.0925], [-13.08953, 9.0409], [-12.94095, 9.26335], [-12.76788, 9.3133], [-12.47254, 9.86834], [-12.24262, 9.92386], [-12.12634, 9.87203], [-11.91023, 9.93927], [-11.89624, 9.99763], [-11.2118, 10.00098], [-10.6534, 9.29919], [-10.74484, 9.07998], [-10.5783, 9.06386], [-10.56197, 8.81225], [-10.47707, 8.67669], [-10.61422, 8.5314], [-10.70565, 8.29235], [-10.63934, 8.35326], [-10.54891, 8.31174], [-10.37257, 8.48941], [-10.27575, 8.48711], [-10.203, 8.47991], [-10.14579, 8.52665], [-10.05375, 8.50697], [-10.05873, 8.42578], [-9.77763, 8.54633], [-9.47415, 8.35195], [-9.50898, 8.18455], [-9.41445, 8.02448], [-9.44928, 7.9284], [-9.35724, 7.74111], [-9.37465, 7.62032], [-9.48161, 7.37122], [-9.41943, 7.41809], [-9.305, 7.42056], [-9.20798, 7.38109], [-9.18311, 7.30461], [-9.09107, 7.1985], [-8.93435, 7.2824], [-8.85724, 7.26019], [-8.8448, 7.35149], [-8.72789, 7.51429], [-8.67814, 7.69428], [-8.55874, 7.70167], [-8.55874, 7.62525], [-8.47114, 7.55676], [-8.4003, 7.6285], [-8.21374, 7.54466], [-8.09931, 7.78626], [-8.13414, 7.87991], [-8.06449, 8.04989], [-7.94695, 8.00925], [-7.99919, 8.11023], [-7.98675, 8.20134], [-8.062, 8.16071], [-8.2411, 8.24196], [-8.22991, 8.48438], [-7.92518, 8.50652], [-7.65653, 8.36873], [-7.69882, 8.66148], [-7.95503, 8.81146], [-7.92518, 8.99332], [-7.73862, 9.08422], [-7.90777, 9.20456], [-7.85056, 9.41812], [-8.03463, 9.39604], [-8.14657, 9.55062], [-8.09434, 9.86936], [-8.15652, 9.94288], [-8.11921, 10.04577], [-8.01225, 10.1021], [-7.97971, 10.17117], [-7.9578, 10.2703], [-8.10207, 10.44649], [-8.22711, 10.41722], [-8.32614, 10.69273], [-8.2667, 10.91762], [-8.35083, 11.06234], [-8.66923, 10.99397], [-8.40058, 11.37466], [-8.80854, 11.66715], [-8.94784, 12.34842], [-9.13689, 12.50875], [-9.38067, 12.48446], [-9.32097, 12.29009], [-9.63938, 12.18312], [-9.714, 12.0226], [-10.30604, 12.24634], [-10.71897, 11.91552], [-10.80355, 12.1053], [-10.99758, 12.24634], [-11.24136, 12.01286], [-11.50006, 12.17826], [-11.37536, 12.40788]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GP", + iso1A3: "GLP", + iso1N3: "312", + wikidata: "Q17012", + nameEn: "Guadeloupe", + country: "FR", + groups: ["Q3320166", "EU", "029", "003", "419", "019", "UN"], + callingCodes: ["590"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-60.03183, 16.1129], [-61.60296, 16.73066], [-63.00549, 15.26166], [-60.03183, 16.1129]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GQ", + iso1A3: "GNQ", + iso1N3: "226", + wikidata: "Q983", + nameEn: "Equatorial Guinea", + groups: ["017", "202", "002", "UN"], + callingCodes: ["240"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GR", + iso1A3: "GRC", + iso1N3: "300", + wikidata: "Q41", + nameEn: "Greece", + aliases: ["EL"], + groups: ["EU", "039", "150", "UN"], + callingCodes: ["30"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[26.03489, 40.73051], [26.0754, 40.72772], [26.08638, 40.73214], [26.12495, 40.74283], [26.12854, 40.77339], [26.15685, 40.80709], [26.21351, 40.83298], [26.20856, 40.86048], [26.26169, 40.9168], [26.29441, 40.89119], [26.28623, 40.93005], [26.32259, 40.94042], [26.35894, 40.94292], [26.33297, 40.98388], [26.3606, 41.02027], [26.31928, 41.07386], [26.32259, 41.24929], [26.39861, 41.25053], [26.5209, 41.33993], [26.5837, 41.32131], [26.62997, 41.34613], [26.61767, 41.42281], [26.59742, 41.48058], [26.59196, 41.60491], [26.5209, 41.62592], [26.47958, 41.67037], [26.35957, 41.71149], [26.30255, 41.70925], [26.2654, 41.71544], [26.22888, 41.74139], [26.21325, 41.73223], [26.16841, 41.74858], [26.06148, 41.70345], [26.07083, 41.64584], [26.15146, 41.60828], [26.14328, 41.55496], [26.17951, 41.55409], [26.176, 41.50072], [26.14796, 41.47533], [26.20288, 41.43943], [26.16548, 41.42278], [26.12926, 41.35878], [25.87919, 41.30526], [25.8266, 41.34563], [25.70507, 41.29209], [25.66183, 41.31316], [25.61042, 41.30614], [25.55082, 41.31667], [25.52394, 41.2798], [25.48187, 41.28506], [25.28322, 41.23411], [25.11611, 41.34212], [24.942, 41.38685], [24.90928, 41.40876], [24.86136, 41.39298], [24.82514, 41.4035], [24.8041, 41.34913], [24.71529, 41.41928], [24.61129, 41.42278], [24.52599, 41.56808], [24.30513, 41.51297], [24.27124, 41.57682], [24.18126, 41.51735], [24.10063, 41.54796], [24.06323, 41.53222], [24.06908, 41.46132], [23.96975, 41.44118], [23.91483, 41.47971], [23.89613, 41.45257], [23.80148, 41.43943], [23.76525, 41.40175], [23.67644, 41.41139], [23.63203, 41.37632], [23.52453, 41.40262], [23.40416, 41.39999], [23.33639, 41.36317], [23.31301, 41.40525], [23.22771, 41.37106], [23.21953, 41.33773], [23.1833, 41.31755], [22.93334, 41.34104], [22.81199, 41.3398], [22.76408, 41.32225], [22.74538, 41.16321], [22.71266, 41.13945], [22.65306, 41.18168], [22.62852, 41.14385], [22.58295, 41.11568], [22.5549, 41.13065], [22.42285, 41.11921], [22.26744, 41.16409], [22.17629, 41.15969], [22.1424, 41.12449], [22.06527, 41.15617], [21.90869, 41.09191], [21.91102, 41.04786], [21.7556, 40.92525], [21.69601, 40.9429], [21.57448, 40.86076], [21.53007, 40.90759], [21.41555, 40.9173], [21.35595, 40.87578], [21.25779, 40.86165], [21.21105, 40.8855], [21.15262, 40.85546], [20.97887, 40.85475], [20.98396, 40.79109], [20.95752, 40.76982], [20.98134, 40.76046], [21.05833, 40.66586], [21.03932, 40.56299], [20.96908, 40.51526], [20.94925, 40.46625], [20.83688, 40.47882], [20.7906, 40.42726], [20.78234, 40.35803], [20.71789, 40.27739], [20.67162, 40.09433], [20.62566, 40.0897], [20.61081, 40.07866], [20.55593, 40.06524], [20.51297, 40.08168], [20.48487, 40.06271], [20.42373, 40.06777], [20.37911, 39.99058], [20.31135, 39.99438], [20.41546, 39.82832], [20.41475, 39.81437], [20.38572, 39.78516], [20.30804, 39.81563], [20.29152, 39.80421], [20.31961, 39.72799], [20.27412, 39.69884], [20.22707, 39.67459], [20.22376, 39.64532], [20.15988, 39.652], [20.12956, 39.65805], [20.05189, 39.69112], [20.00957, 39.69227], [19.98042, 39.6504], [19.92466, 39.69533], [19.97622, 39.78684], [19.95905, 39.82857], [19.0384, 40.35325], [19.20409, 39.7532], [22.5213, 33.45682], [29.73302, 35.92555], [29.69611, 36.10365], [29.61805, 36.14179], [29.61002, 36.1731], [29.48192, 36.18377], [29.30783, 36.01033], [28.23708, 36.56812], [27.95037, 36.46155], [27.89482, 36.69898], [27.46117, 36.53789], [27.24613, 36.71622], [27.45627, 36.9008], [27.20312, 36.94571], [27.14757, 37.32], [26.95583, 37.64989], [26.99377, 37.69034], [27.16428, 37.72343], [27.05537, 37.9131], [26.21136, 38.17558], [26.24183, 38.44695], [26.32173, 38.48731], [26.21136, 38.65436], [26.61814, 38.81372], [26.70773, 39.0312], [26.43357, 39.43096], [25.94257, 39.39358], [25.61285, 40.17161], [26.04292, 40.3958], [25.94795, 40.72797], [26.03489, 40.73051]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GS", + iso1A3: "SGS", + iso1N3: "239", + wikidata: "Q35086", + nameEn: "South Georgia and South Sandwich Islands", + country: "GB", + groups: ["BOTS", "005", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["500"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-35.26394, -43.68272], [-53.39656, -59.87088], [-22.31757, -59.85974], [-35.26394, -43.68272]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GT", + iso1A3: "GTM", + iso1N3: "320", + wikidata: "Q774", + nameEn: "Guatemala", + groups: ["013", "003", "419", "019", "UN"], + callingCodes: ["502"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-89.14985, 17.81563], [-90.98678, 17.81655], [-90.99199, 17.25192], [-91.43809, 17.25373], [-91.04436, 16.92175], [-90.69064, 16.70697], [-90.61212, 16.49832], [-90.40499, 16.40524], [-90.44567, 16.07573], [-91.73182, 16.07371], [-92.20983, 15.26077], [-92.0621, 15.07406], [-92.1454, 14.98143], [-92.1423, 14.88647], [-92.18161, 14.84147], [-92.1454, 14.6804], [-92.2261, 14.53423], [-92.37213, 14.39277], [-90.55276, 12.8866], [-90.11344, 13.73679], [-90.10505, 13.85104], [-89.88937, 14.0396], [-89.81807, 14.07073], [-89.76103, 14.02923], [-89.73251, 14.04133], [-89.75569, 14.07073], [-89.70756, 14.1537], [-89.61844, 14.21937], [-89.52397, 14.22628], [-89.50614, 14.26084], [-89.58814, 14.33165], [-89.57441, 14.41637], [-89.39028, 14.44561], [-89.34776, 14.43013], [-89.35189, 14.47553], [-89.23719, 14.58046], [-89.15653, 14.57802], [-89.13132, 14.71582], [-89.23467, 14.85596], [-89.15149, 14.97775], [-89.18048, 14.99967], [-89.15149, 15.07392], [-88.97343, 15.14039], [-88.32467, 15.63665], [-88.31459, 15.66942], [-88.24022, 15.69247], [-88.22552, 15.72294], [-88.20359, 16.03858], [-88.40779, 16.09624], [-88.95358, 15.88698], [-89.02415, 15.9063], [-89.17418, 15.90898], [-89.22683, 15.88619], [-89.15025, 17.04813], [-89.14985, 17.81563]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GU", + iso1A3: "GUM", + iso1N3: "316", + wikidata: "Q16635", + nameEn: "Guam", + aliases: ["US-GU"], + country: "US", + groups: ["Q1352230", "Q153732", "057", "009", "UN"], + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1 671"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[146.25931, 13.85876], [143.82485, 13.92273], [144.61642, 12.82462], [146.25931, 13.85876]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GW", + iso1A3: "GNB", + iso1N3: "624", + wikidata: "Q1007", + nameEn: "Guinea-Bissau", + groups: ["011", "202", "002", "UN"], + callingCodes: ["245"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-14.31513, 11.60713], [-14.26623, 11.67486], [-14.09799, 11.63649], [-13.7039, 11.70195], [-13.7039, 12.00869], [-13.94589, 12.16869], [-13.92745, 12.24077], [-13.70851, 12.24978], [-13.64168, 12.42764], [-13.65089, 12.49515], [-13.7039, 12.60313], [-13.70523, 12.68013], [-15.17582, 12.6847], [-15.67302, 12.42974], [-16.20591, 12.46157], [-16.38191, 12.36449], [-16.70562, 12.34803], [-17.4623, 11.92379], [-15.96748, 10.162], [-15.07174, 10.89557], [-14.95993, 10.99244], [-14.77786, 11.36323], [-14.66677, 11.51188], [-14.51173, 11.49708], [-14.31513, 11.60713]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "GY", + iso1A3: "GUY", + iso1N3: "328", + wikidata: "Q734", + nameEn: "Guyana", + groups: ["005", "419", "019", "UN"], + driveSide: "left", + callingCodes: ["592"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-56.84822, 6.73257], [-59.54058, 8.6862], [-59.98508, 8.53046], [-59.85562, 8.35213], [-59.80661, 8.28906], [-59.83156, 8.23261], [-59.97059, 8.20791], [-60.02407, 8.04557], [-60.38056, 7.8302], [-60.51959, 7.83373], [-60.64793, 7.56877], [-60.71923, 7.55817], [-60.59802, 7.33194], [-60.63367, 7.25061], [-60.54098, 7.14804], [-60.44116, 7.20817], [-60.28074, 7.1162], [-60.39419, 6.94847], [-60.54873, 6.8631], [-61.13632, 6.70922], [-61.20762, 6.58174], [-61.15058, 6.19558], [-61.4041, 5.95304], [-60.73204, 5.20931], [-60.32352, 5.21299], [-60.20944, 5.28754], [-59.98129, 5.07097], [-60.04189, 4.69801], [-60.15953, 4.53456], [-59.78878, 4.45637], [-59.69361, 4.34069], [-59.73353, 4.20399], [-59.51963, 3.91951], [-59.86899, 3.57089], [-59.79769, 3.37162], [-59.99733, 2.92312], [-59.91177, 2.36759], [-59.7264, 2.27497], [-59.74066, 1.87596], [-59.25583, 1.40559], [-58.92072, 1.31293], [-58.84229, 1.17749], [-58.53571, 1.29154], [-58.4858, 1.48399], [-58.33887, 1.58014], [-58.01873, 1.51966], [-57.97336, 1.64566], [-57.77281, 1.73344], [-57.55743, 1.69605], [-57.35073, 1.98327], [-57.23981, 1.95808], [-57.09109, 2.01854], [-57.07092, 1.95304], [-56.7659, 1.89509], [-56.47045, 1.95135], [-56.55439, 2.02003], [-56.70519, 2.02964], [-57.35891, 3.32121], [-58.0307, 3.95513], [-57.8699, 4.89394], [-57.37442, 5.0208], [-57.22536, 5.15605], [-57.31629, 5.33714], [-56.84822, 6.73257]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "HK", + iso1A3: "HKG", + iso1N3: "344", + wikidata: "Q8646", + nameEn: "Hong Kong", + country: "CN", + groups: ["030", "142", "UN"], + driveSide: "left", + callingCodes: ["852"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[113.92195, 22.13873], [114.50148, 22.15017], [114.44998, 22.55977], [114.25154, 22.55977], [114.22888, 22.5436], [114.22185, 22.55343], [114.20655, 22.55706], [114.18338, 22.55444], [114.17247, 22.55944], [114.1597, 22.56041], [114.15123, 22.55163], [114.1482, 22.54091], [114.13823, 22.54319], [114.12665, 22.54003], [114.11656, 22.53415], [114.11181, 22.52878], [114.1034, 22.5352], [114.09692, 22.53435], [114.09048, 22.53716], [114.08606, 22.53276], [114.07817, 22.52997], [114.07267, 22.51855], [114.06272, 22.51617], [114.05729, 22.51104], [114.05438, 22.5026], [114.03113, 22.5065], [113.86771, 22.42972], [113.81621, 22.2163], [113.83338, 22.1826], [113.92195, 22.13873]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "HM", + iso1A3: "HMD", + iso1N3: "334", + wikidata: "Q131198", + nameEn: "Heard Island and McDonald Islands", + country: "AU", + groups: ["053", "009", "UN"], + driveSide: "left" + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[71.08716, -53.87687], [75.44182, -53.99822], [72.87012, -51.48322], [71.08716, -53.87687]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "HN", + iso1A3: "HND", + iso1N3: "340", + wikidata: "Q783", + nameEn: "Honduras", + groups: ["013", "003", "419", "019", "UN"], + callingCodes: ["504"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-83.86109, 17.73736], [-88.20359, 16.03858], [-88.22552, 15.72294], [-88.24022, 15.69247], [-88.31459, 15.66942], [-88.32467, 15.63665], [-88.97343, 15.14039], [-89.15149, 15.07392], [-89.18048, 14.99967], [-89.15149, 14.97775], [-89.23467, 14.85596], [-89.13132, 14.71582], [-89.15653, 14.57802], [-89.23719, 14.58046], [-89.35189, 14.47553], [-89.34776, 14.43013], [-89.04187, 14.33644], [-88.94608, 14.20207], [-88.85785, 14.17763], [-88.815, 14.11652], [-88.73182, 14.10919], [-88.70661, 14.04317], [-88.49738, 13.97224], [-88.48982, 13.86458], [-88.25791, 13.91108], [-88.23018, 13.99915], [-88.07641, 13.98447], [-88.00331, 13.86948], [-87.7966, 13.91353], [-87.68821, 13.80829], [-87.73106, 13.75443], [-87.78148, 13.52906], [-87.71657, 13.50577], [-87.72115, 13.46083], [-87.73841, 13.44169], [-87.77354, 13.45767], [-87.83467, 13.44655], [-87.84675, 13.41078], [-87.80177, 13.35689], [-87.73714, 13.32715], [-87.69751, 13.25228], [-87.55124, 13.12523], [-87.37107, 12.98646], [-87.06306, 13.00892], [-87.03785, 12.98682], [-86.93197, 13.05313], [-86.93383, 13.18677], [-86.87066, 13.30641], [-86.71267, 13.30348], [-86.76812, 13.79605], [-86.35219, 13.77157], [-86.14801, 14.04317], [-86.00685, 14.08474], [-86.03458, 13.99181], [-85.75477, 13.8499], [-85.73964, 13.9698], [-85.45762, 14.11304], [-85.32149, 14.2562], [-85.18602, 14.24929], [-85.1575, 14.53934], [-84.90082, 14.80489], [-84.82596, 14.82212], [-84.70119, 14.68078], [-84.48373, 14.63249], [-84.10584, 14.76353], [-83.89551, 14.76697], [-83.62101, 14.89448], [-83.49268, 15.01158], [-83.13724, 15.00002], [-83.04763, 15.03256], [-82.06974, 14.49418], [-81.58685, 18.0025], [-83.86109, 17.73736]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "HR", + iso1A3: "HRV", + iso1N3: "191", + wikidata: "Q224", + nameEn: "Croatia", + groups: ["EU", "039", "150", "UN"], + callingCodes: ["385"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[17.6444, 42.88641], [17.5392, 42.92787], [17.70879, 42.97223], [17.64268, 43.08595], [17.46986, 43.16559], [17.286, 43.33065], [17.25579, 43.40353], [17.29699, 43.44542], [17.24411, 43.49376], [17.15828, 43.49376], [17.00585, 43.58037], [16.80736, 43.76011], [16.75316, 43.77157], [16.70922, 43.84887], [16.55472, 43.95326], [16.50528, 44.0244], [16.43629, 44.02826], [16.43662, 44.07523], [16.36864, 44.08263], [16.18688, 44.27012], [16.21346, 44.35231], [16.12969, 44.38275], [16.16814, 44.40679], [16.10566, 44.52586], [16.03012, 44.55572], [16.00884, 44.58605], [16.05828, 44.61538], [15.89348, 44.74964], [15.8255, 44.71501], [15.72584, 44.82334], [15.79472, 44.8455], [15.76096, 44.87045], [15.74723, 44.96818], [15.78568, 44.97401], [15.74585, 45.0638], [15.78842, 45.11519], [15.76371, 45.16508], [15.83512, 45.22459], [15.98412, 45.23088], [16.12153, 45.09616], [16.29036, 44.99732], [16.35404, 45.00241], [16.35863, 45.03529], [16.3749, 45.05206], [16.38219, 45.05139], [16.38309, 45.05955], [16.40023, 45.1147], [16.4634, 45.14522], [16.49155, 45.21153], [16.52982, 45.22713], [16.5501, 45.2212], [16.56559, 45.22307], [16.60194, 45.23042], [16.64962, 45.20714], [16.74845, 45.20393], [16.78219, 45.19002], [16.81137, 45.18434], [16.83804, 45.18951], [16.92405, 45.27607], [16.9385, 45.22742], [17.0415, 45.20759], [17.18438, 45.14764], [17.24325, 45.146], [17.25131, 45.14957], [17.26815, 45.18444], [17.32092, 45.16246], [17.33573, 45.14521], [17.41229, 45.13335], [17.4498, 45.16119], [17.45615, 45.12523], [17.47589, 45.12656], [17.51469, 45.10791], [17.59104, 45.10816], [17.66571, 45.13408], [17.84826, 45.04489], [17.87148, 45.04645], [17.93706, 45.08016], [17.97336, 45.12245], [17.97834, 45.13831], [17.99479, 45.14958], [18.01594, 45.15163], [18.03121, 45.12632], [18.1624, 45.07654], [18.24387, 45.13699], [18.32077, 45.1021], [18.41896, 45.11083], [18.47939, 45.05871], [18.65723, 45.07544], [18.78357, 44.97741], [18.80661, 44.93561], [18.76369, 44.93707], [18.76347, 44.90669], [18.8704, 44.85097], [19.01994, 44.85493], [18.98957, 44.90645], [19.02871, 44.92541], [19.06853, 44.89915], [19.15573, 44.95409], [19.05205, 44.97692], [19.1011, 45.01191], [19.07952, 45.14668], [19.14063, 45.12972], [19.19144, 45.17863], [19.43589, 45.17137], [19.41941, 45.23475], [19.28208, 45.23813], [19.10774, 45.29547], [18.97446, 45.37528], [18.99918, 45.49333], [19.08364, 45.48804], [19.07471, 45.53086], [18.94562, 45.53712], [18.88776, 45.57253], [18.96691, 45.66731], [18.90305, 45.71863], [18.85783, 45.85493], [18.81394, 45.91329], [18.80211, 45.87995], [18.6792, 45.92057], [18.57483, 45.80772], [18.44368, 45.73972], [18.12439, 45.78905], [18.08869, 45.76511], [17.99805, 45.79671], [17.87377, 45.78522], [17.66545, 45.84207], [17.56821, 45.93728], [17.35672, 45.95209], [17.14592, 46.16697], [16.8903, 46.28122], [16.8541, 46.36255], [16.7154, 46.39523], [16.6639, 46.45203], [16.59527, 46.47524], [16.52604, 46.47831], [16.5007, 46.49644], [16.44036, 46.5171], [16.38771, 46.53608], [16.37193, 46.55008], [16.29793, 46.5121], [16.26733, 46.51505], [16.26759, 46.50566], [16.23961, 46.49653], [16.25124, 46.48067], [16.27398, 46.42875], [16.27329, 46.41467], [16.30162, 46.40437], [16.30233, 46.37837], [16.18824, 46.38282], [16.14859, 46.40547], [16.05281, 46.39141], [16.05065, 46.3833], [16.07314, 46.36458], [16.07616, 46.3463], [15.97965, 46.30652], [15.79284, 46.25811], [15.78817, 46.21719], [15.75479, 46.20336], [15.75436, 46.21969], [15.67395, 46.22478], [15.6434, 46.21396], [15.64904, 46.19229], [15.59909, 46.14761], [15.6083, 46.11992], [15.62317, 46.09103], [15.72977, 46.04682], [15.71246, 46.01196], [15.70327, 46.00015], [15.70636, 45.92116], [15.67967, 45.90455], [15.68383, 45.88867], [15.68232, 45.86819], [15.70411, 45.8465], [15.66662, 45.84085], [15.64185, 45.82915], [15.57952, 45.84953], [15.52234, 45.82195], [15.47325, 45.8253], [15.47531, 45.79802], [15.40836, 45.79491], [15.25423, 45.72275], [15.30872, 45.69014], [15.34919, 45.71623], [15.4057, 45.64727], [15.38952, 45.63682], [15.34214, 45.64702], [15.34695, 45.63382], [15.31027, 45.6303], [15.27747, 45.60504], [15.29837, 45.5841], [15.30249, 45.53224], [15.38188, 45.48752], [15.33051, 45.45258], [15.27758, 45.46678], [15.16862, 45.42309], [15.05187, 45.49079], [15.02385, 45.48533], [14.92266, 45.52788], [14.90554, 45.47769], [14.81992, 45.45913], [14.80124, 45.49515], [14.71718, 45.53442], [14.68605, 45.53006], [14.69694, 45.57366], [14.59576, 45.62812], [14.60977, 45.66403], [14.57397, 45.67165], [14.53816, 45.6205], [14.5008, 45.60852], [14.49769, 45.54424], [14.36693, 45.48642], [14.32487, 45.47142], [14.27681, 45.4902], [14.26611, 45.48239], [14.24239, 45.50607], [14.22371, 45.50388], [14.20348, 45.46896], [14.07116, 45.48752], [14.00578, 45.52352], [13.96063, 45.50825], [13.99488, 45.47551], [13.97309, 45.45258], [13.90771, 45.45149], [13.88124, 45.42637], [13.81742, 45.43729], [13.7785, 45.46787], [13.67398, 45.4436], [13.62902, 45.45898], [13.56979, 45.4895], [13.45644, 45.59464], [13.05142, 45.33128], [13.12821, 44.48877], [16.15283, 42.18525], [18.45131, 42.21682], [18.54128, 42.39171], [18.52152, 42.42302], [18.43588, 42.48556], [18.44307, 42.51077], [18.43735, 42.55921], [18.36197, 42.61423], [18.24318, 42.6112], [17.88201, 42.83668], [17.80854, 42.9182], [17.7948, 42.89556], [17.68151, 42.92725], [17.6444, 42.88641]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "HT", + iso1A3: "HTI", + iso1N3: "332", + wikidata: "Q790", + nameEn: "Haiti", + aliases: ["RH"], + groups: ["029", "003", "419", "019", "UN"], + callingCodes: ["509"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-71.71885, 18.78423], [-71.72624, 18.87802], [-71.77766, 18.95007], [-71.88102, 18.95007], [-71.74088, 19.0437], [-71.71088, 19.08353], [-71.69938, 19.10916], [-71.65337, 19.11759], [-71.62642, 19.21212], [-71.73229, 19.26686], [-71.77766, 19.33823], [-71.69448, 19.37866], [-71.6802, 19.45008], [-71.71268, 19.53374], [-71.71449, 19.55364], [-71.7429, 19.58445], [-71.75865, 19.70231], [-71.77419, 19.73128], [-72.38946, 20.27111], [-73.37289, 20.43199], [-74.7289, 18.71009], [-74.76465, 18.06252], [-72.29523, 17.48026], [-71.75671, 18.03456], [-71.73783, 18.07177], [-71.74994, 18.11115], [-71.75465, 18.14405], [-71.78271, 18.18302], [-71.69952, 18.34101], [-71.90875, 18.45821], [-71.88102, 18.50125], [-72.00201, 18.62312], [-71.95412, 18.64939], [-71.82556, 18.62551], [-71.71885, 18.78423]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "HU", + iso1A3: "HUN", + iso1N3: "348", + wikidata: "Q28", + nameEn: "Hungary", + groups: ["EU", "151", "150", "UN"], + callingCodes: ["36"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[21.72525, 48.34628], [21.67134, 48.3989], [21.6068, 48.50365], [21.44063, 48.58456], [21.11516, 48.49546], [20.83248, 48.5824], [20.5215, 48.53336], [20.29943, 48.26104], [20.24312, 48.2784], [19.92452, 48.1283], [19.63338, 48.25006], [19.52489, 48.19791], [19.47957, 48.09437], [19.28182, 48.08336], [19.23924, 48.0595], [19.01952, 48.07052], [18.82176, 48.04206], [18.76134, 47.97499], [18.76821, 47.87469], [18.8506, 47.82308], [18.74074, 47.8157], [18.66521, 47.76772], [18.56496, 47.76588], [18.29305, 47.73541], [18.02938, 47.75665], [17.71215, 47.7548], [17.23699, 48.02094], [17.16001, 48.00636], [17.09786, 47.97336], [17.11022, 47.92461], [17.08275, 47.87719], [17.00997, 47.86245], [17.07039, 47.81129], [17.05048, 47.79377], [17.08893, 47.70928], [16.87538, 47.68895], [16.86509, 47.72268], [16.82938, 47.68432], [16.7511, 47.67878], [16.72089, 47.73469], [16.65679, 47.74197], [16.61183, 47.76171], [16.54779, 47.75074], [16.53514, 47.73837], [16.55129, 47.72268], [16.4222, 47.66537], [16.58699, 47.61772], [16.64193, 47.63114], [16.71059, 47.52692], [16.64821, 47.50155], [16.6718, 47.46139], [16.57152, 47.40868], [16.52414, 47.41007], [16.49908, 47.39416], [16.45104, 47.41181], [16.47782, 47.25918], [16.44142, 47.25079], [16.43663, 47.21127], [16.41739, 47.20649], [16.42801, 47.18422], [16.4523, 47.18812], [16.46442, 47.16845], [16.44932, 47.14418], [16.52863, 47.13974], [16.46134, 47.09395], [16.52176, 47.05747], [16.43936, 47.03548], [16.51369, 47.00084], [16.28202, 47.00159], [16.27594, 46.9643], [16.22403, 46.939], [16.19904, 46.94134], [16.10983, 46.867], [16.14365, 46.8547], [16.15711, 46.85434], [16.21892, 46.86961], [16.2365, 46.87775], [16.2941, 46.87137], [16.34547, 46.83836], [16.3408, 46.80641], [16.31303, 46.79838], [16.30966, 46.7787], [16.37816, 46.69975], [16.42641, 46.69228], [16.41863, 46.66238], [16.38594, 46.6549], [16.39217, 46.63673], [16.50139, 46.56684], [16.52885, 46.53303], [16.52604, 46.5051], [16.59527, 46.47524], [16.6639, 46.45203], [16.7154, 46.39523], [16.8541, 46.36255], [16.8903, 46.28122], [17.14592, 46.16697], [17.35672, 45.95209], [17.56821, 45.93728], [17.66545, 45.84207], [17.87377, 45.78522], [17.99805, 45.79671], [18.08869, 45.76511], [18.12439, 45.78905], [18.44368, 45.73972], [18.57483, 45.80772], [18.6792, 45.92057], [18.80211, 45.87995], [18.81394, 45.91329], [18.99712, 45.93537], [19.01284, 45.96529], [19.0791, 45.96458], [19.10388, 46.04015], [19.14543, 45.9998], [19.28826, 45.99694], [19.52473, 46.1171], [19.56113, 46.16824], [19.66007, 46.19005], [19.81491, 46.1313], [19.93508, 46.17553], [20.01816, 46.17696], [20.03533, 46.14509], [20.09713, 46.17315], [20.26068, 46.12332], [20.28324, 46.1438], [20.35573, 46.16629], [20.45377, 46.14405], [20.49718, 46.18721], [20.63863, 46.12728], [20.76085, 46.21002], [20.74574, 46.25467], [20.86797, 46.28884], [21.06572, 46.24897], [21.16872, 46.30118], [21.28061, 46.44941], [21.26929, 46.4993], [21.33214, 46.63035], [21.43926, 46.65109], [21.5151, 46.72147], [21.48935, 46.7577], [21.52028, 46.84118], [21.59307, 46.86935], [21.59581, 46.91628], [21.68645, 46.99595], [21.648, 47.03902], [21.78395, 47.11104], [21.94463, 47.38046], [22.01055, 47.37767], [22.03389, 47.42508], [22.00917, 47.50492], [22.31816, 47.76126], [22.41979, 47.7391], [22.46559, 47.76583], [22.67247, 47.7871], [22.76617, 47.8417], [22.77991, 47.87211], [22.89849, 47.95851], [22.84276, 47.98602], [22.87847, 48.04665], [22.81804, 48.11363], [22.73427, 48.12005], [22.66835, 48.09162], [22.58733, 48.10813], [22.59007, 48.15121], [22.49806, 48.25189], [22.38133, 48.23726], [22.2083, 48.42534], [22.14689, 48.4005], [21.83339, 48.36242], [21.8279, 48.33321], [21.72525, 48.34628]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "IC", + wikidata: "Q5813", + nameEn: "Canary Islands", + country: "ES", + groups: ["Q3320166", "Q105472", "EU", "039", "150", "UN"], + isoStatus: "excRes", + callingCodes: ["34"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-12.00985, 30.24121], [-25.3475, 27.87574], [-14.43883, 27.02969], [-12.00985, 30.24121]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "ID", + iso1A3: "IDN", + iso1N3: "360", + wikidata: "Q252", + nameEn: "Indonesia", + aliases: ["RI"] + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "IE", + iso1A3: "IRL", + iso1N3: "372", + wikidata: "Q27", + nameEn: "Republic of Ireland", + groups: ["EU", "Q22890", "154", "150", "UN"], + driveSide: "left", + callingCodes: ["353"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-6.26218, 54.09785], [-6.29003, 54.11278], [-6.32694, 54.09337], [-6.36279, 54.11248], [-6.36605, 54.07234], [-6.47849, 54.06947], [-6.62842, 54.03503], [-6.66264, 54.0666], [-6.6382, 54.17071], [-6.70175, 54.20218], [-6.74575, 54.18788], [-6.81583, 54.22791], [-6.85179, 54.29176], [-6.87775, 54.34682], [-7.02034, 54.4212], [-7.19145, 54.31296], [-7.14908, 54.22732], [-7.25012, 54.20063], [-7.26316, 54.13863], [-7.29493, 54.12013], [-7.29687, 54.1354], [-7.28017, 54.16714], [-7.29157, 54.17191], [-7.34005, 54.14698], [-7.30553, 54.11869], [-7.32834, 54.11475], [-7.44567, 54.1539], [-7.4799, 54.12239], [-7.55812, 54.12239], [-7.69501, 54.20731], [-7.81397, 54.20159], [-7.8596, 54.21779], [-7.87101, 54.29299], [-8.04555, 54.36292], [-8.179, 54.46763], [-8.04538, 54.48941], [-7.99812, 54.54427], [-7.8596, 54.53671], [-7.70315, 54.62077], [-7.93293, 54.66603], [-7.83352, 54.73854], [-7.75041, 54.7103], [-7.64449, 54.75265], [-7.54671, 54.74606], [-7.54508, 54.79401], [-7.47626, 54.83084], [-7.4473, 54.87003], [-7.44404, 54.9403], [-7.40004, 54.94498], [-7.4033, 55.00391], [-7.34464, 55.04688], [-7.2471, 55.06933], [-6.34755, 55.49206], [-7.75229, 55.93854], [-22.01468, 48.19557], [-6.03913, 51.13217], [-5.37267, 53.63269], [-6.26218, 54.09785]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "IL", + iso1A3: "ISR", + iso1N3: "376", + wikidata: "Q801", + nameEn: "Israel", + groups: ["145", "142", "UN"], + callingCodes: ["972"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[34.052, 31.46619], [34.29262, 31.70393], [34.48681, 31.59711], [34.56797, 31.54197], [34.48892, 31.48365], [34.40077, 31.40926], [34.36505, 31.36404], [34.37381, 31.30598], [34.36523, 31.28963], [34.29417, 31.24194], [34.26742, 31.21998], [34.92298, 29.45305], [34.97718, 29.54294], [34.98207, 29.58147], [35.02147, 29.66343], [35.14108, 30.07374], [35.19183, 30.34636], [35.16218, 30.43535], [35.19595, 30.50297], [35.21379, 30.60401], [35.29311, 30.71365], [35.33456, 30.81224], [35.33984, 30.8802], [35.41371, 30.95565], [35.43658, 31.12444], [35.40316, 31.25535], [35.47672, 31.49578], [35.39675, 31.49572], [35.22921, 31.37445], [35.13033, 31.3551], [35.02459, 31.35979], [34.92571, 31.34337], [34.88932, 31.37093], [34.87833, 31.39321], [34.89756, 31.43891], [34.93258, 31.47816], [34.94356, 31.50743], [34.9415, 31.55601], [34.95249, 31.59813], [35.00879, 31.65426], [35.08226, 31.69107], [35.10782, 31.71594], [35.11895, 31.71454], [35.12933, 31.7325], [35.13931, 31.73012], [35.15119, 31.73634], [35.15474, 31.73352], [35.16478, 31.73242], [35.18023, 31.72067], [35.20538, 31.72388], [35.21937, 31.71578], [35.22392, 31.71899], [35.23972, 31.70896], [35.24315, 31.71244], [35.2438, 31.7201], [35.24981, 31.72543], [35.25182, 31.73945], [35.26319, 31.74846], [35.25225, 31.7678], [35.26058, 31.79064], [35.25573, 31.81362], [35.26404, 31.82567], [35.251, 31.83085], [35.25753, 31.8387], [35.24816, 31.8458], [35.2304, 31.84222], [35.2249, 31.85433], [35.22817, 31.8638], [35.22567, 31.86745], [35.22294, 31.87889], [35.22014, 31.88264], [35.2136, 31.88241], [35.21276, 31.88153], [35.21016, 31.88237], [35.20945, 31.8815], [35.20791, 31.8821], [35.20673, 31.88151], [35.20381, 31.86716], [35.21128, 31.863], [35.216, 31.83894], [35.21469, 31.81835], [35.19461, 31.82687], [35.18169, 31.82542], [35.18603, 31.80901], [35.14174, 31.81325], [35.07677, 31.85627], [35.05617, 31.85685], [35.01978, 31.82944], [34.9724, 31.83352], [34.99712, 31.85569], [35.03489, 31.85919], [35.03978, 31.89276], [35.03489, 31.92448], [35.00124, 31.93264], [34.98682, 31.96935], [35.00261, 32.027], [34.9863, 32.09551], [34.99437, 32.10962], [34.98507, 32.12606], [34.99039, 32.14626], [34.96009, 32.17503], [34.95703, 32.19522], [34.98885, 32.20758], [35.01841, 32.23981], [35.02939, 32.2671], [35.01119, 32.28684], [35.01772, 32.33863], [35.04243, 32.35008], [35.05142, 32.3667], [35.0421, 32.38242], [35.05311, 32.4024], [35.05423, 32.41754], [35.07059, 32.4585], [35.08564, 32.46948], [35.09236, 32.47614], [35.10024, 32.47856], [35.10882, 32.4757], [35.15937, 32.50466], [35.2244, 32.55289], [35.25049, 32.52453], [35.29306, 32.50947], [35.30685, 32.51024], [35.35212, 32.52047], [35.40224, 32.50136], [35.42034, 32.46009], [35.41598, 32.45593], [35.41048, 32.43706], [35.42078, 32.41562], [35.55807, 32.38674], [35.55494, 32.42687], [35.57485, 32.48669], [35.56614, 32.64393], [35.59813, 32.65159], [35.61669, 32.67999], [35.66527, 32.681], [35.68467, 32.70715], [35.75983, 32.74803], [35.78745, 32.77938], [35.83758, 32.82817], [35.84021, 32.8725], [35.87012, 32.91976], [35.89298, 32.9456], [35.87188, 32.98028], [35.84802, 33.1031], [35.81911, 33.11077], [35.81911, 33.1336], [35.84285, 33.16673], [35.83846, 33.19397], [35.81647, 33.2028], [35.81295, 33.24841], [35.77513, 33.27342], [35.813, 33.3172], [35.77477, 33.33609], [35.62019, 33.27278], [35.62283, 33.24226], [35.58502, 33.26653], [35.58326, 33.28381], [35.56523, 33.28969], [35.55555, 33.25844], [35.54544, 33.25513], [35.54808, 33.236], [35.5362, 33.23196], [35.54228, 33.19865], [35.52573, 33.11921], [35.50335, 33.114], [35.50272, 33.09056], [35.448, 33.09264], [35.43059, 33.06659], [35.35223, 33.05617], [35.31429, 33.10515], [35.1924, 33.08743], [35.10645, 33.09318], [34.78515, 33.20368], [33.62659, 31.82938], [34.052, 31.46619]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "IM", + iso1A3: "IMN", + iso1N3: "833", + wikidata: "Q9676", + nameEn: "Isle of Man", + country: "GB", + groups: ["Q185086", "154", "150", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["44 01624", "44 07624", "44 07524", "44 07924"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-3.98763, 54.07351], [-4.1819, 54.57861], [-5.6384, 53.81157], [-3.98763, 54.07351]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "IN", + iso1A3: "IND", + iso1N3: "356", + wikidata: "Q668", + nameEn: "India" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "IO", + iso1A3: "IOT", + iso1N3: "086", + wikidata: "Q43448", + nameEn: "British Indian Ocean Territory", + country: "GB" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "IQ", + iso1A3: "IRQ", + iso1N3: "368", + wikidata: "Q796", + nameEn: "Iraq", + groups: ["145", "142", "UN"], + callingCodes: ["964"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[42.78887, 37.38615], [42.56725, 37.14878], [42.35724, 37.10998], [42.36697, 37.0627], [41.81736, 36.58782], [41.40058, 36.52502], [41.28864, 36.35368], [41.2564, 36.06012], [41.37027, 35.84095], [41.38184, 35.62502], [41.26569, 35.42708], [41.21654, 35.1508], [41.2345, 34.80049], [41.12388, 34.65742], [40.97676, 34.39788], [40.64314, 34.31604], [38.79171, 33.37328], [39.08202, 32.50304], [38.98762, 32.47694], [39.04251, 32.30203], [39.26157, 32.35555], [39.29903, 32.23259], [40.01521, 32.05667], [42.97601, 30.72204], [42.97796, 30.48295], [44.72255, 29.19736], [46.42415, 29.05947], [46.5527, 29.10283], [46.89695, 29.50584], [47.15166, 30.01044], [47.37192, 30.10421], [47.7095, 30.10453], [48.01114, 29.98906], [48.06782, 30.02906], [48.17332, 30.02448], [48.40479, 29.85763], [48.59531, 29.66815], [48.83867, 29.78572], [48.61441, 29.93675], [48.51011, 29.96238], [48.44785, 30.00148], [48.4494, 30.04456], [48.43384, 30.08233], [48.38869, 30.11062], [48.38714, 30.13485], [48.41671, 30.17254], [48.41117, 30.19846], [48.26393, 30.3408], [48.24385, 30.33846], [48.21279, 30.31644], [48.19425, 30.32796], [48.18321, 30.39703], [48.14585, 30.44133], [48.02443, 30.4789], [48.03221, 30.9967], [47.68219, 31.00004], [47.6804, 31.39086], [47.86337, 31.78422], [47.64771, 32.07666], [47.52474, 32.15972], [47.57144, 32.20583], [47.37529, 32.47808], [47.17218, 32.45393], [46.46788, 32.91992], [46.32298, 32.9731], [46.17198, 32.95612], [46.09103, 32.98354], [46.15175, 33.07229], [46.03966, 33.09577], [46.05367, 33.13097], [46.11905, 33.11924], [46.20623, 33.20395], [45.99919, 33.5082], [45.86687, 33.49263], [45.96183, 33.55751], [45.89801, 33.63661], [45.77814, 33.60938], [45.50261, 33.94968], [45.42789, 33.9458], [45.41077, 33.97421], [45.47264, 34.03099], [45.56176, 34.15088], [45.58667, 34.30147], [45.53552, 34.35148], [45.49171, 34.3439], [45.46697, 34.38221], [45.43879, 34.45949], [45.51883, 34.47692], [45.53219, 34.60441], [45.59074, 34.55558], [45.60224, 34.55057], [45.73923, 34.54416], [45.70031, 34.69277], [45.65672, 34.7222], [45.68284, 34.76624], [45.70031, 34.82322], [45.73641, 34.83975], [45.79682, 34.85133], [45.78904, 34.91135], [45.86532, 34.89858], [45.89477, 34.95805], [45.87864, 35.03441], [45.92173, 35.0465], [45.92203, 35.09538], [45.93108, 35.08148], [45.94756, 35.09188], [46.06508, 35.03699], [46.07747, 35.0838], [46.11763, 35.07551], [46.19116, 35.11097], [46.15642, 35.1268], [46.16229, 35.16984], [46.19738, 35.18536], [46.18457, 35.22561], [46.11367, 35.23729], [46.15474, 35.2883], [46.13152, 35.32548], [46.05358, 35.38568], [45.98453, 35.49848], [46.01518, 35.52012], [45.97584, 35.58132], [46.03028, 35.57416], [46.01307, 35.59756], [46.0165, 35.61501], [45.99452, 35.63574], [46.0117, 35.65059], [46.01631, 35.69139], [46.23736, 35.71414], [46.34166, 35.78363], [46.32921, 35.82655], [46.17198, 35.8013], [46.08325, 35.8581], [45.94711, 35.82218], [45.89784, 35.83708], [45.81442, 35.82107], [45.76145, 35.79898], [45.6645, 35.92872], [45.60018, 35.96069], [45.55245, 35.99943], [45.46594, 36.00042], [45.38275, 35.97156], [45.33916, 35.99424], [45.37652, 36.06222], [45.37312, 36.09917], [45.32235, 36.17383], [45.30038, 36.27769], [45.26261, 36.3001], [45.27394, 36.35846], [45.23953, 36.43257], [45.11811, 36.40751], [45.00759, 36.5402], [45.06985, 36.62645], [45.06985, 36.6814], [45.01537, 36.75128], [44.84725, 36.77622], [44.83479, 36.81362], [44.90173, 36.86096], [44.91199, 36.91468], [44.89862, 37.01897], [44.81611, 37.04383], [44.75229, 37.11958], [44.78319, 37.1431], [44.76698, 37.16162], [44.63179, 37.19229], [44.42631, 37.05825], [44.38117, 37.05825], [44.35315, 37.04955], [44.35937, 37.02843], [44.30645, 36.97373], [44.25975, 36.98119], [44.18503, 37.09551], [44.22239, 37.15756], [44.27998, 37.16501], [44.2613, 37.25055], [44.13521, 37.32486], [44.02002, 37.33229], [43.90949, 37.22453], [43.84878, 37.22205], [43.82699, 37.19477], [43.8052, 37.22825], [43.7009, 37.23692], [43.63085, 37.21957], [43.56702, 37.25675], [43.50787, 37.24436], [43.33508, 37.33105], [43.30083, 37.30629], [43.11403, 37.37436], [42.93705, 37.32015], [42.78887, 37.38615]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "IR", + iso1A3: "IRN", + iso1N3: "364", + wikidata: "Q794", + nameEn: "Iran", + groups: ["034", "142", "UN"], + callingCodes: ["98"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[44.96746, 39.42998], [44.88916, 39.59653], [44.81043, 39.62677], [44.71806, 39.71124], [44.65422, 39.72163], [44.6137, 39.78393], [44.47298, 39.68788], [44.48111, 39.61579], [44.41849, 39.56659], [44.42832, 39.4131], [44.37921, 39.4131], [44.29818, 39.378], [44.22452, 39.4169], [44.03667, 39.39223], [44.1043, 39.19842], [44.20946, 39.13975], [44.18863, 38.93881], [44.30322, 38.81581], [44.26155, 38.71427], [44.28065, 38.6465], [44.32058, 38.62752], [44.3207, 38.49799], [44.3119, 38.37887], [44.38309, 38.36117], [44.44386, 38.38295], [44.50115, 38.33939], [44.42476, 38.25763], [44.22509, 37.88859], [44.3883, 37.85433], [44.45948, 37.77065], [44.55498, 37.783], [44.62096, 37.71985], [44.56887, 37.6429], [44.61401, 37.60165], [44.58449, 37.45018], [44.81021, 37.2915], [44.75986, 37.21549], [44.7868, 37.16644], [44.78319, 37.1431], [44.75229, 37.11958], [44.81611, 37.04383], [44.89862, 37.01897], [44.91199, 36.91468], [44.90173, 36.86096], [44.83479, 36.81362], [44.84725, 36.77622], [45.01537, 36.75128], [45.06985, 36.6814], [45.06985, 36.62645], [45.00759, 36.5402], [45.11811, 36.40751], [45.23953, 36.43257], [45.27394, 36.35846], [45.26261, 36.3001], [45.30038, 36.27769], [45.32235, 36.17383], [45.37312, 36.09917], [45.37652, 36.06222], [45.33916, 35.99424], [45.38275, 35.97156], [45.46594, 36.00042], [45.55245, 35.99943], [45.60018, 35.96069], [45.6645, 35.92872], [45.76145, 35.79898], [45.81442, 35.82107], [45.89784, 35.83708], [45.94711, 35.82218], [46.08325, 35.8581], [46.17198, 35.8013], [46.32921, 35.82655], [46.34166, 35.78363], [46.23736, 35.71414], [46.01631, 35.69139], [46.0117, 35.65059], [45.99452, 35.63574], [46.0165, 35.61501], [46.01307, 35.59756], [46.03028, 35.57416], [45.97584, 35.58132], [46.01518, 35.52012], [45.98453, 35.49848], [46.05358, 35.38568], [46.13152, 35.32548], [46.15474, 35.2883], [46.11367, 35.23729], [46.18457, 35.22561], [46.19738, 35.18536], [46.16229, 35.16984], [46.15642, 35.1268], [46.19116, 35.11097], [46.11763, 35.07551], [46.07747, 35.0838], [46.06508, 35.03699], [45.94756, 35.09188], [45.93108, 35.08148], [45.92203, 35.09538], [45.92173, 35.0465], [45.87864, 35.03441], [45.89477, 34.95805], [45.86532, 34.89858], [45.78904, 34.91135], [45.79682, 34.85133], [45.73641, 34.83975], [45.70031, 34.82322], [45.68284, 34.76624], [45.65672, 34.7222], [45.70031, 34.69277], [45.73923, 34.54416], [45.60224, 34.55057], [45.59074, 34.55558], [45.53219, 34.60441], [45.51883, 34.47692], [45.43879, 34.45949], [45.46697, 34.38221], [45.49171, 34.3439], [45.53552, 34.35148], [45.58667, 34.30147], [45.56176, 34.15088], [45.47264, 34.03099], [45.41077, 33.97421], [45.42789, 33.9458], [45.50261, 33.94968], [45.77814, 33.60938], [45.89801, 33.63661], [45.96183, 33.55751], [45.86687, 33.49263], [45.99919, 33.5082], [46.20623, 33.20395], [46.11905, 33.11924], [46.05367, 33.13097], [46.03966, 33.09577], [46.15175, 33.07229], [46.09103, 32.98354], [46.17198, 32.95612], [46.32298, 32.9731], [46.46788, 32.91992], [47.17218, 32.45393], [47.37529, 32.47808], [47.57144, 32.20583], [47.52474, 32.15972], [47.64771, 32.07666], [47.86337, 31.78422], [47.6804, 31.39086], [47.68219, 31.00004], [48.03221, 30.9967], [48.02443, 30.4789], [48.14585, 30.44133], [48.18321, 30.39703], [48.19425, 30.32796], [48.21279, 30.31644], [48.24385, 30.33846], [48.26393, 30.3408], [48.41117, 30.19846], [48.41671, 30.17254], [48.38714, 30.13485], [48.38869, 30.11062], [48.43384, 30.08233], [48.4494, 30.04456], [48.44785, 30.00148], [48.51011, 29.96238], [48.61441, 29.93675], [48.83867, 29.78572], [49.98877, 27.87827], [50.37726, 27.89227], [54.39838, 25.68383], [55.14145, 25.62624], [55.81777, 26.18798], [56.2644, 26.58649], [56.68954, 26.76645], [56.79239, 26.41236], [56.82555, 25.7713], [56.86325, 25.03856], [61.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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "IS", + iso1A3: "ISL", + iso1N3: "352", + wikidata: "Q189", + nameEn: "Iceland", + groups: ["154", "150", "UN"], + callingCodes: ["354"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-33.15676, 62.62995], [-8.25539, 63.0423], [-15.70914, 69.67442], [-33.15676, 62.62995]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "IT", + iso1A3: "ITA", + iso1N3: "380", + wikidata: "Q38", + nameEn: "Italy", + groups: ["EU", "039", "150", "UN"], + callingCodes: ["39"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[8.95861, 45.96485], [8.97604, 45.96151], [8.97741, 45.98317], [8.96668, 45.98436], [8.95861, 45.96485]]], [[[7.63035, 43.57419], [9.56115, 43.20816], [10.09675, 41.44089], [7.60802, 41.05927], [7.89009, 38.19924], [11.2718, 37.6713], [12.13667, 34.20326], [14.02721, 36.53141], [17.67657, 35.68918], [18.83516, 40.36999], [16.15283, 42.18525], [13.12821, 44.48877], [13.05142, 45.33128], [13.45644, 45.59464], [13.6076, 45.64761], [13.7198, 45.59352], [13.74587, 45.59811], [13.78445, 45.5825], [13.84106, 45.58185], [13.86771, 45.59898], [13.8695, 45.60835], [13.9191, 45.6322], [13.87933, 45.65207], [13.83422, 45.68703], [13.83332, 45.70855], [13.8235, 45.7176], [13.66986, 45.79955], [13.59784, 45.8072], [13.58858, 45.83503], [13.57563, 45.8425], [13.58644, 45.88173], [13.59565, 45.89446], [13.60857, 45.89907], [13.61931, 45.91782], [13.63815, 45.93607], [13.6329, 45.94894], [13.64307, 45.98326], [13.63458, 45.98947], [13.62074, 45.98388], [13.58903, 45.99009], [13.56759, 45.96991], [13.52963, 45.96588], [13.50104, 45.98078], [13.47474, 46.00546], [13.49702, 46.01832], [13.50998, 46.04498], [13.49568, 46.04839], [13.50104, 46.05986], [13.57072, 46.09022], [13.64053, 46.13587], [13.66472, 46.17392], [13.64451, 46.18966], [13.56682, 46.18703], [13.56114, 46.2054], [13.47587, 46.22725], [13.42218, 46.20758], [13.37671, 46.29668], [13.44808, 46.33507], [13.43418, 46.35992], [13.47019, 46.3621], [13.5763, 46.40915], [13.5763, 46.42613], [13.59777, 46.44137], [13.68684, 46.43881], [13.7148, 46.5222], [13.64088, 46.53438], [13.27627, 46.56059], [12.94445, 46.60401], [12.59992, 46.6595], [12.38708, 46.71529], [12.27591, 46.88651], [12.2006, 46.88854], [12.11675, 47.01241], [12.21781, 47.03996], [12.19254, 47.09331], [11.74789, 46.98484], [11.50739, 47.00644], [11.33355, 46.99862], [11.10618, 46.92966], [11.00764, 46.76896], [10.72974, 46.78972], [10.75753, 46.82258], [10.66405, 46.87614], [10.54783, 46.84505], [10.47197, 46.85698], [10.38659, 46.67847], [10.40475, 46.63671], [10.44686, 46.64162], [10.49375, 46.62049], [10.46136, 46.53164], [10.25309, 46.57432], [10.23674, 46.63484], [10.10307, 46.61003], [10.03715, 46.44479], [10.165, 46.41051], [10.10506, 46.3372], [10.17862, 46.25626], [10.14439, 46.22992], [10.07055, 46.21668], [9.95249, 46.38045], [9.73086, 46.35071], [9.71273, 46.29266], [9.57015, 46.2958], [9.46117, 46.37481], [9.45936, 46.50873], [9.40487, 46.46621], [9.36128, 46.5081], [9.28136, 46.49685], [9.25502, 46.43743], [9.29226, 46.32717], [9.24503, 46.23616], [9.01618, 46.04928], [8.99257, 45.9698], [9.09065, 45.89906], [9.06642, 45.8761], [9.04546, 45.84968], [9.04059, 45.8464], [9.03505, 45.83976], [9.03793, 45.83548], [9.03279, 45.82865], [9.0298, 45.82127], [9.00324, 45.82055], [8.99663, 45.83466], [8.9621, 45.83707], [8.94737, 45.84285], [8.91129, 45.8388], [8.93504, 45.86245], [8.94372, 45.86587], [8.93649, 45.86775], [8.88904, 45.95465], [8.86688, 45.96135], [8.85121, 45.97239], [8.8319, 45.9879], [8.79362, 45.99207], [8.78585, 45.98973], [8.79414, 46.00913], [8.85617, 46.0748], [8.80778, 46.10085], [8.75697, 46.10395], [8.62242, 46.12112], [8.45032, 46.26869], [8.46317, 46.43712], [8.42464, 46.46367], [8.30648, 46.41587], [8.31162, 46.38044], [8.08814, 46.26692], [8.16866, 46.17817], [8.11383, 46.11577], [8.02906, 46.10331], [7.98881, 45.99867], [7.9049, 45.99945], [7.85949, 45.91485], [7.56343, 45.97421], [7.10685, 45.85653], [7.04151, 45.92435], [6.95315, 45.85163], [6.80785, 45.83265], [6.80785, 45.71864], [6.98948, 45.63869], [7.00037, 45.509], [7.18019, 45.40071], [7.10572, 45.32924], [7.13115, 45.25386], [7.07074, 45.21228], [6.96706, 45.20841], [6.85144, 45.13226], [6.7697, 45.16044], [6.62803, 45.11175], [6.66981, 45.02324], [6.74791, 45.01939], [6.74519, 44.93661], [6.75518, 44.89915], [6.90774, 44.84322], [6.93499, 44.8664], [7.02217, 44.82519], [7.00401, 44.78782], [7.07484, 44.68073], [7.00582, 44.69364], [6.95133, 44.66264], [6.96042, 44.62129], [6.85507, 44.53072], [6.86233, 44.49834], [6.94504, 44.43112], [6.88784, 44.42043], [6.89171, 44.36637], [6.98221, 44.28289], [7.00764, 44.23736], [7.16929, 44.20352], [7.27827, 44.1462], [7.34547, 44.14359], [7.36364, 44.11882], [7.62155, 44.14881], [7.63245, 44.17877], [7.68694, 44.17487], [7.66878, 44.12795], [7.72508, 44.07578], [7.6597, 44.03009], [7.66848, 43.99943], [7.65266, 43.9763], [7.60771, 43.95772], [7.56858, 43.94506], [7.56075, 43.89932], [7.51162, 43.88301], [7.49355, 43.86551], [7.50423, 43.84345], [7.53006, 43.78405], [7.63035, 43.57419]], [[12.45181, 41.90056], [12.44834, 41.90095], [12.44582, 41.90194], [12.44815, 41.90326], [12.44984, 41.90545], [12.45091, 41.90625], [12.45543, 41.90738], [12.45561, 41.90629], [12.45762, 41.9058], [12.45755, 41.9033], [12.45826, 41.90281], [12.45834, 41.90174], [12.4577, 41.90115], [12.45691, 41.90125], [12.45626, 41.90172], [12.45435, 41.90143], [12.45446, 41.90028], [12.45181, 41.90056]], [[12.45648, 43.89369], [12.44184, 43.90498], [12.41641, 43.89991], [12.40935, 43.9024], [12.41233, 43.90956], [12.40733, 43.92379], [12.41551, 43.92984], [12.41165, 43.93769], [12.40506, 43.94325], [12.40415, 43.95485], [12.41414, 43.95273], [12.42005, 43.9578], [12.43662, 43.95698], [12.44684, 43.96597], [12.46205, 43.97463], [12.47853, 43.98052], [12.49406, 43.98492], [12.50678, 43.99113], [12.51463, 43.99122], [12.5154, 43.98508], [12.51064, 43.98165], [12.51109, 43.97201], [12.50622, 43.97131], [12.50875, 43.96198], [12.50655, 43.95796], [12.51427, 43.94897], [12.51553, 43.94096], [12.50496, 43.93017], [12.50269, 43.92363], [12.49724, 43.92248], [12.49247, 43.91774], [12.49429, 43.90973], [12.48771, 43.89706], [12.45648, 43.89369]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "JE", + iso1A3: "JEY", + iso1N3: "832", + wikidata: "Q785", + nameEn: "Bailiwick of Jersey", + country: "GB", + groups: ["830", "Q185086", "154", "150", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["44 01534"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-2.00491, 48.86706], [-1.83944, 49.23037], [-2.09454, 49.46288], [-2.65349, 49.15373], [-2.00491, 48.86706]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "JM", + iso1A3: "JAM", + iso1N3: "388", + wikidata: "Q766", + nameEn: "Jamaica", + aliases: ["JA"], + groups: ["029", "003", "419", "019", "UN"], + driveSide: "left", + callingCodes: ["1 876", "1 658"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-74.09729, 17.36817], [-78.9741, 19.59515], [-78.34606, 16.57862], [-74.09729, 17.36817]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "JO", + iso1A3: "JOR", + iso1N3: "400", + wikidata: "Q810", + nameEn: "Jordan", + groups: ["145", "142", "UN"], + callingCodes: ["962"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[39.04251, 32.30203], [38.98762, 32.47694], [39.08202, 32.50304], [38.79171, 33.37328], [36.83946, 32.31293], [36.40959, 32.37908], [36.23948, 32.50108], [36.20875, 32.49529], [36.20379, 32.52751], [36.08074, 32.51463], [36.02239, 32.65911], [35.96633, 32.66237], [35.93307, 32.71966], [35.88405, 32.71321], [35.75983, 32.74803], [35.68467, 32.70715], [35.66527, 32.681], [35.61669, 32.67999], [35.59813, 32.65159], [35.56614, 32.64393], [35.57485, 32.48669], [35.55494, 32.42687], [35.55807, 32.38674], [35.57111, 32.21877], [35.52012, 32.04076], [35.54375, 31.96587], [35.52758, 31.9131], [35.55941, 31.76535], [35.47672, 31.49578], [35.40316, 31.25535], [35.43658, 31.12444], [35.41371, 30.95565], [35.33984, 30.8802], [35.33456, 30.81224], [35.29311, 30.71365], [35.21379, 30.60401], [35.19595, 30.50297], [35.16218, 30.43535], [35.19183, 30.34636], [35.14108, 30.07374], [35.02147, 29.66343], [34.98207, 29.58147], [34.97718, 29.54294], [34.92298, 29.45305], [34.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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "JP", + iso1A3: "JPN", + iso1N3: "392", + wikidata: "Q17", + nameEn: "Japan", + groups: ["030", "142", "UN"], + driveSide: "left", + callingCodes: ["81"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[145.82361, 43.38904], [145.23667, 43.76813], [145.82343, 44.571], [140.9182, 45.92937], [133.61399, 37.41], [129.2669, 34.87122], [122.26612, 25.98197], [123.92912, 17.8782], [155.16731, 23.60141], [145.82361, 43.38904]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "KE", + iso1A3: "KEN", + iso1N3: "404", + wikidata: "Q114", + nameEn: "Kenya", + groups: ["014", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["254"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[35.9419, 4.61933], [35.51424, 4.61643], [35.42366, 4.76969], [35.47843, 4.91872], [35.30992, 4.90402], [35.34151, 5.02364], [34.47601, 4.72162], [33.9873, 4.23316], [34.06046, 4.15235], [34.15429, 3.80464], [34.45815, 3.67385], [34.44922, 3.51627], [34.39112, 3.48802], [34.41794, 3.44342], [34.40006, 3.37949], [34.45815, 3.18319], [34.56242, 3.11478], [34.60114, 2.93034], [34.65774, 2.8753], [34.73967, 2.85447], [34.78137, 2.76223], [34.77244, 2.70272], [34.95267, 2.47209], [34.90947, 2.42447], [34.98692, 1.97348], [34.9899, 1.6668], [34.92734, 1.56109], [34.87819, 1.5596], [34.7918, 1.36752], [34.82606, 1.30944], [34.82606, 1.26626], [34.80223, 1.22754], [34.67562, 1.21265], [34.58029, 1.14712], [34.57427, 1.09868], [34.52369, 1.10692], [34.43349, 0.85254], [34.40041, 0.80266], [34.31516, 0.75693], [34.27345, 0.63182], [34.20196, 0.62289], [34.13493, 0.58118], [34.11408, 0.48884], [34.08727, 0.44713], [34.10067, 0.36372], [33.90936, 0.10581], [33.98449, -0.13079], [33.9264, -0.54188], [33.93107, -0.99298], [34.02286, -1.00779], [34.03084, -1.05101], [34.0824, -1.02264], [37.67199, -3.06222], [37.71745, -3.304], [37.5903, -3.42735], [37.63099, -3.50723], [37.75036, -3.54243], [37.81321, -3.69179], [39.21631, -4.67835], [39.44306, -4.93877], [39.62121, -4.68136], [41.75542, -1.85308], [41.56362, -1.66375], [41.56, -1.59812], [41.00099, -0.83068], [40.98767, 2.82959], [41.31368, 3.14314], [41.89488, 3.97375], [41.1754, 3.94079], [40.77498, 4.27683], [39.86043, 3.86974], [39.76808, 3.67058], [39.58339, 3.47434], [39.55132, 3.39634], [39.51551, 3.40895], [39.49444, 3.45521], [39.19954, 3.47834], [39.07736, 3.5267], [38.91938, 3.51198], [38.52336, 3.62551], [38.45812, 3.60445], [38.14168, 3.62487], [37.07724, 4.33503], [36.84474, 4.44518], [36.03924, 4.44406], [35.95449, 4.53244], [35.9419, 4.61933]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "KG", + iso1A3: "KGZ", + iso1N3: "417", + wikidata: "Q813", + nameEn: "Kyrgyzstan", + groups: ["143", "142", "UN"], + callingCodes: ["996"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[74.88756, 42.98612], [74.75, 42.99029], [74.70331, 43.02519], [74.64615, 43.05881], [74.57491, 43.13702], [74.22489, 43.24657], [73.55634, 43.03071], [73.50992, 42.82356], [73.44393, 42.43098], [71.88792, 42.83578], [71.62405, 42.76613], [71.53272, 42.8014], [71.2724, 42.77853], [71.22785, 42.69248], [71.17807, 42.67381], [71.15232, 42.60486], [70.97717, 42.50147], [70.85973, 42.30188], [70.94483, 42.26238], [71.13263, 42.28356], [71.28719, 42.18033], [70.69777, 41.92554], [70.17682, 41.5455], [70.48909, 41.40335], [70.67586, 41.47953], [70.78572, 41.36419], [70.77885, 41.24813], [70.86263, 41.23833], [70.9615, 41.16393], [71.02193, 41.19494], [71.11806, 41.15359], [71.25813, 41.18796], [71.27187, 41.11015], [71.34877, 41.16807], [71.40198, 41.09436], [71.46148, 41.13958], [71.43814, 41.19644], [71.46688, 41.31883], [71.57227, 41.29175], [71.6787, 41.42111], [71.65914, 41.49599], [71.73054, 41.54713], [71.71132, 41.43012], [71.76625, 41.4466], [71.83914, 41.3546], [71.91457, 41.2982], [71.85964, 41.19081], [72.07249, 41.11739], [72.10745, 41.15483], [72.16433, 41.16483], [72.17594, 41.15522], [72.14864, 41.13363], [72.1792, 41.10621], [72.21061, 41.05607], [72.17594, 41.02377], [72.18339, 40.99571], [72.324, 41.03381], [72.34026, 41.04539], [72.34757, 41.06104], [72.36138, 41.04384], [72.38511, 41.02785], [72.45206, 41.03018], [72.48742, 40.97136], [72.55109, 40.96046], [72.59136, 40.86947], [72.68157, 40.84942], [72.84291, 40.85512], [72.94454, 40.8094], [73.01869, 40.84681], [73.13267, 40.83512], [73.13412, 40.79122], [73.0612, 40.76678], [72.99133, 40.76457], [72.93296, 40.73089], [72.8722, 40.71111], [72.85372, 40.7116], [72.84754, 40.67229], [72.80137, 40.67856], [72.74866, 40.60873], [72.74894, 40.59592], [72.75982, 40.57273], [72.74862, 40.57131], [72.74768, 40.58051], [72.73995, 40.58409], [72.69579, 40.59778], [72.66713, 40.59076], [72.66713, 40.5219], [72.47795, 40.5532], [72.40517, 40.61917], [72.34406, 40.60144], [72.41714, 40.55736], [72.38384, 40.51535], [72.41513, 40.50856], [72.44191, 40.48222], [72.40346, 40.4007], [72.24368, 40.46091], [72.18648, 40.49893], [71.96401, 40.31907], [72.05464, 40.27586], [71.85002, 40.25647], [71.82646, 40.21872], [71.73054, 40.14818], [71.71719, 40.17886], [71.69621, 40.18492], [71.70569, 40.20391], [71.68386, 40.26984], [71.61931, 40.26775], [71.61725, 40.20615], [71.51549, 40.22986], [71.51215, 40.26943], [71.4246, 40.28619], [71.36663, 40.31593], [71.13042, 40.34106], [71.05901, 40.28765], [70.95789, 40.28761], [70.9818, 40.22392], [70.80495, 40.16813], [70.7928, 40.12797], [70.65827, 40.0981], [70.65946, 39.9878], [70.58912, 39.95211], [70.55033, 39.96619], [70.47557, 39.93216], [70.57384, 39.99394], [70.58297, 40.00891], [70.01283, 40.23288], [69.67001, 40.10639], [69.64704, 40.12165], [69.57615, 40.10524], [69.55555, 40.12296], [69.53794, 40.11833], [69.53855, 40.0887], [69.5057, 40.03277], [69.53615, 39.93991], [69.43557, 39.92877], [69.43134, 39.98431], [69.35649, 40.01994], [69.26938, 39.8127], [69.3594, 39.52516], [69.68677, 39.59281], [69.87491, 39.53882], [70.11111, 39.58223], [70.2869, 39.53141], [70.44757, 39.60128], [70.64087, 39.58792], [70.7854, 39.38933], [71.06418, 39.41586], [71.08752, 39.50704], [71.49814, 39.61397], [71.55856, 39.57588], [71.5517, 39.45722], [71.62688, 39.44056], [71.76816, 39.45456], [71.80164, 39.40631], [71.7522, 39.32031], [71.79202, 39.27355], [71.90601, 39.27674], [72.04059, 39.36704], [72.09689, 39.26823], [72.17242, 39.2661], [72.23834, 39.17248], [72.33173, 39.33093], [72.62027, 39.39696], [72.85934, 39.35116], [73.18454, 39.35536], [73.31912, 39.38615], [73.45096, 39.46677], [73.59831, 39.46425], [73.87018, 39.47879], [73.94683, 39.60733], [73.92354, 39.69565], [73.9051, 39.75073], [73.83006, 39.76136], [73.97049, 40.04378], [74.25533, 40.13191], [74.35063, 40.09742], [74.69875, 40.34668], [74.85996, 40.32857], [74.78168, 40.44886], [74.82013, 40.52197], [75.08243, 40.43945], [75.22834, 40.45382], [75.5854, 40.66874], [75.69663, 40.28642], [75.91361, 40.2948], [75.96168, 40.38064], [76.33659, 40.3482], [76.5261, 40.46114], [76.75681, 40.95354], [76.99302, 41.0696], [77.28004, 41.0033], [77.3693, 41.0375], [77.52723, 41.00227], [77.76206, 41.01574], [77.81287, 41.14307], [78.12873, 41.23091], [78.15757, 41.38565], [78.3732, 41.39603], [79.92977, 42.04113], [80.17842, 42.03211], [80.17807, 42.21166], [79.97364, 42.42816], [79.52921, 42.44778], [79.19763, 42.804], [78.91502, 42.76839], [78.48469, 42.89649], [75.82823, 42.94848], [75.72174, 42.79672], [75.29966, 42.86183], [75.22619, 42.85528], [74.88756, 42.98612]], [[70.74189, 39.86319], [70.63105, 39.77923], [70.59667, 39.83542], [70.54998, 39.85137], [70.52631, 39.86989], [70.53651, 39.89155], [70.74189, 39.86319]], [[71.86463, 39.98598], [71.84316, 39.95582], [71.7504, 39.93701], [71.71511, 39.96348], [71.78838, 40.01404], [71.86463, 39.98598]], [[71.21139, 40.03369], [71.1427, 39.95026], [71.23067, 39.93581], [71.16101, 39.88423], [71.10531, 39.91354], [71.04979, 39.89808], [71.10501, 39.95568], [71.09063, 39.99], [71.11668, 39.99291], [71.11037, 40.01984], [71.01035, 40.05481], [71.00236, 40.18154], [71.06305, 40.1771], [71.12218, 40.03052], [71.21139, 40.03369]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "KH", + iso1A3: "KHM", + iso1N3: "116", + wikidata: "Q424", + nameEn: "Cambodia", + groups: ["035", "142", "UN"], + callingCodes: ["855"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[105.87328, 11.55953], [105.81645, 11.56876], [105.80867, 11.60536], [105.8507, 11.66635], [105.88962, 11.67854], [105.95188, 11.63738], [106.00792, 11.7197], [106.02038, 11.77457], [106.06708, 11.77761], [106.13158, 11.73283], [106.18539, 11.75171], [106.26478, 11.72122], [106.30525, 11.67549], [106.37219, 11.69836], [106.44691, 11.66787], [106.45158, 11.68616], [106.41577, 11.76999], [106.44535, 11.8279], [106.44068, 11.86294], [106.4687, 11.86751], [106.4111, 11.97413], [106.70687, 11.96956], [106.79405, 12.0807], [106.92325, 12.06548], [106.99953, 12.08983], [107.15831, 12.27547], [107.34511, 12.33327], [107.42917, 12.24657], [107.4463, 12.29373], [107.55059, 12.36824], [107.5755, 12.52177], [107.55993, 12.7982], [107.49611, 12.88926], [107.49144, 13.01215], [107.62843, 13.3668], [107.61909, 13.52577], [107.53503, 13.73908], [107.45252, 13.78897], [107.46498, 13.91593], [107.44318, 13.99751], [107.38247, 13.99147], [107.35757, 14.02319], [107.37158, 14.07906], [107.33577, 14.11832], [107.40427, 14.24509], [107.39493, 14.32655], [107.44941, 14.41552], [107.48521, 14.40346], [107.52569, 14.54665], [107.52102, 14.59034], [107.55371, 14.628], [107.54361, 14.69092], [107.47238, 14.61523], [107.44435, 14.52785], [107.37897, 14.54443], [107.3276, 14.58812], [107.29803, 14.58963], [107.26534, 14.54292], [107.256, 14.48716], [107.21241, 14.48716], [107.17038, 14.41782], [107.09722, 14.3937], [107.03962, 14.45099], [107.04585, 14.41782], [106.98825, 14.36806], [106.9649, 14.3198], [106.90574, 14.33639], [106.8497, 14.29416], [106.80767, 14.31226], [106.73762, 14.42687], [106.63333, 14.44194], [106.59908, 14.50977], [106.57106, 14.50525], [106.54148, 14.59565], [106.50723, 14.58963], [106.45898, 14.55045], [106.47766, 14.50977], [106.43874, 14.52032], [106.40916, 14.45249], [106.32355, 14.44043], [106.25194, 14.48415], [106.21302, 14.36203], [106.00131, 14.36957], [105.99509, 14.32734], [106.02311, 14.30623], [106.04801, 14.20363], [106.10872, 14.18401], [106.11962, 14.11307], [106.18656, 14.06324], [106.16632, 14.01794], [106.10094, 13.98471], [106.10405, 13.9137], [105.90791, 13.92881], [105.78182, 14.02247], [105.78338, 14.08438], [105.5561, 14.15684], [105.44869, 14.10703], [105.36775, 14.09948], [105.2759, 14.17496], [105.20894, 14.34967], [105.17748, 14.34432], [105.14012, 14.23873], [105.08408, 14.20402], [105.02804, 14.23722], [104.97667, 14.38806], [104.69335, 14.42726], [104.55014, 14.36091], [104.27616, 14.39861], [103.93836, 14.3398], [103.70175, 14.38052], [103.71109, 14.4348], [103.53518, 14.42575], [103.39353, 14.35639], [103.16469, 14.33075], [102.93275, 14.19044], [102.91251, 14.01531], [102.77864, 13.93374], [102.72727, 13.77806], [102.56848, 13.69366], [102.5481, 13.6589], [102.58635, 13.6286], [102.62483, 13.60883], [102.57573, 13.60461], [102.5358, 13.56933], [102.44601, 13.5637], [102.36859, 13.57488], [102.33828, 13.55613], [102.361, 13.50551], [102.35563, 13.47307], [102.35692, 13.38274], [102.34611, 13.35618], [102.36001, 13.31142], [102.36146, 13.26006], [102.43422, 13.09061], [102.46011, 13.08057], [102.52275, 12.99813], [102.48694, 12.97537], [102.49335, 12.92711], [102.53053, 12.77506], [102.4994, 12.71736], [102.51963, 12.66117], [102.57567, 12.65358], [102.7796, 12.43781], [102.78116, 12.40284], [102.73134, 12.37091], [102.70176, 12.1686], [102.77026, 12.06815], [102.78427, 11.98746], [102.83957, 11.8519], [102.90973, 11.75613], [102.91449, 11.65512], [102.52395, 11.25257], [102.47649, 9.66162], [103.99198, 10.48391], [104.43778, 10.42386], [104.47963, 10.43046], [104.49869, 10.4057], [104.59018, 10.53073], [104.87933, 10.52833], [104.95094, 10.64003], [105.09571, 10.72722], [105.02722, 10.89236], [105.08326, 10.95656], [105.11449, 10.96332], [105.34011, 10.86179], [105.42884, 10.96878], [105.50045, 10.94586], [105.77751, 11.03671], [105.86376, 10.89839], [105.84603, 10.85873], [105.93403, 10.83853], [105.94535, 10.9168], [106.06708, 10.8098], [106.18539, 10.79451], [106.14301, 10.98176], [106.20095, 10.97795], [106.1757, 11.07301], [106.1527, 11.10476], [106.10444, 11.07879], [105.86782, 11.28343], [105.88962, 11.43605], [105.87328, 11.55953]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "KI", + iso1A3: "KIR", + iso1N3: "296", + wikidata: "Q710", + nameEn: "Kiribati", + groups: ["057", "009", "UN"], + driveSide: "left", + callingCodes: ["686"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "KM", + iso1A3: "COM", + iso1N3: "174", + wikidata: "Q970", + nameEn: "Comoros", + groups: ["014", "202", "002", "UN"], + callingCodes: ["269"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[42.63904, -10.02522], [43.28731, -13.97126], [45.4971, -11.75965], [42.63904, -10.02522]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "KN", + iso1A3: "KNA", + iso1N3: "659", + wikidata: "Q763", + nameEn: "St. Kitts and Nevis", + groups: ["029", "003", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + callingCodes: ["1 869"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-62.29333, 17.43155], [-62.76692, 17.64353], [-63.09677, 17.21372], [-62.63813, 16.65446], [-62.29333, 17.43155]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "KP", + iso1A3: "PRK", + iso1N3: "408", + wikidata: "Q423", + nameEn: "North Korea", + groups: ["030", "142", "UN"], + callingCodes: ["850"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[130.26095, 42.9027], [130.09764, 42.91425], [130.12957, 42.98361], [129.96409, 42.97306], [129.95082, 43.01051], [129.8865, 43.00395], [129.85261, 42.96494], [129.83277, 42.86746], [129.80719, 42.79218], [129.7835, 42.76521], [129.77183, 42.69435], [129.75294, 42.59409], [129.72541, 42.43739], [129.60482, 42.44461], [129.54701, 42.37254], [129.42882, 42.44702], [129.28541, 42.41574], [129.22423, 42.3553], [129.22285, 42.26491], [129.15178, 42.17224], [128.96068, 42.06657], [128.94007, 42.03537], [128.04487, 42.01769], [128.15119, 41.74568], [128.30716, 41.60322], [128.20061, 41.40895], [128.18546, 41.41279], [128.12967, 41.37931], [128.03311, 41.39232], [128.02633, 41.42103], [127.92943, 41.44291], [127.29712, 41.49473], [127.17841, 41.59714], [126.90729, 41.79955], [126.60631, 41.65565], [126.53189, 41.35206], [126.242, 41.15454], [126.00335, 40.92835], [125.76869, 40.87908], [125.71172, 40.85223], [124.86913, 40.45387], [124.40719, 40.13655], [124.38556, 40.11047], [124.3322, 40.05573], [124.37089, 40.03004], [124.35029, 39.95639], [124.23201, 39.9248], [124.17532, 39.8232], [123.90497, 38.79949], [123.85601, 37.49093], [124.67666, 38.05679], [124.84224, 37.977], [124.87921, 37.80827], [125.06408, 37.66334], [125.37112, 37.62643], [125.81159, 37.72949], [126.13074, 37.70512], [126.18776, 37.74728], [126.19097, 37.81462], [126.24402, 37.83113], [126.43239, 37.84095], [126.46818, 37.80873], [126.56709, 37.76857], [126.59918, 37.76364], [126.66067, 37.7897], [126.68793, 37.83728], [126.68793, 37.9175], [126.67023, 37.95852], [126.84961, 38.0344], [126.88106, 38.10246], [126.95887, 38.1347], [126.95338, 38.17735], [127.04479, 38.25518], [127.15749, 38.30722], [127.38727, 38.33227], [127.49672, 38.30647], [127.55013, 38.32257], [128.02917, 38.31861], [128.27652, 38.41657], [128.31105, 38.58462], [128.37487, 38.62345], [128.65655, 38.61914], [131.95041, 41.5445], [130.65022, 42.32281], [130.66367, 42.38024], [130.64181, 42.41422], [130.60805, 42.4317], [130.56835, 42.43281], [130.55143, 42.52158], [130.50123, 42.61636], [130.44361, 42.54849], [130.41826, 42.6011], [130.2385, 42.71127], [130.23068, 42.80125], [130.26095, 42.9027]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "KR", + iso1A3: "KOR", + iso1N3: "410", + wikidata: "Q884", + nameEn: "South Korea", + groups: ["030", "142", "UN"], + callingCodes: ["82"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "KW", + iso1A3: "KWT", + iso1N3: "414", + wikidata: "Q817", + nameEn: "Kuwait", + groups: ["145", "142", "UN"], + callingCodes: ["965"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[49.00421, 28.81495], [48.59531, 29.66815], [48.40479, 29.85763], [48.17332, 30.02448], [48.06782, 30.02906], [48.01114, 29.98906], [47.7095, 30.10453], [47.37192, 30.10421], [47.15166, 30.01044], [46.89695, 29.50584], [46.5527, 29.10283], [47.46202, 29.0014], [47.58376, 28.83382], [47.59863, 28.66798], [47.70561, 28.5221], [48.42991, 28.53628], [49.00421, 28.81495]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "KY", + iso1A3: "CYM", + iso1N3: "136", + wikidata: "Q5785", + nameEn: "Cayman Islands", + country: "GB", + groups: ["BOTS", "029", "003", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1 345"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-82.11509, 19.60401], [-80.36068, 18.11751], [-79.32727, 20.06742], [-82.11509, 19.60401]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "KZ", + iso1A3: "KAZ", + iso1N3: "398", + wikidata: "Q232", + nameEn: "Kazakhstan", + groups: ["143", "142", "UN"], + callingCodes: ["7"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[68.90865, 55.38148], [68.19206, 55.18823], [68.26661, 55.09226], [68.21308, 54.98645], [65.20174, 54.55216], [65.24663, 54.35721], [65.11033, 54.33028], [64.97216, 54.4212], [63.97686, 54.29763], [64.02715, 54.22679], [63.91224, 54.20013], [63.80604, 54.27079], [62.58651, 54.05871], [62.56876, 53.94047], [62.45931, 53.90737], [62.38535, 54.03961], [62.00966, 54.04134], [62.03913, 53.94768], [61.65318, 54.02445], [61.56941, 53.95703], [61.47603, 54.08048], [61.3706, 54.08464], [61.26863, 53.92797], [60.99796, 53.93699], [61.14283, 53.90063], [61.22574, 53.80268], [60.90626, 53.62937], [61.55706, 53.57144], [61.57185, 53.50112], [61.37957, 53.45887], [61.29082, 53.50992], [61.14291, 53.41481], [61.19024, 53.30536], [62.14574, 53.09626], [62.12799, 52.99133], [62.0422, 52.96105], [61.23462, 53.03227], [61.05842, 52.92217], [60.71989, 52.75923], [60.71693, 52.66245], [60.84118, 52.63912], [60.84709, 52.52228], [60.98021, 52.50068], [61.05417, 52.35096], [60.78201, 52.22067], [60.72581, 52.15538], [60.48915, 52.15175], [60.19925, 51.99173], [59.99809, 51.98263], [60.09867, 51.87135], [60.50986, 51.7964], [60.36787, 51.66815], [60.5424, 51.61675], [60.92401, 51.61124], [60.95655, 51.48615], [61.50677, 51.40687], [61.55114, 51.32746], [61.6813, 51.25716], [61.56889, 51.23679], [61.4431, 50.80679], [60.81833, 50.6629], [60.31914, 50.67705], [60.17262, 50.83312], [60.01288, 50.8163], [59.81172, 50.54451], [59.51886, 50.49937], [59.48928, 50.64216], [58.87974, 50.70852], [58.3208, 51.15151], [57.75578, 51.13852], [57.74986, 50.93017], [57.44221, 50.88354], [57.17302, 51.11253], [56.17906, 50.93204], [56.11398, 50.7471], [55.67774, 50.54508], [54.72067, 51.03261], [54.56685, 51.01958], [54.71476, 50.61214], [54.55797, 50.52006], [54.41894, 50.61214], [54.46331, 50.85554], [54.12248, 51.11542], [53.69299, 51.23466], [53.46165, 51.49445], [52.54329, 51.48444], [52.36119, 51.74161], [51.8246, 51.67916], [51.77431, 51.49536], [51.301, 51.48799], [51.26254, 51.68466], [50.59695, 51.61859], [50.26859, 51.28677], [49.97277, 51.2405], [49.76866, 51.11067], [49.39001, 51.09396], [49.41959, 50.85927], [49.12673, 50.78639], [48.86936, 50.61589], [48.57946, 50.63278], [48.90782, 50.02281], [48.68352, 49.89546], [48.42564, 49.82283], [48.24519, 49.86099], [48.10044, 50.09242], [47.58551, 50.47867], [47.30448, 50.30894], [47.34589, 50.09308], [47.18319, 49.93721], [46.9078, 49.86707], [46.78398, 49.34026], [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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "LA", + iso1A3: "LAO", + iso1N3: "418", + wikidata: "Q819", + nameEn: "Laos", + groups: ["035", "142", "UN"], + callingCodes: ["856"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[102.1245, 22.43372], [102.03633, 22.46164], [101.98487, 22.42766], [101.91344, 22.44417], [101.90714, 22.38688], [101.86828, 22.38397], [101.7685, 22.50337], [101.68973, 22.46843], [101.61306, 22.27515], [101.56789, 22.28876], [101.53638, 22.24794], [101.60675, 22.13513], [101.57525, 22.13026], [101.62566, 21.96574], [101.7791, 21.83019], [101.74555, 21.72852], [101.83257, 21.61562], [101.80001, 21.57461], [101.7475, 21.5873], [101.7727, 21.51794], [101.74224, 21.48276], [101.74014, 21.30967], [101.84412, 21.25291], [101.83887, 21.20983], [101.76745, 21.21571], [101.79266, 21.19025], [101.7622, 21.14813], [101.70548, 21.14911], [101.66977, 21.20004], [101.60886, 21.17947], [101.59491, 21.18621], [101.6068, 21.23329], [101.54563, 21.25668], [101.29326, 21.17254], [101.2229, 21.23271], [101.26912, 21.36482], [101.19349, 21.41959], [101.2124, 21.56422], [101.15156, 21.56129], [101.16198, 21.52808], [101.00234, 21.39612], [100.80173, 21.2934], [100.72716, 21.31786], [100.63578, 21.05639], [100.55281, 21.02796], [100.50974, 20.88574], [100.64628, 20.88279], [100.60112, 20.8347], [100.51079, 20.82194], [100.36375, 20.82783], [100.1957, 20.68247], [100.08404, 20.36626], [100.09999, 20.31614], [100.09337, 20.26293], [100.11785, 20.24787], [100.1712, 20.24324], [100.16668, 20.2986], [100.22076, 20.31598], [100.25769, 20.3992], [100.33383, 20.4028], [100.37439, 20.35156], [100.41473, 20.25625], [100.44992, 20.23644], [100.4537, 20.19971], [100.47567, 20.19133], [100.51052, 20.14928], [100.55218, 20.17741], [100.58808, 20.15791], [100.5094, 19.87904], [100.398, 19.75047], [100.49604, 19.53504], [100.58219, 19.49164], [100.64606, 19.55884], [100.77231, 19.48324], [100.90302, 19.61901], [101.08928, 19.59748], [101.26545, 19.59242], [101.26991, 19.48324], [101.21347, 19.46223], [101.20604, 19.35296], [101.24911, 19.33334], [101.261, 19.12717], [101.35606, 19.04716], [101.25803, 18.89545], [101.22832, 18.73377], [101.27585, 18.68875], [101.06047, 18.43247], [101.18227, 18.34367], [101.15108, 18.25624], [101.19118, 18.2125], [101.1793, 18.0544], [101.02185, 17.87637], [100.96541, 17.57926], [101.15108, 17.47586], [101.44667, 17.7392], [101.72294, 17.92867], [101.78087, 18.07559], [101.88485, 18.02474], [102.11359, 18.21532], [102.45523, 17.97106], [102.59234, 17.96127], [102.60971, 17.95411], [102.61432, 17.92273], [102.5896, 17.84889], [102.59485, 17.83537], [102.68194, 17.80151], [102.69946, 17.81686], [102.67543, 17.84529], [102.68538, 17.86653], [102.75954, 17.89561], [102.79044, 17.93612], [102.81988, 17.94233], [102.86323, 17.97531], [102.95812, 18.0054], [102.9912, 17.9949], [103.01998, 17.97095], [103.0566, 18.00144], [103.07823, 18.03833], [103.07343, 18.12351], [103.1493, 18.17799], [103.14994, 18.23172], [103.17093, 18.2618], [103.29757, 18.30475], [103.23818, 18.34875], [103.24779, 18.37807], [103.30977, 18.4341], [103.41044, 18.4486], [103.47773, 18.42841], [103.60957, 18.40528], [103.699, 18.34125], [103.82449, 18.33979], [103.85642, 18.28666], [103.93916, 18.33914], [103.97725, 18.33631], [104.06533, 18.21656], [104.10927, 18.10826], [104.21776, 17.99335], [104.2757, 17.86139], [104.35432, 17.82871], [104.45404, 17.66788], [104.69867, 17.53038], [104.80061, 17.39367], [104.80716, 17.19025], [104.73712, 17.01404], [104.7373, 16.91125], [104.76442, 16.84752], [104.7397, 16.81005], [104.76099, 16.69302], [104.73349, 16.565], [104.88057, 16.37311], [105.00262, 16.25627], [105.06204, 16.09792], [105.42001, 16.00657], [105.38508, 15.987], [105.34115, 15.92737], [105.37959, 15.84074], [105.42285, 15.76971], [105.46573, 15.74742], [105.61756, 15.68792], [105.60446, 15.53301], [105.58191, 15.41031], [105.47635, 15.3796], [105.4692, 15.33709], [105.50662, 15.32054], [105.58043, 15.32724], [105.46661, 15.13132], [105.61162, 15.00037], [105.5121, 14.80802], [105.53864, 14.55731], [105.43783, 14.43865], [105.20894, 14.34967], [105.2759, 14.17496], [105.36775, 14.09948], [105.44869, 14.10703], [105.5561, 14.15684], [105.78338, 14.08438], [105.78182, 14.02247], [105.90791, 13.92881], [106.10405, 13.9137], [106.10094, 13.98471], [106.16632, 14.01794], [106.18656, 14.06324], [106.11962, 14.11307], [106.10872, 14.18401], [106.04801, 14.20363], [106.02311, 14.30623], [105.99509, 14.32734], [106.00131, 14.36957], [106.21302, 14.36203], [106.25194, 14.48415], [106.32355, 14.44043], [106.40916, 14.45249], [106.43874, 14.52032], [106.47766, 14.50977], [106.45898, 14.55045], [106.50723, 14.58963], [106.54148, 14.59565], [106.57106, 14.50525], [106.59908, 14.50977], [106.63333, 14.44194], [106.73762, 14.42687], [106.80767, 14.31226], [106.8497, 14.29416], [106.90574, 14.33639], [106.9649, 14.3198], [106.98825, 14.36806], [107.04585, 14.41782], [107.03962, 14.45099], [107.09722, 14.3937], [107.17038, 14.41782], [107.21241, 14.48716], [107.256, 14.48716], [107.26534, 14.54292], [107.29803, 14.58963], [107.3276, 14.58812], [107.37897, 14.54443], [107.44435, 14.52785], [107.47238, 14.61523], [107.54361, 14.69092], [107.51579, 14.79282], [107.59285, 14.87795], [107.48277, 14.93751], [107.46516, 15.00982], [107.61486, 15.0566], [107.61926, 15.13949], [107.58844, 15.20111], [107.62587, 15.2266], [107.60605, 15.37524], [107.62367, 15.42193], [107.53341, 15.40496], [107.50699, 15.48771], [107.3815, 15.49832], [107.34408, 15.62345], [107.27583, 15.62769], [107.27143, 15.71459], [107.21859, 15.74638], [107.21419, 15.83747], [107.34188, 15.89464], [107.39471, 15.88829], [107.46296, 16.01106], [107.44975, 16.08511], [107.33968, 16.05549], [107.25822, 16.13587], [107.14595, 16.17816], [107.15035, 16.26271], [107.09091, 16.3092], [107.02597, 16.31132], [106.97385, 16.30204], [106.96638, 16.34938], [106.88067, 16.43594], [106.88727, 16.52671], [106.84104, 16.55415], [106.74418, 16.41904], [106.65832, 16.47816], [106.66052, 16.56892], [106.61477, 16.60713], [106.58267, 16.6012], [106.59013, 16.62259], [106.55485, 16.68704], [106.55265, 16.86831], [106.52183, 16.87884], [106.51963, 16.92097], [106.54824, 16.92729], [106.55045, 17.0031], [106.50862, 16.9673], [106.43597, 17.01362], [106.31929, 17.20509], [106.29287, 17.3018], [106.24444, 17.24714], [106.18991, 17.28227], [106.09019, 17.36399], [105.85744, 17.63221], [105.76612, 17.67147], [105.60381, 17.89356], [105.64784, 17.96687], [105.46292, 18.22008], [105.38366, 18.15315], [105.15942, 18.38691], [105.10408, 18.43533], [105.1327, 18.58355], [105.19654, 18.64196], [105.12829, 18.70453], [104.64617, 18.85668], [104.5361, 18.97747], [103.87125, 19.31854], [104.06058, 19.43484], [104.10832, 19.51575], [104.05617, 19.61743], [104.06498, 19.66926], [104.23229, 19.70242], [104.41281, 19.70035], [104.53169, 19.61743], [104.64837, 19.62365], [104.68359, 19.72729], [104.8355, 19.80395], [104.8465, 19.91783], [104.9874, 20.09573], [104.91695, 20.15567], [104.86852, 20.14121], [104.61315, 20.24452], [104.62195, 20.36633], [104.72102, 20.40554], [104.66158, 20.47774], [104.47886, 20.37459], [104.40621, 20.3849], [104.38199, 20.47155], [104.63957, 20.6653], [104.27412, 20.91433], [104.11121, 20.96779], [103.98024, 20.91531], [103.82282, 20.8732], [103.73478, 20.6669], [103.68633, 20.66324], [103.45737, 20.82382], [103.38032, 20.79501], [103.21497, 20.89832], [103.12055, 20.89994], [103.03469, 21.05821], [102.97745, 21.05821], [102.89825, 21.24707], [102.80794, 21.25736], [102.88939, 21.3107], [102.94223, 21.46034], [102.86297, 21.4255], [102.98846, 21.58936], [102.97965, 21.74076], [102.86077, 21.71213], [102.85637, 21.84501], [102.81894, 21.83888], [102.82115, 21.73667], [102.74189, 21.66713], [102.67145, 21.65894], [102.62301, 21.91447], [102.49092, 21.99002], [102.51734, 22.02676], [102.18712, 22.30403], [102.14099, 22.40092], [102.1245, 22.43372]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "LB", + iso1A3: "LBN", + iso1N3: "422", + wikidata: "Q822", + nameEn: "Lebanon", + aliases: ["RL"], + groups: ["145", "142", "UN"], + callingCodes: ["961"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[35.94816, 33.47886], [35.94465, 33.52774], [36.05723, 33.57904], [35.9341, 33.6596], [36.06778, 33.82927], [36.14517, 33.85118], [36.3967, 33.83365], [36.38263, 33.86579], [36.28589, 33.91981], [36.41078, 34.05253], [36.50576, 34.05982], [36.5128, 34.09916], [36.62537, 34.20251], [36.59195, 34.2316], [36.58667, 34.27667], [36.60778, 34.31009], [36.56556, 34.31881], [36.53039, 34.3798], [36.55853, 34.41609], [36.46179, 34.46541], [36.4442, 34.50165], [36.34745, 34.5002], [36.3369, 34.52629], [36.39846, 34.55672], [36.41429, 34.61175], [36.45299, 34.59438], [36.46003, 34.6378], [36.42941, 34.62505], [36.35384, 34.65447], [36.35135, 34.68516], [36.32399, 34.69334], [36.29165, 34.62991], [35.98718, 34.64977], [35.97386, 34.63322], [35.48515, 34.70851], [34.78515, 33.20368], [35.10645, 33.09318], [35.1924, 33.08743], [35.31429, 33.10515], [35.35223, 33.05617], [35.43059, 33.06659], [35.448, 33.09264], [35.50272, 33.09056], [35.50335, 33.114], [35.52573, 33.11921], [35.54228, 33.19865], [35.5362, 33.23196], [35.54808, 33.236], [35.54544, 33.25513], [35.55555, 33.25844], [35.56523, 33.28969], [35.58326, 33.28381], [35.58502, 33.26653], [35.62283, 33.24226], [35.62019, 33.27278], [35.77477, 33.33609], [35.81324, 33.36354], [35.82577, 33.40479], [35.88668, 33.43183], [35.94816, 33.47886]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "LC", + iso1A3: "LCA", + iso1N3: "662", + wikidata: "Q760", + nameEn: "St. Lucia", + aliases: ["WL"], + groups: ["029", "003", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + callingCodes: ["1 758"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-59.95997, 14.20285], [-61.69315, 14.26451], [-59.94058, 12.34011], [-59.95997, 14.20285]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "LI", + iso1A3: "LIE", + iso1N3: "438", + wikidata: "Q347", + nameEn: "Liechtenstein", + aliases: ["FL"], + groups: ["155", "150", "UN"], + callingCodes: ["423"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[9.60717, 47.06091], [9.61216, 47.07732], [9.63395, 47.08443], [9.62623, 47.14685], [9.56539, 47.17124], [9.58264, 47.20673], [9.56981, 47.21926], [9.55176, 47.22585], [9.56766, 47.24281], [9.53116, 47.27029], [9.52406, 47.24959], [9.50318, 47.22153], [9.4891, 47.19346], [9.48774, 47.17402], [9.51044, 47.13727], [9.52089, 47.10019], [9.51362, 47.08505], [9.47139, 47.06402], [9.47548, 47.05257], [9.54041, 47.06495], [9.55721, 47.04762], [9.60717, 47.06091]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "LK", + iso1A3: "LKA", + iso1N3: "144", + wikidata: "Q854", + nameEn: "Sri Lanka", + groups: ["034", "142", "UN"], + driveSide: "left", + callingCodes: ["94"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "LR", + iso1A3: "LBR", + iso1N3: "430", + wikidata: "Q1014", + nameEn: "Liberia", + groups: ["011", "202", "002", "UN"], + callingCodes: ["231"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-8.47114, 7.55676], [-8.55874, 7.62525], [-8.55874, 7.70167], [-8.67814, 7.69428], [-8.72789, 7.51429], [-8.8448, 7.35149], [-8.85724, 7.26019], [-8.93435, 7.2824], [-9.09107, 7.1985], [-9.18311, 7.30461], [-9.20798, 7.38109], [-9.305, 7.42056], [-9.41943, 7.41809], [-9.48161, 7.37122], [-9.37465, 7.62032], [-9.35724, 7.74111], [-9.44928, 7.9284], [-9.41445, 8.02448], [-9.50898, 8.18455], [-9.47415, 8.35195], [-9.77763, 8.54633], [-10.05873, 8.42578], [-10.05375, 8.50697], [-10.14579, 8.52665], [-10.203, 8.47991], [-10.27575, 8.48711], [-10.30084, 8.30008], [-10.31635, 8.28554], [-10.29839, 8.21283], [-10.35227, 8.15223], [-10.45023, 8.15627], [-10.51554, 8.1393], [-10.57523, 8.04829], [-10.60492, 8.04072], [-10.60422, 7.7739], [-11.29417, 7.21576], [-11.4027, 6.97746], [-11.50429, 6.92704], [-12.15048, 6.15992], [-7.52774, 3.7105], [-7.53259, 4.35145], [-7.59349, 4.8909], [-7.53876, 4.94294], [-7.55369, 5.08667], [-7.48901, 5.14118], [-7.46165, 5.26256], [-7.36463, 5.32944], [-7.43428, 5.42355], [-7.37209, 5.61173], [-7.43926, 5.74787], [-7.43677, 5.84687], [-7.46165, 5.84934], [-7.48155, 5.80974], [-7.67309, 5.94337], [-7.70294, 5.90625], [-7.78254, 5.99037], [-7.79747, 6.07696], [-7.8497, 6.08932], [-7.83478, 6.20309], [-7.90692, 6.27728], [-8.00642, 6.31684], [-8.17557, 6.28222], [-8.3298, 6.36381], [-8.38453, 6.35887], [-8.45666, 6.49977], [-8.48652, 6.43797], [-8.59456, 6.50612], [-8.31736, 6.82837], [-8.29249, 7.1691], [-8.37458, 7.25794], [-8.41935, 7.51203], [-8.47114, 7.55676]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "LS", + iso1A3: "LSO", + iso1N3: "426", + wikidata: "Q1013", + nameEn: "Lesotho", + groups: ["018", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["266"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[29.33204, -29.45598], [29.44883, -29.3772], [29.40524, -29.21246], [28.68043, -28.58744], [28.65091, -28.57025], [28.40612, -28.6215], [28.30518, -28.69531], [28.2348, -28.69471], [28.1317, -28.7293], [28.02503, -28.85991], [27.98675, -28.8787], [27.9392, -28.84864], [27.88933, -28.88156], [27.8907, -28.91612], [27.75458, -28.89839], [27.55974, -29.18954], [27.5158, -29.2261], [27.54258, -29.25575], [27.48679, -29.29349], [27.45125, -29.29708], [27.47254, -29.31968], [27.4358, -29.33465], [27.33464, -29.48161], [27.01016, -29.65439], [27.09489, -29.72796], [27.22719, -30.00718], [27.29603, -30.05473], [27.32555, -30.14785], [27.40778, -30.14577], [27.37293, -30.19401], [27.36649, -30.27246], [27.38108, -30.33456], [27.45452, -30.32239], [27.56901, -30.42504], [27.56781, -30.44562], [27.62137, -30.50509], [27.6521, -30.51707], [27.67819, -30.53437], [27.69467, -30.55862], [27.74814, -30.60635], [28.12073, -30.68072], [28.2319, -30.28476], [28.399, -30.1592], [28.68627, -30.12885], [28.80222, -30.10579], [28.9338, -30.05072], [29.16548, -29.91706], [29.12553, -29.76266], [29.28545, -29.58456], [29.33204, -29.45598]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "LT", + iso1A3: "LTU", + iso1N3: "440", + wikidata: "Q37", + nameEn: "Lithuania", + groups: ["EU", "154", "150", "UN"], + callingCodes: ["370"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[24.89005, 56.46666], [24.83686, 56.41565], [24.70022, 56.40483], [24.57353, 56.31525], [24.58143, 56.29125], [24.42746, 56.26522], [24.32334, 56.30226], [24.13139, 56.24881], [24.02657, 56.3231], [23.75726, 56.37282], [23.49803, 56.34307], [23.40486, 56.37689], [23.31606, 56.3827], [23.17312, 56.36795], [23.09531, 56.30511], [22.96988, 56.41213], [22.83048, 56.367], [22.69354, 56.36284], [22.56441, 56.39305], [22.3361, 56.4016], [22.09728, 56.42851], [22.00548, 56.41508], [21.74558, 56.33181], [21.57888, 56.31406], [21.49736, 56.29106], [21.24644, 56.16917], [21.15016, 56.07818], [20.68447, 56.04073], [20.60454, 55.40986], [20.95181, 55.27994], [21.26425, 55.24456], [21.35465, 55.28427], [21.38446, 55.29348], [21.46766, 55.21115], [21.51095, 55.18507], [21.55605, 55.20311], [21.64954, 55.1791], [21.85521, 55.09493], [21.96505, 55.07353], [21.99543, 55.08691], [22.03984, 55.07888], [22.02582, 55.05078], [22.06087, 55.02935], [22.11697, 55.02131], [22.14267, 55.05345], [22.31562, 55.0655], [22.47688, 55.04408], [22.58907, 55.07085], [22.60075, 55.01863], [22.65451, 54.97037], [22.68723, 54.9811], [22.76422, 54.92521], [22.85083, 54.88711], [22.87317, 54.79492], [22.73631, 54.72952], [22.73397, 54.66604], [22.75467, 54.6483], [22.74225, 54.64339], [22.7522, 54.63525], [22.68021, 54.58486], [22.71293, 54.56454], [22.67788, 54.532], [22.70208, 54.45312], [22.7253, 54.41732], [22.79705, 54.36264], [22.83756, 54.40827], [23.00584, 54.38514], [22.99649, 54.35927], [23.05726, 54.34565], [23.04323, 54.31567], [23.104, 54.29794], [23.13905, 54.31567], [23.15526, 54.31076], [23.15938, 54.29894], [23.24656, 54.25701], [23.3494, 54.25155], [23.39525, 54.21672], [23.42418, 54.17911], [23.45223, 54.17775], [23.49196, 54.14764], [23.52702, 54.04622], [23.48261, 53.98855], [23.51284, 53.95052], [23.61677, 53.92691], [23.71726, 53.93379], [23.80543, 53.89558], [23.81309, 53.94205], [23.95098, 53.9613], [23.98837, 53.92554], [24.19638, 53.96405], [24.34128, 53.90076], [24.44411, 53.90076], [24.62275, 54.00217], [24.69652, 54.01901], [24.69185, 53.96543], [24.74279, 53.96663], [24.85311, 54.02862], [24.77131, 54.11091], [24.96894, 54.17589], [24.991, 54.14241], [25.0728, 54.13419], [25.19199, 54.219], [25.22705, 54.26271], [25.35559, 54.26544], [25.509, 54.30267], [25.56823, 54.25212], [25.51452, 54.17799], [25.54724, 54.14925], [25.64875, 54.1259], [25.71084, 54.16704], [25.78563, 54.15747], [25.78553, 54.23327], [25.68513, 54.31727], [25.55425, 54.31591], [25.5376, 54.33158], [25.63371, 54.42075], [25.62203, 54.4656], [25.64813, 54.48704], [25.68045, 54.5321], [25.75977, 54.57252], [25.74122, 54.80108], [25.89462, 54.93438], [25.99129, 54.95705], [26.05907, 54.94631], [26.13386, 54.98924], [26.20397, 54.99729], [26.26941, 55.08032], [26.23202, 55.10439], [26.30628, 55.12536], [26.35121, 55.1525], [26.46249, 55.12814], [26.51481, 55.16051], [26.54753, 55.14181], [26.69243, 55.16718], [26.68075, 55.19787], [26.72983, 55.21788], [26.73017, 55.24226], [26.835, 55.28182], [26.83266, 55.30444], [26.80929, 55.31642], [26.6714, 55.33902], [26.5709, 55.32572], [26.44937, 55.34832], [26.5522, 55.40277], [26.55094, 55.5093], [26.63167, 55.57887], [26.63231, 55.67968], [26.58248, 55.6754], [26.46661, 55.70375], [26.39561, 55.71156], [26.18509, 55.86813], [26.03815, 55.95884], [25.90047, 56.0013], [25.85893, 56.00188], [25.81773, 56.05444], [25.69246, 56.08892], [25.68588, 56.14725], [25.53621, 56.16663], [25.39751, 56.15707], [25.23099, 56.19147], [25.09325, 56.1878], [25.05762, 56.26742], [24.89005, 56.46666]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "LU", + iso1A3: "LUX", + iso1N3: "442", + wikidata: "Q32", + nameEn: "Luxembourg", + groups: ["EU", "155", "150", "UN"], + callingCodes: ["352"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[6.1379, 50.12964], [6.1137, 50.13668], [6.12028, 50.16374], [6.08577, 50.17246], [6.06406, 50.15344], [6.03093, 50.16362], [6.02488, 50.18283], [5.96453, 50.17259], [5.95929, 50.13295], [5.89488, 50.11476], [5.8857, 50.07824], [5.85474, 50.06342], [5.86904, 50.04614], [5.8551, 50.02683], [5.81866, 50.01286], [5.82331, 49.99662], [5.83968, 49.9892], [5.83467, 49.97823], [5.81163, 49.97142], [5.80833, 49.96451], [5.77291, 49.96056], [5.77314, 49.93646], [5.73621, 49.89796], [5.78415, 49.87922], [5.75269, 49.8711], [5.75861, 49.85631], [5.74567, 49.85368], [5.75884, 49.84811], [5.74953, 49.84709], [5.74975, 49.83933], [5.74076, 49.83823], [5.7404, 49.83452], [5.74844, 49.82435], [5.74364, 49.82058], [5.74953, 49.81428], [5.75409, 49.79239], [5.78871, 49.7962], [5.82245, 49.75048], [5.83149, 49.74729], [5.82562, 49.72395], [5.84193, 49.72161], [5.86503, 49.72739], [5.88677, 49.70951], [5.86527, 49.69291], [5.86175, 49.67862], [5.9069, 49.66377], [5.90164, 49.6511], [5.90599, 49.63853], [5.88552, 49.63507], [5.88393, 49.62802], [5.87609, 49.62047], [5.8762, 49.60898], [5.84826, 49.5969], [5.84971, 49.58674], [5.86986, 49.58756], [5.87256, 49.57539], [5.8424, 49.56082], [5.84692, 49.55663], [5.84143, 49.5533], [5.81838, 49.54777], [5.80871, 49.5425], [5.81664, 49.53775], [5.83648, 49.5425], [5.84466, 49.53027], [5.83467, 49.52717], [5.83389, 49.52152], [5.86571, 49.50015], [5.94128, 49.50034], [5.94224, 49.49608], [5.96876, 49.49053], [5.97693, 49.45513], [6.02648, 49.45451], [6.02743, 49.44845], [6.04176, 49.44801], [6.05553, 49.46663], [6.07887, 49.46399], [6.08373, 49.45594], [6.10072, 49.45268], [6.09845, 49.46351], [6.10325, 49.4707], [6.12346, 49.4735], [6.12814, 49.49365], [6.14321, 49.48796], [6.16115, 49.49297], [6.15366, 49.50226], [6.17386, 49.50934], [6.19543, 49.50536], [6.2409, 49.51408], [6.25029, 49.50609], [6.27875, 49.503], [6.28818, 49.48465], [6.3687, 49.4593], [6.36778, 49.46937], [6.36907, 49.48931], [6.36788, 49.50377], [6.35666, 49.52931], [6.38072, 49.55171], [6.38228, 49.55855], [6.35825, 49.57053], [6.36676, 49.57813], [6.38024, 49.57593], [6.38342, 49.5799], [6.37464, 49.58886], [6.385, 49.59946], [6.39822, 49.60081], [6.41861, 49.61723], [6.4413, 49.65722], [6.43768, 49.66021], [6.42726, 49.66078], [6.42937, 49.66857], [6.44654, 49.67799], [6.46048, 49.69092], [6.48014, 49.69767], [6.49785, 49.71118], [6.50647, 49.71353], [6.5042, 49.71808], [6.49694, 49.72205], [6.49535, 49.72645], [6.50261, 49.72718], [6.51397, 49.72058], [6.51805, 49.72425], [6.50193, 49.73291], [6.50174, 49.75292], [6.51646, 49.75961], [6.51828, 49.76855], [6.51056, 49.77515], [6.51669, 49.78336], [6.50534, 49.78952], [6.52169, 49.79787], [6.53122, 49.80666], [6.52121, 49.81338], [6.51215, 49.80124], [6.50647, 49.80916], [6.48718, 49.81267], [6.47111, 49.82263], [6.45425, 49.81164], [6.44131, 49.81443], [6.42905, 49.81091], [6.42521, 49.81591], [6.40022, 49.82029], [6.36576, 49.85032], [6.34267, 49.84974], [6.33585, 49.83785], [6.32098, 49.83728], [6.32303, 49.85133], [6.30963, 49.87021], [6.29692, 49.86685], [6.28874, 49.87592], [6.26146, 49.88203], [6.23496, 49.89972], [6.22926, 49.92096], [6.21882, 49.92403], [6.22608, 49.929], [6.22094, 49.94955], [6.19856, 49.95053], [6.19089, 49.96991], [6.18045, 49.96611], [6.18554, 49.95622], [6.17872, 49.9537], [6.16466, 49.97086], [6.1701, 49.98518], [6.14147, 49.99563], [6.14948, 50.00908], [6.13806, 50.01056], [6.1295, 50.01849], [6.13273, 50.02019], [6.13794, 50.01466], [6.14666, 50.02207], [6.13044, 50.02929], [6.13458, 50.04141], [6.11274, 50.05916], [6.12055, 50.09171], [6.1379, 50.12964]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "LV", + iso1A3: "LVA", + iso1N3: "428", + wikidata: "Q211", + nameEn: "Latvia", + groups: ["EU", "154", "150", "UN"], + callingCodes: ["371"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[27.34698, 57.52242], [26.90364, 57.62823], [26.54675, 57.51813], [26.46527, 57.56885], [26.29253, 57.59244], [26.1866, 57.6849], [26.2029, 57.7206], [26.08098, 57.76619], [26.0543, 57.76105], [26.03332, 57.7718], [26.02415, 57.76865], [26.02069, 57.77169], [26.0266, 57.77441], [26.027, 57.78158], [26.02456, 57.78342], [26.0324, 57.79037], [26.05949, 57.84744], [25.73499, 57.90193], [25.29581, 58.08288], [25.28237, 57.98539], [25.19484, 58.0831], [24.3579, 57.87471], [24.26221, 57.91787], [23.20055, 57.56697], [22.80496, 57.87798], [19.84909, 57.57876], [19.64795, 57.06466], [20.68447, 56.04073], [21.15016, 56.07818], [21.24644, 56.16917], [21.49736, 56.29106], [21.57888, 56.31406], [21.74558, 56.33181], [22.00548, 56.41508], [22.09728, 56.42851], [22.3361, 56.4016], [22.56441, 56.39305], [22.69354, 56.36284], [22.83048, 56.367], [22.96988, 56.41213], [23.09531, 56.30511], [23.17312, 56.36795], [23.31606, 56.3827], [23.40486, 56.37689], [23.49803, 56.34307], [23.75726, 56.37282], [24.02657, 56.3231], [24.13139, 56.24881], [24.32334, 56.30226], [24.42746, 56.26522], [24.58143, 56.29125], [24.57353, 56.31525], [24.70022, 56.40483], [24.83686, 56.41565], [24.89005, 56.46666], [25.05762, 56.26742], [25.09325, 56.1878], [25.23099, 56.19147], [25.39751, 56.15707], [25.53621, 56.16663], [25.68588, 56.14725], [25.69246, 56.08892], [25.81773, 56.05444], [25.85893, 56.00188], [25.90047, 56.0013], [26.03815, 55.95884], [26.18509, 55.86813], [26.39561, 55.71156], [26.46661, 55.70375], [26.58248, 55.6754], [26.63231, 55.67968], [26.64888, 55.70515], [26.71802, 55.70645], [26.76872, 55.67658], [26.87448, 55.7172], [26.97153, 55.8102], [27.1559, 55.85032], [27.27804, 55.78299], [27.3541, 55.8089], [27.61683, 55.78558], [27.63065, 55.89687], [27.97865, 56.11849], [28.15217, 56.16964], [28.23716, 56.27588], [28.16599, 56.37806], [28.19057, 56.44637], [28.10069, 56.524], [28.13526, 56.57989], [28.04768, 56.59004], [27.86101, 56.88204], [27.66511, 56.83921], [27.86101, 57.29402], [27.52453, 57.42826], [27.56832, 57.53728], [27.34698, 57.52242]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "LY", + iso1A3: "LBY", + iso1N3: "434", + wikidata: "Q1016", + nameEn: "Libya", + groups: ["015", "002", "UN"], + callingCodes: ["218"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MA", + iso1A3: "MAR", + iso1N3: "504", + wikidata: "Q1028", + nameEn: "Morocco", + groups: ["015", "002", "UN"], + callingCodes: ["212"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MC", + iso1A3: "MCO", + iso1N3: "492", + wikidata: "Q235", + nameEn: "Monaco", + groups: ["155", "150", "UN"], + callingCodes: ["377"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[7.47823, 43.73341], [7.4379, 43.74963], [7.4389, 43.75151], [7.43708, 43.75197], [7.43624, 43.75014], [7.43013, 43.74895], [7.42809, 43.74396], [7.42443, 43.74087], [7.42299, 43.74176], [7.42062, 43.73977], [7.41233, 43.73439], [7.41298, 43.73311], [7.41291, 43.73168], [7.41113, 43.73156], [7.40903, 43.7296], [7.42422, 43.72209], [7.47823, 43.73341]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MD", + iso1A3: "MDA", + iso1N3: "498", + wikidata: "Q217", + nameEn: "Moldova", + groups: ["151", "150", "UN"], + callingCodes: ["373"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[27.74422, 48.45926], [27.6658, 48.44034], [27.59027, 48.46311], [27.5889, 48.49224], [27.46942, 48.454], [27.44333, 48.41209], [27.37741, 48.41026], [27.37604, 48.44398], [27.32159, 48.4434], [27.27855, 48.37534], [27.13434, 48.37288], [27.08078, 48.43214], [27.0231, 48.42485], [27.03821, 48.37653], [26.93384, 48.36558], [26.85556, 48.41095], [26.71274, 48.40388], [26.82809, 48.31629], [26.79239, 48.29071], [26.6839, 48.35828], [26.62823, 48.25804], [26.81161, 48.25049], [26.87708, 48.19919], [26.94265, 48.1969], [26.98042, 48.15752], [26.96119, 48.13003], [27.04118, 48.12522], [27.02985, 48.09083], [27.15622, 47.98538], [27.1618, 47.92391], [27.29069, 47.73722], [27.25519, 47.71366], [27.32202, 47.64009], [27.3979, 47.59473], [27.47942, 47.48113], [27.55731, 47.46637], [27.60263, 47.32507], [27.68706, 47.28962], [27.73172, 47.29248], [27.81892, 47.1381], [28.09095, 46.97621], [28.12173, 46.82283], [28.24808, 46.64305], [28.22281, 46.50481], [28.25769, 46.43334], [28.18902, 46.35283], [28.19864, 46.31869], [28.10937, 46.22852], [28.13684, 46.18099], [28.08612, 46.01105], [28.13111, 45.92819], [28.16568, 45.6421], [28.08927, 45.6051], [28.18741, 45.47358], [28.21139, 45.46895], [28.30201, 45.54744], [28.41836, 45.51715], [28.43072, 45.48538], [28.51449, 45.49982], [28.49252, 45.56716], [28.54196, 45.58062], [28.51587, 45.6613], [28.47879, 45.66994], [28.52823, 45.73803], [28.70401, 45.78019], [28.69852, 45.81753], [28.78503, 45.83475], [28.74383, 45.96664], [28.98004, 46.00385], [29.00613, 46.04962], [28.94643, 46.09176], [29.06656, 46.19716], [28.94953, 46.25852], [28.98478, 46.31803], [29.004, 46.31495], [28.9306, 46.45699], [29.01241, 46.46177], [29.02409, 46.49582], [29.23547, 46.55435], [29.24886, 46.37912], [29.35357, 46.49505], [29.49914, 46.45889], [29.5939, 46.35472], [29.6763, 46.36041], [29.66359, 46.4215], [29.74496, 46.45605], [29.88329, 46.35851], [29.94114, 46.40114], [30.09103, 46.38694], [30.16794, 46.40967], [30.02511, 46.45132], [29.88916, 46.54302], [29.94409, 46.56002], [29.9743, 46.75325], [29.94522, 46.80055], [29.98814, 46.82358], [29.87405, 46.88199], [29.75458, 46.8604], [29.72986, 46.92234], [29.57056, 46.94766], [29.62137, 47.05069], [29.61038, 47.09932], [29.53044, 47.07851], [29.49732, 47.12878], [29.57696, 47.13581], [29.54996, 47.24962], [29.59665, 47.25521], [29.5733, 47.36508], [29.48678, 47.36043], [29.47854, 47.30366], [29.39889, 47.30179], [29.3261, 47.44664], [29.18603, 47.43387], [29.11743, 47.55001], [29.22414, 47.60012], [29.22242, 47.73607], [29.27255, 47.79953], [29.20663, 47.80367], [29.27804, 47.88893], [29.19839, 47.89261], [29.1723, 47.99013], [28.9306, 47.96255], [28.8414, 48.03392], [28.85232, 48.12506], [28.69896, 48.13106], [28.53921, 48.17453], [28.48428, 48.0737], [28.42454, 48.12047], [28.43701, 48.15832], [28.38712, 48.17567], [28.34009, 48.13147], [28.30609, 48.14018], [28.30586, 48.1597], [28.34912, 48.1787], [28.36996, 48.20543], [28.35519, 48.24957], [28.32508, 48.23384], [28.2856, 48.23202], [28.19314, 48.20749], [28.17666, 48.25963], [28.07504, 48.23494], [28.09873, 48.3124], [28.04527, 48.32661], [27.95883, 48.32368], [27.88391, 48.36699], [27.87533, 48.4037], [27.81902, 48.41874], [27.79225, 48.44244], [27.74422, 48.45926]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "ME", + iso1A3: "MNE", + iso1N3: "499", + wikidata: "Q236", + nameEn: "Montenegro", + groups: ["039", "150", "UN"], + callingCodes: ["382"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[19.22807, 43.5264], [19.15685, 43.53943], [19.13933, 43.5282], [19.04934, 43.50384], [19.01078, 43.55806], [18.91379, 43.50299], [18.95469, 43.49367], [18.96053, 43.45042], [19.01078, 43.43854], [19.04071, 43.397], [19.08673, 43.31453], [19.08206, 43.29668], [19.04233, 43.30008], [19.00844, 43.24988], [18.95001, 43.29327], [18.95819, 43.32899], [18.90911, 43.36383], [18.83912, 43.34795], [18.84794, 43.33735], [18.85342, 43.32426], [18.76538, 43.29838], [18.6976, 43.25243], [18.71747, 43.2286], [18.66605, 43.2056], [18.64735, 43.14766], [18.66254, 43.03928], [18.52232, 43.01451], [18.49076, 42.95553], [18.49661, 42.89306], [18.4935, 42.86433], [18.47633, 42.85829], [18.45921, 42.81682], [18.47324, 42.74992], [18.56789, 42.72074], [18.55221, 42.69045], [18.54603, 42.69171], [18.54841, 42.68328], [18.57373, 42.64429], [18.52232, 42.62279], [18.55504, 42.58409], [18.53751, 42.57376], [18.49778, 42.58409], [18.43735, 42.55921], [18.44307, 42.51077], [18.43588, 42.48556], [18.52152, 42.42302], [18.54128, 42.39171], [18.45131, 42.21682], [19.26406, 41.74971], [19.37597, 41.84849], [19.37451, 41.8842], [19.33812, 41.90669], [19.34601, 41.95675], [19.37691, 41.96977], [19.36867, 42.02564], [19.37548, 42.06835], [19.40687, 42.10024], [19.28623, 42.17745], [19.42, 42.33019], [19.42352, 42.36546], [19.4836, 42.40831], [19.65972, 42.62774], [19.73244, 42.66299], [19.77375, 42.58517], [19.74731, 42.57422], [19.76549, 42.50237], [19.82333, 42.46581], [19.9324, 42.51699], [20.00842, 42.5109], [20.01834, 42.54622], [20.07761, 42.55582], [20.0969, 42.65559], [20.02915, 42.71147], [20.02088, 42.74789], [20.04898, 42.77701], [20.2539, 42.76245], [20.27869, 42.81945], [20.35692, 42.8335], [20.34528, 42.90676], [20.16415, 42.97177], [20.14896, 42.99058], [20.12325, 42.96237], [20.05431, 42.99571], [20.04729, 43.02732], [19.98887, 43.0538], [19.96549, 43.11098], [19.92576, 43.08539], [19.79255, 43.11951], [19.76918, 43.16044], [19.64063, 43.19027], [19.62661, 43.2286], [19.54598, 43.25158], [19.52962, 43.31623], [19.48171, 43.32644], [19.44315, 43.38846], [19.22229, 43.47926], [19.22807, 43.5264]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MF", + iso1A3: "MAF", + iso1N3: "663", + wikidata: "Q126125", + nameEn: "Saint-Martin", + country: "FR", + groups: ["Q3320166", "EU", "029", "003", "419", "019", "UN"], + callingCodes: ["590"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MG", + iso1A3: "MDG", + iso1N3: "450", + wikidata: "Q1019", + nameEn: "Madagascar", + aliases: ["RM"], + groups: ["014", "202", "002", "UN"], + callingCodes: ["261"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[51.93891, -10.85085], [45.84651, -12.77177], [42.14681, -19.63341], [45.80092, -33.00974], [51.93891, -10.85085]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MH", + iso1A3: "MHL", + iso1N3: "584", + wikidata: "Q709", + nameEn: "Marshall Islands", + groups: ["057", "009", "UN"], + roadSpeedUnit: "mph", + callingCodes: ["692"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[169, 3.9], [173.53711, 5.70687], [169.29099, 15.77133], [159.04653, 10.59067], [169, 3.9]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MK", + iso1A3: "MKD", + iso1N3: "807", + wikidata: "Q221", + nameEn: "North Macedonia", + groups: ["039", "150", "UN"], + callingCodes: ["389"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[22.34773, 42.31725], [22.29275, 42.34913], [22.29605, 42.37477], [22.16384, 42.32103], [22.02908, 42.29848], [21.94405, 42.34669], [21.91595, 42.30392], [21.84654, 42.3247], [21.77176, 42.2648], [21.70111, 42.23789], [21.58992, 42.25915], [21.52145, 42.24465], [21.50823, 42.27156], [21.43882, 42.2789], [21.43882, 42.23609], [21.38428, 42.24465], [21.30496, 42.1418], [21.29913, 42.13954], [21.31983, 42.10993], [21.22728, 42.08909], [21.16614, 42.19815], [21.11491, 42.20794], [20.75464, 42.05229], [20.76786, 41.91839], [20.68523, 41.85318], [20.59524, 41.8818], [20.55976, 41.87068], [20.57144, 41.7897], [20.53405, 41.78099], [20.51301, 41.72433], [20.52937, 41.69292], [20.51769, 41.65975], [20.55508, 41.58113], [20.52103, 41.56473], [20.45809, 41.5549], [20.45331, 41.51436], [20.49039, 41.49277], [20.51301, 41.442], [20.55976, 41.4087], [20.52119, 41.34381], [20.49432, 41.33679], [20.51068, 41.2323], [20.59715, 41.13644], [20.58546, 41.11179], [20.59832, 41.09066], [20.63454, 41.0889], [20.65558, 41.08009], [20.71634, 40.91781], [20.73504, 40.9081], [20.81567, 40.89662], [20.83671, 40.92752], [20.94305, 40.92399], [20.97693, 40.90103], [20.97887, 40.85475], [21.15262, 40.85546], [21.21105, 40.8855], [21.25779, 40.86165], [21.35595, 40.87578], [21.41555, 40.9173], [21.53007, 40.90759], [21.57448, 40.86076], [21.69601, 40.9429], [21.7556, 40.92525], [21.91102, 41.04786], [21.90869, 41.09191], [22.06527, 41.15617], [22.1424, 41.12449], [22.17629, 41.15969], [22.26744, 41.16409], [22.42285, 41.11921], [22.5549, 41.13065], [22.58295, 41.11568], [22.62852, 41.14385], [22.65306, 41.18168], [22.71266, 41.13945], [22.74538, 41.16321], [22.76408, 41.32225], [22.81199, 41.3398], [22.93334, 41.34104], [22.96331, 41.35782], [22.95513, 41.63265], [23.03342, 41.71034], [23.01239, 41.76527], [22.96682, 41.77137], [22.90254, 41.87587], [22.86749, 42.02275], [22.67701, 42.06614], [22.51224, 42.15457], [22.50289, 42.19527], [22.47251, 42.20393], [22.38136, 42.30339], [22.34773, 42.31725]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "ML", + iso1A3: "MLI", + iso1N3: "466", + wikidata: "Q912", + nameEn: "Mali", + groups: ["011", "202", "002", "UN"], + callingCodes: ["223"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-4.83423, 24.99935], [-6.57191, 25.0002], [-5.60725, 16.49919], [-5.33435, 16.33354], [-5.50165, 15.50061], [-9.32979, 15.50032], [-9.31106, 15.69412], [-9.33314, 15.7044], [-9.44673, 15.60553], [-9.40447, 15.4396], [-10.71721, 15.4223], [-10.90932, 15.11001], [-11.43483, 15.62339], [-11.70705, 15.51558], [-11.94903, 14.76143], [-12.23936, 14.76324], [-11.93043, 13.84505], [-12.06897, 13.71049], [-11.83345, 13.33333], [-11.63025, 13.39174], [-11.39935, 12.97808], [-11.37536, 12.40788], [-11.50006, 12.17826], [-11.24136, 12.01286], [-10.99758, 12.24634], [-10.80355, 12.1053], [-10.71897, 11.91552], [-10.30604, 12.24634], [-9.714, 12.0226], [-9.63938, 12.18312], [-9.32097, 12.29009], [-9.38067, 12.48446], [-9.13689, 12.50875], [-8.94784, 12.34842], [-8.80854, 11.66715], [-8.40058, 11.37466], [-8.66923, 10.99397], [-8.35083, 11.06234], [-8.2667, 10.91762], [-8.32614, 10.69273], [-8.22711, 10.41722], [-8.10207, 10.44649], [-7.9578, 10.2703], [-7.97971, 10.17117], [-7.92107, 10.15577], [-7.63048, 10.46334], [-7.54462, 10.40921], [-7.52261, 10.4655], [-7.44555, 10.44602], [-7.3707, 10.24677], [-7.13331, 10.24877], [-7.0603, 10.14711], [-7.00966, 10.15794], [-6.97444, 10.21644], [-7.01186, 10.25111], [-6.93921, 10.35291], [-6.68164, 10.35074], [-6.63541, 10.66893], [-6.52974, 10.59104], [-6.42847, 10.5694], [-6.40646, 10.69922], [-6.325, 10.68624], [-6.24795, 10.74248], [-6.1731, 10.46983], [-6.18851, 10.24244], [-5.99478, 10.19694], [-5.78124, 10.43952], [-5.65135, 10.46767], [-5.51058, 10.43177], [-5.46643, 10.56074], [-5.47083, 10.75329], [-5.41579, 10.84628], [-5.49284, 11.07538], [-5.32994, 11.13371], [-5.32553, 11.21578], [-5.25949, 11.24816], [-5.25509, 11.36905], [-5.20665, 11.43811], [-5.22867, 11.60421], [-5.29251, 11.61715], [-5.26389, 11.75728], [-5.40258, 11.8327], [-5.26389, 11.84778], [-5.07897, 11.97918], [-4.72893, 12.01579], [-4.70692, 12.06746], [-4.62987, 12.06531], [-4.62546, 12.13204], [-4.54841, 12.1385], [-4.57703, 12.19875], [-4.41412, 12.31922], [-4.47356, 12.71252], [-4.238, 12.71467], [-4.21819, 12.95722], [-4.34477, 13.12927], [-3.96501, 13.49778], [-3.90558, 13.44375], [-3.96282, 13.38164], [-3.7911, 13.36665], [-3.54454, 13.1781], [-3.4313, 13.1588], [-3.43507, 13.27272], [-3.23599, 13.29035], [-3.28396, 13.5422], [-3.26407, 13.70699], [-2.88189, 13.64921], [-2.90831, 13.81174], [-2.84667, 14.05532], [-2.66175, 14.14713], [-2.47587, 14.29671], [-2.10223, 14.14878], [-1.9992, 14.19011], [-1.97945, 14.47709], [-1.68083, 14.50023], [-1.32166, 14.72774], [-1.05875, 14.7921], [-0.72004, 15.08655], [-0.24673, 15.07805], [0.06588, 14.96961], [0.23859, 15.00135], [0.72632, 14.95898], [0.96711, 14.98275], [1.31275, 15.27978], [3.01806, 15.34571], [3.03134, 15.42221], [3.50368, 15.35934], [4.19893, 16.39923], [4.21787, 17.00118], [4.26762, 17.00432], [4.26651, 19.14224], [3.36082, 18.9745], [3.12501, 19.1366], [3.24648, 19.81703], [1.20992, 20.73533], [1.15698, 21.12843], [-4.83423, 24.99935]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MM", + iso1A3: "MMR", + iso1N3: "104", + wikidata: "Q836", + nameEn: "Myanmar", + aliases: ["Burma", "BU"], + groups: ["035", "142", "UN"], + callingCodes: ["95"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[92.62187, 21.87037], [92.59775, 21.6092], [92.68152, 21.28454], [92.60187, 21.24615], [92.55105, 21.3856], [92.43158, 21.37025], [92.37939, 21.47764], [92.20087, 21.337], [92.17752, 21.17445], [92.26071, 21.05697], [92.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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MN", + iso1A3: "MNG", + iso1N3: "496", + wikidata: "Q711", + nameEn: "Mongolia", + groups: ["030", "142", "UN"], + callingCodes: ["976"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[102.14032, 51.35566], [101.5044, 51.50467], [101.39085, 51.45753], [100.61116, 51.73028], [99.89203, 51.74903], [99.75578, 51.90108], [99.27888, 51.96876], [98.87768, 52.14563], [98.74142, 51.8637], [98.33222, 51.71832], [98.22053, 51.46579], [98.05257, 51.46696], [97.83305, 51.00248], [98.01472, 50.86652], [97.9693, 50.78044], [98.06393, 50.61262], [98.31373, 50.4996], [98.29481, 50.33561], [97.85197, 49.91339], [97.76871, 49.99861], [97.56432, 49.92801], [97.56811, 49.84265], [97.24639, 49.74737], [96.97388, 49.88413], [95.80056, 50.04239], [95.74757, 49.97915], [95.02465, 49.96941], [94.97166, 50.04725], [94.6121, 50.04239], [94.49477, 50.17832], [94.39258, 50.22193], [94.30823, 50.57498], [92.99595, 50.63183], [93.01109, 50.79001], [92.44714, 50.78762], [92.07173, 50.69585], [91.86048, 50.73734], [89.59711, 49.90851], [89.70687, 49.72535], [88.82499, 49.44808], [88.42449, 49.48821], [88.17223, 49.46934], [88.15543, 49.30314], [87.98977, 49.18147], [87.81333, 49.17354], [87.88171, 48.95853], [87.73822, 48.89582], [88.0788, 48.71436], [87.96361, 48.58478], [88.58939, 48.34531], [88.58316, 48.21893], [88.8011, 48.11302], [88.93186, 48.10263], [89.0711, 47.98528], [89.55453, 48.0423], [89.76624, 47.82745], [90.06512, 47.88177], [90.10871, 47.7375], [90.33598, 47.68303], [90.48854, 47.41826], [90.48542, 47.30438], [90.76108, 46.99399], [90.84035, 46.99525], [91.03649, 46.72916], [91.0147, 46.58171], [91.07696, 46.57315], [90.89639, 46.30711], [90.99672, 46.14207], [91.03026, 46.04194], [90.70907, 45.73437], [90.65114, 45.49314], [90.89169, 45.19667], [91.64048, 45.07408], [93.51161, 44.95964], [94.10003, 44.71016], [94.71959, 44.35284], [95.01191, 44.25274], [95.39772, 44.2805], [95.32891, 44.02407], [95.52594, 43.99353], [95.89543, 43.2528], [96.35658, 42.90363], [96.37926, 42.72055], [97.1777, 42.7964], [99.50671, 42.56535], [100.33297, 42.68231], [100.84979, 42.67087], [101.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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MO", + iso1A3: "MAC", + iso1N3: "446", + wikidata: "Q14773", + nameEn: "Macau", + aliases: ["Macao"], + country: "CN", + groups: ["030", "142", "UN"], + driveSide: "left", + callingCodes: ["853"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[113.54942, 22.14519], [113.54839, 22.10909], [113.57191, 22.07696], [113.63011, 22.10782], [113.60504, 22.20464], [113.57123, 22.20416], [113.56865, 22.20973], [113.5508, 22.21672], [113.54333, 22.21688], [113.54093, 22.21314], [113.53593, 22.2137], [113.53301, 22.21235], [113.53552, 22.20607], [113.52659, 22.18271], [113.54093, 22.15497], [113.54942, 22.14519]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MP", + iso1A3: "MNP", + iso1N3: "580", + wikidata: "Q16644", + nameEn: "Northern Mariana Islands", + aliases: ["US-MP"], + country: "US", + groups: ["Q1352230", "Q153732", "057", "009", "UN"], + roadSpeedUnit: "mph", + callingCodes: ["1 670"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[135.52896, 14.32623], [152.19114, 13.63487], [145.05972, 21.28731], [135.52896, 14.32623]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MQ", + iso1A3: "MTQ", + iso1N3: "474", + wikidata: "Q17054", + nameEn: "Martinique", + country: "FR", + groups: ["Q3320166", "EU", "029", "003", "419", "019", "UN"], + callingCodes: ["596"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-59.95997, 14.20285], [-61.07821, 15.25109], [-61.69315, 14.26451], [-59.95997, 14.20285]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MR", + iso1A3: "MRT", + iso1N3: "478", + wikidata: "Q1025", + nameEn: "Mauritania", + groups: ["011", "202", "002", "UN"], + callingCodes: ["222"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-5.60725, 16.49919], [-6.57191, 25.0002], [-4.83423, 24.99935], [-8.66674, 27.31569], [-8.66721, 25.99918], [-12.0002, 25.9986], [-12.00251, 23.4538], [-12.14969, 23.41935], [-12.36213, 23.3187], [-12.5741, 23.28975], [-13.00412, 23.02297], [-13.10753, 22.89493], [-13.15313, 22.75649], [-13.08438, 22.53866], [-13.01525, 21.33343], [-16.95474, 21.33997], [-16.99806, 21.12142], [-17.0357, 21.05368], [-17.0396, 20.9961], [-17.06781, 20.92697], [-17.0695, 20.85742], [-17.0471, 20.76408], [-17.15288, 16.07139], [-16.50854, 16.09032], [-16.48967, 16.0496], [-16.44814, 16.09753], [-16.4429, 16.20605], [-16.27016, 16.51565], [-15.6509, 16.50315], [-15.00557, 16.64997], [-14.32144, 16.61495], [-13.80075, 16.13961], [-13.43135, 16.09022], [-13.11029, 15.52116], [-12.23936, 14.76324], [-11.94903, 14.76143], [-11.70705, 15.51558], [-11.43483, 15.62339], [-10.90932, 15.11001], [-10.71721, 15.4223], [-9.40447, 15.4396], [-9.44673, 15.60553], [-9.33314, 15.7044], [-9.31106, 15.69412], [-9.32979, 15.50032], [-5.50165, 15.50061], [-5.33435, 16.33354], [-5.60725, 16.49919]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MS", + iso1A3: "MSR", + iso1N3: "500", + wikidata: "Q13353", + nameEn: "Montserrat", + country: "GB", + groups: ["BOTS", "029", "003", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1 664"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-61.91508, 16.51165], [-62.1023, 16.97277], [-62.58307, 16.68909], [-61.91508, 16.51165]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MT", + iso1A3: "MLT", + iso1N3: "470", + wikidata: "Q233", + nameEn: "Malta", + groups: ["EU", "039", "150", "UN"], + driveSide: "left", + callingCodes: ["356"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[15.70991, 35.79901], [14.07544, 36.41525], [13.27636, 35.20764], [15.70991, 35.79901]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MU", + iso1A3: "MUS", + iso1N3: "480", + wikidata: "Q1027", + nameEn: "Mauritius", + groups: ["014", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["230"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[56.09755, -9.55401], [57.50644, -31.92637], [68.4673, -19.15185], [56.09755, -9.55401]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MV", + iso1A3: "MDV", + iso1N3: "462", + wikidata: "Q826", + nameEn: "Maldives", + groups: ["034", "142", "UN"], + driveSide: "left", + callingCodes: ["960"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[71.9161, 8.55531], [72.57428, -3.7623], [76.59015, 5.591], [71.9161, 8.55531]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MW", + iso1A3: "MWI", + iso1N3: "454", + wikidata: "Q1020", + nameEn: "Malawi", + groups: ["014", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["265"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[33.48052, -9.62442], [33.31581, -9.48554], [33.14925, -9.49322], [32.99397, -9.36712], [32.95389, -9.40138], [33.00476, -9.5133], [33.00256, -9.63053], [33.05485, -9.61316], [33.10163, -9.66525], [33.12144, -9.58929], [33.2095, -9.61099], [33.31517, -9.82364], [33.36581, -9.81063], [33.37902, -9.9104], [33.31297, -10.05133], [33.53863, -10.20148], [33.54797, -10.36077], [33.70675, -10.56896], [33.47636, -10.78465], [33.28022, -10.84428], [33.25998, -10.88862], [33.39697, -11.15296], [33.29267, -11.3789], [33.29267, -11.43536], [33.23663, -11.40637], [33.24252, -11.59302], [33.32692, -11.59248], [33.33937, -11.91252], [33.25998, -12.14242], [33.3705, -12.34931], [33.47636, -12.32498], [33.54485, -12.35996], [33.37517, -12.54085], [33.28177, -12.54692], [33.18837, -12.61377], [33.05917, -12.59554], [32.94397, -12.76868], [32.96733, -12.88251], [33.02181, -12.88707], [32.98289, -13.12671], [33.0078, -13.19492], [32.86113, -13.47292], [32.84176, -13.52794], [32.73683, -13.57682], [32.68436, -13.55769], [32.66468, -13.60019], [32.68654, -13.64268], [32.7828, -13.64805], [32.84528, -13.71576], [32.76962, -13.77224], [32.79015, -13.80755], [32.88985, -13.82956], [32.99042, -13.95689], [33.02977, -14.05022], [33.07568, -13.98447], [33.16749, -13.93992], [33.24249, -14.00019], [33.66677, -14.61306], [33.7247, -14.4989], [33.88503, -14.51652], [33.92898, -14.47929], [34.08588, -14.48893], [34.18733, -14.43823], [34.22355, -14.43607], [34.34453, -14.3985], [34.35843, -14.38652], [34.39277, -14.39467], [34.4192, -14.43191], [34.44641, -14.47746], [34.45053, -14.49873], [34.47628, -14.53363], [34.48932, -14.53646], [34.49636, -14.55091], [34.52366, -14.5667], [34.53962, -14.59776], [34.55112, -14.64494], [34.53516, -14.67782], [34.52057, -14.68263], [34.54503, -14.74672], [34.567, -14.77345], [34.61522, -14.99583], [34.57503, -15.30619], [34.43126, -15.44778], [34.44981, -15.60864], [34.25195, -15.90321], [34.43126, -16.04737], [34.40344, -16.20923], [35.04805, -16.83167], [35.13771, -16.81687], [35.17017, -16.93521], [35.04805, -17.00027], [35.0923, -17.13235], [35.3062, -17.1244], [35.27065, -16.93817], [35.30929, -16.82871], [35.27219, -16.69402], [35.14235, -16.56812], [35.25828, -16.4792], [35.30157, -16.2211], [35.43355, -16.11371], [35.52365, -16.15414], [35.70107, -16.10147], [35.80487, -16.03907], [35.85303, -15.41913], [35.78799, -15.17428], [35.91812, -14.89514], [35.87212, -14.89478], [35.86945, -14.67481], [35.5299, -14.27714], [35.47989, -14.15594], [34.86229, -13.48958], [34.60253, -13.48487], [34.37831, -12.17408], [34.46088, -12.0174], [34.70739, -12.15652], [34.82903, -12.04837], [34.57917, -11.87849], [34.64241, -11.57499], [34.96296, -11.57354], [34.91153, -11.39799], [34.79375, -11.32245], [34.63305, -11.11731], [34.61161, -11.01611], [34.67047, -10.93796], [34.65946, -10.6828], [34.57581, -10.56271], [34.51911, -10.12279], [34.54499, -10.0678], [34.03865, -9.49398], [33.95829, -9.54066], [33.9638, -9.62206], [33.93298, -9.71647], [33.76677, -9.58516], [33.48052, -9.62442]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MX", + iso1A3: "MEX", + iso1N3: "484", + wikidata: "Q96", + nameEn: "Mexico", + groups: ["013", "003", "419", "019", "UN"], + callingCodes: ["52"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-117.1243, 32.53427], [-118.48109, 32.5991], [-120.12904, 18.41089], [-92.37213, 14.39277], [-92.2261, 14.53423], [-92.1454, 14.6804], [-92.18161, 14.84147], [-92.1423, 14.88647], [-92.1454, 14.98143], [-92.0621, 15.07406], [-92.20983, 15.26077], [-91.73182, 16.07371], [-90.44567, 16.07573], [-90.40499, 16.40524], [-90.61212, 16.49832], [-90.69064, 16.70697], [-91.04436, 16.92175], [-91.43809, 17.25373], [-90.99199, 17.25192], [-90.98678, 17.81655], [-89.14985, 17.81563], [-89.15105, 17.95104], [-89.03839, 18.0067], [-88.8716, 17.89535], [-88.71505, 18.0707], [-88.48242, 18.49164], [-88.3268, 18.49048], [-88.29909, 18.47591], [-88.26593, 18.47617], [-88.03238, 18.41778], [-88.03165, 18.16657], [-87.90671, 18.15213], [-87.87604, 18.18313], [-87.86657, 18.19971], [-87.85693, 18.18266], [-87.84815, 18.18511], [-86.92368, 17.61462], [-85.9092, 21.8218], [-96.92418, 25.97377], [-97.13927, 25.96583], [-97.35946, 25.92189], [-97.37332, 25.83854], [-97.42511, 25.83969], [-97.45669, 25.86874], [-97.49828, 25.89877], [-97.52025, 25.88518], [-97.66511, 26.01708], [-97.95155, 26.0625], [-97.97017, 26.05232], [-98.24603, 26.07191], [-98.27075, 26.09457], [-98.30491, 26.10475], [-98.35126, 26.15129], [-99.00546, 26.3925], [-99.03053, 26.41249], [-99.08477, 26.39849], [-99.53573, 27.30926], [-99.49744, 27.43746], [-99.482, 27.47128], [-99.48045, 27.49016], [-99.50208, 27.50021], [-99.52955, 27.49747], [-99.51478, 27.55836], [-99.55409, 27.61314], [-100.50029, 28.66117], [-100.51222, 28.70679], [-100.5075, 28.74066], [-100.52313, 28.75598], [-100.59809, 28.88197], [-100.63689, 28.90812], [-100.67294, 29.09744], [-100.79696, 29.24688], [-100.87982, 29.296], [-100.94056, 29.33371], [-100.94579, 29.34523], [-100.96725, 29.3477], [-101.01128, 29.36947], [-101.05686, 29.44738], [-101.47277, 29.7744], [-102.60596, 29.8192], [-103.15787, 28.93865], [-104.37752, 29.54255], [-104.39363, 29.55396], [-104.3969, 29.57105], [-104.5171, 29.64671], [-104.77674, 30.4236], [-106.00363, 31.39181], [-106.09025, 31.40569], [-106.20346, 31.46305], [-106.23711, 31.51262], [-106.24612, 31.54193], [-106.28084, 31.56173], [-106.30305, 31.62154], [-106.33419, 31.66303], [-106.34864, 31.69663], [-106.3718, 31.71165], [-106.38003, 31.73151], [-106.41773, 31.75196], [-106.43419, 31.75478], [-106.45244, 31.76523], [-106.46726, 31.75998], [-106.47298, 31.75054], [-106.48815, 31.74769], [-106.50111, 31.75714], [-106.50962, 31.76155], [-106.51251, 31.76922], [-106.52266, 31.77509], [-106.529, 31.784], [-108.20899, 31.78534], [-108.20979, 31.33316], [-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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "MY", + iso1A3: "MYS", + iso1N3: "458", + wikidata: "Q833", + nameEn: "Malaysia" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "MZ", + iso1A3: "MOZ", + iso1N3: "508", + wikidata: "Q1029", + nameEn: "Mozambique", + groups: ["014", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["258"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[40.74206, -10.25691], [40.44265, -10.4618], [40.00295, -10.80255], [39.58249, -10.96043], [39.24395, -11.17433], [38.88996, -11.16978], [38.47258, -11.4199], [38.21598, -11.27289], [37.93618, -11.26228], [37.8388, -11.3123], [37.76614, -11.53352], [37.3936, -11.68949], [36.80309, -11.56836], [36.62068, -11.72884], [36.19094, -11.70008], [36.19094, -11.57593], [35.82767, -11.41081], [35.63599, -11.55927], [34.96296, -11.57354], [34.64241, -11.57499], [34.57917, -11.87849], [34.82903, -12.04837], [34.70739, -12.15652], [34.46088, -12.0174], [34.37831, -12.17408], [34.60253, -13.48487], [34.86229, -13.48958], [35.47989, -14.15594], [35.5299, -14.27714], [35.86945, -14.67481], [35.87212, -14.89478], [35.91812, -14.89514], [35.78799, -15.17428], [35.85303, -15.41913], [35.80487, -16.03907], [35.70107, -16.10147], [35.52365, -16.15414], [35.43355, -16.11371], [35.30157, -16.2211], [35.25828, -16.4792], [35.14235, -16.56812], [35.27219, -16.69402], [35.30929, -16.82871], [35.27065, -16.93817], [35.3062, -17.1244], [35.0923, -17.13235], [35.04805, -17.00027], [35.17017, -16.93521], [35.13771, -16.81687], [35.04805, -16.83167], [34.40344, -16.20923], [34.43126, -16.04737], [34.25195, -15.90321], [34.44981, -15.60864], [34.43126, -15.44778], [34.57503, -15.30619], [34.61522, -14.99583], [34.567, -14.77345], [34.54503, -14.74672], [34.52057, -14.68263], [34.53516, -14.67782], [34.55112, -14.64494], [34.53962, -14.59776], [34.52366, -14.5667], [34.49636, -14.55091], [34.48932, -14.53646], [34.47628, -14.53363], [34.45053, -14.49873], [34.44641, -14.47746], [34.4192, -14.43191], [34.39277, -14.39467], [34.35843, -14.38652], [34.34453, -14.3985], [34.22355, -14.43607], [34.18733, -14.43823], [34.08588, -14.48893], [33.92898, -14.47929], [33.88503, -14.51652], [33.7247, -14.4989], [33.66677, -14.61306], [33.24249, -14.00019], [30.22098, -14.99447], [30.41902, -15.62269], [30.42568, -15.9962], [30.91597, -15.99924], [30.97761, -16.05848], [31.13171, -15.98019], [31.30563, -16.01193], [31.42451, -16.15154], [31.67988, -16.19595], [31.90223, -16.34388], [31.91324, -16.41569], [32.02772, -16.43892], [32.28529, -16.43892], [32.42838, -16.4727], [32.71017, -16.59932], [32.69917, -16.66893], [32.78943, -16.70267], [32.97655, -16.70689], [32.91051, -16.89446], [32.84113, -16.92259], [32.96554, -17.11971], [33.00517, -17.30477], [33.0426, -17.3468], [32.96554, -17.48964], [32.98536, -17.55891], [33.0492, -17.60298], [32.94133, -17.99705], [33.03159, -18.35054], [33.02278, -18.4696], [32.88629, -18.51344], [32.88629, -18.58023], [32.95013, -18.69079], [32.9017, -18.7992], [32.82465, -18.77419], [32.70137, -18.84712], [32.73439, -18.92628], [32.69917, -18.94293], [32.72118, -19.02204], [32.84006, -19.0262], [32.87088, -19.09279], [32.85107, -19.29238], [32.77966, -19.36098], [32.78282, -19.47513], [32.84446, -19.48343], [32.84666, -19.68462], [32.95013, -19.67219], [33.06461, -19.77787], [33.01178, -20.02007], [32.93032, -20.03868], [32.85987, -20.16686], [32.85987, -20.27841], [32.66174, -20.56106], [32.55167, -20.56312], [32.48122, -20.63319], [32.51644, -20.91929], [32.37115, -21.133], [32.48236, -21.32873], [32.41234, -21.31246], [31.38336, -22.36919], [31.30611, -22.422], [31.55779, -23.176], [31.56539, -23.47268], [31.67942, -23.60858], [31.70223, -23.72695], [31.77445, -23.90082], [31.87707, -23.95293], [31.90368, -24.18892], [31.9835, -24.29983], [32.03196, -25.10785], [32.01676, -25.38117], [31.97875, -25.46356], [32.00631, -25.65044], [31.92649, -25.84216], [31.974, -25.95387], [32.00916, -25.999], [32.08599, -26.00978], [32.10435, -26.15656], [32.07352, -26.40185], [32.13409, -26.5317], [32.13315, -26.84345], [32.19409, -26.84032], [32.22302, -26.84136], [32.29584, -26.852], [32.35222, -26.86027], [34.51034, -26.91792], [42.99868, -12.65261], [40.74206, -10.25691]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "NA", + iso1A3: "NAM", + iso1N3: "516", + wikidata: "Q1030", + nameEn: "Namibia", + groups: ["018", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["264"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[14.28743, -17.38814], [13.95896, -17.43141], [13.36212, -16.98048], [12.97145, -16.98567], [12.52111, -17.24495], [12.07076, -17.15165], [11.75063, -17.25013], [10.5065, -17.25284], [12.51595, -32.27486], [16.45332, -28.63117], [16.46592, -28.57126], [16.59922, -28.53246], [16.90446, -28.057], [17.15405, -28.08573], [17.4579, -28.68718], [18.99885, -28.89165], [19.99882, -28.42622], [19.99817, -24.76768], [19.99912, -21.99991], [20.99751, -22.00026], [20.99904, -18.31743], [21.45556, -18.31795], [23.0996, -18.00075], [23.29618, -17.99855], [23.61088, -18.4881], [24.19416, -18.01919], [24.40577, -17.95726], [24.57485, -18.07151], [24.6303, -17.9863], [24.71887, -17.9218], [24.73364, -17.89338], [24.95586, -17.79674], [25.05895, -17.84452], [25.16882, -17.78253], [25.26433, -17.79571], [25.00198, -17.58221], [24.70864, -17.49501], [24.5621, -17.52963], [24.38712, -17.46818], [24.32811, -17.49082], [24.23619, -17.47489], [23.47474, -17.62877], [21.42741, -18.02787], [21.14283, -17.94318], [18.84226, -17.80375], [18.39229, -17.38927], [14.28743, -17.38814]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "NC", + iso1A3: "NCL", + iso1N3: "540", + wikidata: "Q33788", + nameEn: "New Caledonia", + country: "FR", + groups: ["Q1451600", "054", "009", "UN"], + callingCodes: ["687"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[159.77159, -28.41151], [174.245, -23.1974], [156.73836, -14.50464], [159.77159, -28.41151]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "NE", + iso1A3: "NER", + iso1N3: "562", + wikidata: "Q1032", + nameEn: "Niger", + aliases: ["RN"], + groups: ["011", "202", "002", "UN"], + callingCodes: ["227"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[14.22918, 22.61719], [13.5631, 23.16574], [11.96886, 23.51735], [7.48273, 20.87258], [7.38361, 20.79165], [5.8153, 19.45101], [4.26651, 19.14224], [4.26762, 17.00432], [4.21787, 17.00118], [4.19893, 16.39923], [3.50368, 15.35934], [3.03134, 15.42221], [3.01806, 15.34571], [1.31275, 15.27978], [0.96711, 14.98275], [0.72632, 14.95898], [0.23859, 15.00135], [0.16936, 14.51654], [0.38051, 14.05575], [0.61924, 13.68491], [0.77377, 13.6866], [0.77637, 13.64442], [0.99514, 13.5668], [1.02813, 13.46635], [1.20088, 13.38951], [1.24429, 13.39373], [1.28509, 13.35488], [1.24516, 13.33968], [1.21217, 13.37853], [1.18873, 13.31771], [0.99253, 13.37515], [0.99167, 13.10727], [2.26349, 12.41915], [2.05785, 12.35539], [2.39723, 11.89473], [2.45824, 11.98672], [2.39657, 12.10952], [2.37783, 12.24804], [2.6593, 12.30631], [2.83978, 12.40585], [3.25352, 12.01467], [3.31613, 11.88495], [3.48187, 11.86092], [3.59375, 11.70269], [3.61075, 11.69181], [3.67988, 11.75429], [3.67122, 11.80865], [3.63063, 11.83042], [3.61955, 11.91847], [3.67775, 11.97599], [3.63136, 12.11826], [3.66364, 12.25884], [3.65111, 12.52223], [3.94339, 12.74979], [4.10006, 12.98862], [4.14367, 13.17189], [4.14186, 13.47586], [4.23456, 13.47725], [4.4668, 13.68286], [4.87425, 13.78], [4.9368, 13.7345], [5.07396, 13.75052], [5.21026, 13.73627], [5.27797, 13.75474], [5.35437, 13.83567], [5.52957, 13.8845], [6.15771, 13.64564], [6.27411, 13.67835], [6.43053, 13.6006], [6.69617, 13.34057], [6.94445, 12.99825], [7.0521, 13.00076], [7.12676, 13.02445], [7.22399, 13.1293], [7.39241, 13.09717], [7.81085, 13.34902], [8.07997, 13.30847], [8.25185, 13.20369], [8.41853, 13.06166], [8.49493, 13.07519], [8.60431, 13.01768], [8.64251, 12.93985], [8.97413, 12.83661], [9.65995, 12.80614], [10.00373, 13.18171], [10.19993, 13.27129], [10.46731, 13.28819], [10.66004, 13.36422], [11.4535, 13.37773], [11.88236, 13.2527], [12.04209, 13.14452], [12.16189, 13.10056], [12.19315, 13.12423], [12.47095, 13.06673], [12.58033, 13.27805], [12.6793, 13.29157], [12.87376, 13.48919], [13.05085, 13.53984], [13.19844, 13.52802], [13.33213, 13.71195], [13.6302, 13.71094], [13.47559, 14.40881], [13.48259, 14.46704], [13.68573, 14.55276], [13.67878, 14.64013], [13.809, 14.72915], [13.78991, 14.87519], [13.86301, 15.04043], [14.37425, 15.72591], [15.50373, 16.89649], [15.6032, 18.77402], [15.75098, 19.93002], [15.99632, 20.35364], [15.6721, 20.70069], [15.59841, 20.74039], [15.56004, 20.79488], [15.55382, 20.86507], [15.57248, 20.92138], [15.62515, 20.95395], [15.28332, 21.44557], [15.20213, 21.49365], [15.19692, 21.99339], [14.99751, 23.00539], [14.22918, 22.61719]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "NF", + iso1A3: "NFK", + iso1N3: "574", + wikidata: "Q31057", + nameEn: "Norfolk Island", + country: "AU", + groups: ["053", "009", "UN"], + driveSide: "left", + callingCodes: ["672 3"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[169.82316, -28.16667], [166.29505, -28.29175], [167.94076, -30.60745], [169.82316, -28.16667]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "NG", + iso1A3: "NGA", + iso1N3: "566", + wikidata: "Q1033", + nameEn: "Nigeria", + groups: ["011", "202", "002", "UN"], + callingCodes: ["234"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[6.15771, 13.64564], [5.52957, 13.8845], [5.35437, 13.83567], [5.27797, 13.75474], [5.21026, 13.73627], [5.07396, 13.75052], [4.9368, 13.7345], [4.87425, 13.78], [4.4668, 13.68286], [4.23456, 13.47725], [4.14186, 13.47586], [4.14367, 13.17189], [4.10006, 12.98862], [3.94339, 12.74979], [3.65111, 12.52223], [3.66364, 12.25884], [3.63136, 12.11826], [3.67775, 11.97599], [3.61955, 11.91847], [3.63063, 11.83042], [3.67122, 11.80865], [3.67988, 11.75429], [3.61075, 11.69181], [3.59375, 11.70269], [3.49175, 11.29765], [3.71505, 11.13015], [3.84243, 10.59316], [3.78292, 10.40538], [3.6844, 10.46351], [3.57275, 10.27185], [3.66908, 10.18136], [3.54429, 9.87739], [3.35383, 9.83641], [3.32099, 9.78032], [3.34726, 9.70696], [3.25093, 9.61632], [3.13928, 9.47167], [3.14147, 9.28375], [3.08017, 9.10006], [2.77907, 9.06924], [2.67523, 7.87825], [2.73095, 7.7755], [2.73405, 7.5423], [2.78668, 7.5116], [2.79442, 7.43486], [2.74489, 7.42565], [2.76965, 7.13543], [2.71702, 6.95722], [2.74024, 6.92802], [2.73405, 6.78508], [2.78823, 6.76356], [2.78204, 6.70514], [2.7325, 6.64057], [2.74334, 6.57291], [2.70464, 6.50831], [2.70566, 6.38038], [2.74181, 6.13349], [5.87055, 3.78489], [8.34397, 4.30689], [8.60302, 4.87353], [8.78027, 5.1243], [8.92029, 5.58403], [8.83687, 5.68483], [8.88156, 5.78857], [8.84209, 5.82562], [9.51757, 6.43874], [9.70674, 6.51717], [9.77824, 6.79088], [9.86314, 6.77756], [10.15135, 7.03781], [10.21466, 6.88996], [10.53639, 6.93432], [10.57214, 7.16345], [10.59746, 7.14719], [10.60789, 7.06885], [10.83727, 6.9358], [10.8179, 6.83377], [10.94302, 6.69325], [11.09644, 6.68437], [11.09495, 6.51717], [11.42041, 6.53789], [11.42264, 6.5882], [11.51499, 6.60892], [11.57755, 6.74059], [11.55818, 6.86186], [11.63117, 6.9905], [11.87396, 7.09398], [11.84864, 7.26098], [11.93205, 7.47812], [12.01844, 7.52981], [11.99908, 7.67302], [12.20909, 7.97553], [12.19271, 8.10826], [12.24782, 8.17904], [12.26123, 8.43696], [12.4489, 8.52536], [12.44146, 8.6152], [12.68722, 8.65938], [12.71701, 8.7595], [12.79, 8.75361], [12.81085, 8.91992], [12.90022, 9.11411], [12.91958, 9.33905], [12.85628, 9.36698], [13.02385, 9.49334], [13.22642, 9.57266], [13.25472, 9.76795], [13.29941, 9.8296], [13.25025, 9.86042], [13.24132, 9.91031], [13.27409, 9.93232], [13.286, 9.9822], [13.25323, 10.00127], [13.25025, 10.03647], [13.34111, 10.12299], [13.43644, 10.13326], [13.5705, 10.53183], [13.54964, 10.61236], [13.73434, 10.9255], [13.70753, 10.94451], [13.7403, 11.00593], [13.78945, 11.00154], [13.97489, 11.30258], [14.17821, 11.23831], [14.6124, 11.51283], [14.64591, 11.66166], [14.55207, 11.72001], [14.61612, 11.7798], [14.6474, 12.17466], [14.4843, 12.35223], [14.22215, 12.36533], [14.17523, 12.41916], [14.20204, 12.53405], [14.08251, 13.0797], [13.6302, 13.71094], [13.33213, 13.71195], [13.19844, 13.52802], [13.05085, 13.53984], [12.87376, 13.48919], [12.6793, 13.29157], [12.58033, 13.27805], [12.47095, 13.06673], [12.19315, 13.12423], [12.16189, 13.10056], [12.04209, 13.14452], [11.88236, 13.2527], [11.4535, 13.37773], [10.66004, 13.36422], [10.46731, 13.28819], [10.19993, 13.27129], [10.00373, 13.18171], [9.65995, 12.80614], [8.97413, 12.83661], [8.64251, 12.93985], [8.60431, 13.01768], [8.49493, 13.07519], [8.41853, 13.06166], [8.25185, 13.20369], [8.07997, 13.30847], [7.81085, 13.34902], [7.39241, 13.09717], [7.22399, 13.1293], [7.12676, 13.02445], [7.0521, 13.00076], [6.94445, 12.99825], [6.69617, 13.34057], [6.43053, 13.6006], [6.27411, 13.67835], [6.15771, 13.64564]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "NI", + iso1A3: "NIC", + iso1N3: "558", + wikidata: "Q811", + nameEn: "Nicaragua", + groups: ["013", "003", "419", "019", "UN"], + callingCodes: ["505"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-83.13724, 15.00002], [-83.49268, 15.01158], [-83.62101, 14.89448], [-83.89551, 14.76697], [-84.10584, 14.76353], [-84.48373, 14.63249], [-84.70119, 14.68078], [-84.82596, 14.82212], [-84.90082, 14.80489], [-85.1575, 14.53934], [-85.18602, 14.24929], [-85.32149, 14.2562], [-85.45762, 14.11304], [-85.73964, 13.9698], [-85.75477, 13.8499], [-86.03458, 13.99181], [-86.00685, 14.08474], [-86.14801, 14.04317], [-86.35219, 13.77157], [-86.76812, 13.79605], [-86.71267, 13.30348], [-86.87066, 13.30641], [-86.93383, 13.18677], [-86.93197, 13.05313], [-87.03785, 12.98682], [-87.06306, 13.00892], [-87.37107, 12.98646], [-87.55124, 13.12523], [-87.7346, 13.13228], [-88.11443, 12.63306], [-86.14524, 11.09059], [-85.71223, 11.06868], [-85.60529, 11.22607], [-84.92439, 10.9497], [-84.68197, 11.07568], [-83.90838, 10.71161], [-83.66597, 10.79916], [-83.68276, 11.01562], [-82.56142, 11.91792], [-82.06974, 14.49418], [-83.04763, 15.03256], [-83.13724, 15.00002]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "NL", + iso1A3: "NLD", + iso1N3: "528", + wikidata: "Q29999", + nameEn: "Kingdom of the Netherlands" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "NO", + iso1A3: "NOR", + iso1N3: "578", + wikidata: "Q20", + nameEn: "Norway" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "NP", + iso1A3: "NPL", + iso1N3: "524", + wikidata: "Q837", + nameEn: "Nepal", + groups: ["034", "142", "UN"], + driveSide: "left", + callingCodes: ["977"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[88.13378, 27.88015], [87.82681, 27.95248], [87.72718, 27.80938], [87.56996, 27.84517], [87.11696, 27.84104], [87.03757, 27.94835], [86.75582, 28.04182], [86.74181, 28.10638], [86.56265, 28.09569], [86.51609, 27.96623], [86.42736, 27.91122], [86.22966, 27.9786], [86.18607, 28.17364], [86.088, 28.09264], [86.08333, 28.02121], [86.12069, 27.93047], [86.06309, 27.90021], [85.94946, 27.9401], [85.97813, 27.99023], [85.90743, 28.05144], [85.84672, 28.18187], [85.74864, 28.23126], [85.71907, 28.38064], [85.69105, 28.38475], [85.60854, 28.25045], [85.59765, 28.30529], [85.4233, 28.32996], [85.38127, 28.28336], [85.10729, 28.34092], [85.18668, 28.54076], [85.19135, 28.62825], [85.06059, 28.68562], [84.85511, 28.58041], [84.62317, 28.73887], [84.47528, 28.74023], [84.2231, 28.89571], [84.24801, 29.02783], [84.18107, 29.23451], [83.97559, 29.33091], [83.82303, 29.30513], [83.63156, 29.16249], [83.44787, 29.30513], [83.28131, 29.56813], [83.07116, 29.61957], [82.73024, 29.81695], [82.5341, 29.9735], [82.38622, 30.02608], [82.16984, 30.0692], [82.19475, 30.16884], [82.10757, 30.23745], [82.10135, 30.35439], [81.99082, 30.33423], [81.62033, 30.44703], [81.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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "NR", + iso1A3: "NRU", + iso1N3: "520", + wikidata: "Q697", + nameEn: "Nauru", + groups: ["057", "009", "UN"], + driveSide: "left", + callingCodes: ["674"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[166.95155, 0.14829], [166.21778, -0.7977], [167.60042, -0.88259], [166.95155, 0.14829]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "NU", + iso1A3: "NIU", + iso1N3: "570", + wikidata: "Q34020", + nameEn: "Niue", + country: "NZ", + groups: ["061", "009", "UN"], + driveSide: "left", + callingCodes: ["683"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-170.83899, -18.53439], [-170.82274, -20.44429], [-168.63096, -18.60489], [-170.83899, -18.53439]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "NZ", + iso1A3: "NZL", + iso1N3: "554", + wikidata: "Q664", + nameEn: "New Zealand" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "OM", + iso1A3: "OMN", + iso1N3: "512", + wikidata: "Q842", + nameEn: "Oman", + groups: ["145", "142", "UN"], + callingCodes: ["968"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[56.82555, 25.7713], [56.79239, 26.41236], [56.68954, 26.76645], [56.2644, 26.58649], [55.81777, 26.18798], [56.08666, 26.05038], [56.15498, 26.06828], [56.19334, 25.9795], [56.13963, 25.82765], [56.17416, 25.77239], [56.13579, 25.73524], [56.14826, 25.66351], [56.18363, 25.65508], [56.20473, 25.61119], [56.25365, 25.60211], [56.26636, 25.60643], [56.25341, 25.61443], [56.26534, 25.62825], [56.82555, 25.7713]]], [[[56.26062, 25.33108], [56.23362, 25.31253], [56.25008, 25.28843], [56.24465, 25.27505], [56.20838, 25.25668], [56.20872, 25.24104], [56.24341, 25.22867], [56.27628, 25.23404], [56.34438, 25.26653], [56.35172, 25.30681], [56.3111, 25.30107], [56.3005, 25.31815], [56.26062, 25.33108]], [[56.28423, 25.26344], [56.27086, 25.26128], [56.2716, 25.27916], [56.28102, 25.28486], [56.29379, 25.2754], [56.28423, 25.26344]]], [[[61.45114, 22.55394], [56.86325, 25.03856], [56.3227, 24.97284], [56.34873, 24.93205], [56.30269, 24.88334], [56.20568, 24.85063], [56.20062, 24.78565], [56.13684, 24.73699], [56.06128, 24.74457], [56.03535, 24.81161], [55.97836, 24.87673], [55.97467, 24.89639], [56.05106, 24.87461], [56.05715, 24.95727], [55.96316, 25.00857], [55.90849, 24.96771], [55.85094, 24.96858], [55.81116, 24.9116], [55.81348, 24.80102], [55.83408, 24.77858], [55.83271, 24.68567], [55.76461, 24.5287], [55.83271, 24.41521], [55.83395, 24.32776], [55.80747, 24.31069], [55.79145, 24.27914], [55.76781, 24.26209], [55.75939, 24.26114], [55.75382, 24.2466], [55.75257, 24.23466], [55.76558, 24.23227], [55.77658, 24.23476], [55.83367, 24.20193], [55.95472, 24.2172], [56.01799, 24.07426], [55.8308, 24.01633], [55.73301, 24.05994], [55.48677, 23.94946], [55.57358, 23.669], [55.22634, 23.10378], [55.2137, 22.71065], [55.66469, 21.99658], [54.99756, 20.00083], [52.00311, 19.00083], [52.78009, 17.35124], [52.74267, 17.29519], [52.81185, 17.28568], [57.49095, 8.14549], [61.45114, 22.55394]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "PA", + iso1A3: "PAN", + iso1N3: "591", + wikidata: "Q804", + nameEn: "Panama", + groups: ["013", "003", "419", "019", "UN"], + callingCodes: ["507"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-77.32389, 8.81247], [-77.58292, 9.22278], [-78.79327, 9.93766], [-82.51044, 9.65379], [-82.56507, 9.57279], [-82.61345, 9.49881], [-82.66667, 9.49746], [-82.77206, 9.59573], [-82.87919, 9.62645], [-82.84871, 9.4973], [-82.93516, 9.46741], [-82.93516, 9.07687], [-82.72126, 8.97125], [-82.88253, 8.83331], [-82.91377, 8.774], [-82.92068, 8.74832], [-82.8794, 8.6981], [-82.82739, 8.60153], [-82.83975, 8.54755], [-82.83322, 8.52464], [-82.8382, 8.48117], [-82.8679, 8.44042], [-82.93056, 8.43465], [-83.05209, 8.33394], [-82.9388, 8.26634], [-82.88641, 8.10219], [-82.89137, 8.05755], [-82.89978, 8.04083], [-82.94503, 7.93865], [-82.13751, 6.97312], [-78.06168, 7.07793], [-77.89178, 7.22681], [-77.81426, 7.48319], [-77.72157, 7.47612], [-77.72514, 7.72348], [-77.57185, 7.51147], [-77.17257, 7.97422], [-77.45064, 8.49991], [-77.32389, 8.81247]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "PE", + iso1A3: "PER", + iso1N3: "604", + wikidata: "Q419", + nameEn: "Peru", + groups: ["005", "419", "019", "UN"], + callingCodes: ["51"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-74.26675, -0.97229], [-74.42701, -0.50218], [-75.18513, -0.0308], [-75.25764, -0.11943], [-75.40192, -0.17196], [-75.61997, -0.10012], [-75.60169, -0.18708], [-75.53615, -0.19213], [-75.22862, -0.60048], [-75.22862, -0.95588], [-75.3872, -0.9374], [-75.57429, -1.55961], [-76.05203, -2.12179], [-76.6324, -2.58397], [-77.94147, -3.05454], [-78.19369, -3.36431], [-78.14324, -3.47653], [-78.22642, -3.51113], [-78.24589, -3.39907], [-78.34362, -3.38633], [-78.68394, -4.60754], [-78.85149, -4.66795], [-79.01659, -5.01481], [-79.1162, -4.97774], [-79.26248, -4.95167], [-79.59402, -4.46848], [-79.79722, -4.47558], [-80.13945, -4.29786], [-80.39256, -4.48269], [-80.46386, -4.41516], [-80.32114, -4.21323], [-80.45023, -4.20938], [-80.4822, -4.05477], [-80.46386, -4.01342], [-80.13232, -3.90317], [-80.19926, -3.68894], [-80.18741, -3.63994], [-80.19848, -3.59249], [-80.21642, -3.5888], [-80.20535, -3.51667], [-80.22629, -3.501], [-80.23651, -3.48652], [-80.24586, -3.48677], [-80.24123, -3.46124], [-80.20647, -3.431], [-80.30602, -3.39149], [-84.52388, -3.36941], [-85.71054, -21.15413], [-70.59118, -18.35072], [-70.378, -18.3495], [-70.31267, -18.31258], [-70.16394, -18.31737], [-69.96732, -18.25992], [-69.81607, -18.12582], [-69.75305, -17.94605], [-69.82868, -17.72048], [-69.79087, -17.65563], [-69.66483, -17.65083], [-69.46897, -17.4988], [-69.46863, -17.37466], [-69.62883, -17.28142], [-69.16896, -16.72233], [-69.00853, -16.66769], [-69.04027, -16.57214], [-68.98358, -16.42165], [-68.79464, -16.33272], [-68.96238, -16.194], [-69.09986, -16.22693], [-69.20291, -16.16668], [-69.40336, -15.61358], [-69.14856, -15.23478], [-69.36254, -14.94634], [-68.88135, -14.18639], [-69.05265, -13.68546], [-68.8864, -13.40792], [-68.85615, -12.87769], [-68.65044, -12.50689], [-68.98115, -11.8979], [-69.57156, -10.94555], [-69.57835, -10.94051], [-69.90896, -10.92744], [-70.38791, -11.07096], [-70.51395, -10.92249], [-70.64134, -11.0108], [-70.62487, -9.80666], [-70.55429, -9.76692], [-70.58453, -9.58303], [-70.53373, -9.42628], [-71.23394, -9.9668], [-72.14742, -9.98049], [-72.31883, -9.5184], [-72.72216, -9.41397], [-73.21498, -9.40904], [-72.92886, -9.04074], [-73.76576, -7.89884], [-73.65485, -7.77897], [-73.96938, -7.58465], [-73.77011, -7.28944], [-73.73986, -6.87919], [-73.12983, -6.43852], [-73.24579, -6.05764], [-72.83973, -5.14765], [-72.64391, -5.0391], [-71.87003, -4.51661], [-70.96814, -4.36915], [-70.77601, -4.15717], [-70.33236, -4.15214], [-70.19582, -4.3607], [-70.11305, -4.27281], [-70.00888, -4.37833], [-69.94708, -4.2431], [-70.3374, -3.79505], [-70.52393, -3.87553], [-70.71396, -3.7921], [-70.04609, -2.73906], [-70.94377, -2.23142], [-71.75223, -2.15058], [-72.92587, -2.44514], [-73.65312, -1.26222], [-74.26675, -0.97229]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "PF", + iso1A3: "PYF", + iso1N3: "258", + wikidata: "Q30971", + nameEn: "French Polynesia", + country: "FR", + groups: ["Q1451600", "061", "009", "UN"], + callingCodes: ["689"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-135.59706, -4.70473], [-156.48634, -15.52824], [-156.45576, -31.75456], [-133.59543, -28.4709], [-135.59706, -4.70473]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "PG", + iso1A3: "PNG", + iso1N3: "598", + wikidata: "Q691", + nameEn: "Papua New Guinea", + groups: ["054", "009", "UN"], + driveSide: "left", + callingCodes: ["675"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "PH", + iso1A3: "PHL", + iso1N3: "608", + wikidata: "Q928", + nameEn: "Philippines", + aliases: ["PI", "RP"], + groups: ["035", "142", "UN"], + callingCodes: ["63"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[129.19694, 7.84182], [121.8109, 21.77688], [120.69238, 21.52331], [118.82252, 14.67191], [115.39742, 10.92666], [116.79524, 7.43869], [117.17735, 7.52841], [117.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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "PK", + iso1A3: "PAK", + iso1N3: "586", + wikidata: "Q843", + nameEn: "Pakistan", + groups: ["034", "142", "UN"], + driveSide: "left", + callingCodes: ["92"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[75.72737, 36.7529], [75.45562, 36.71971], [75.40481, 36.95382], [75.13839, 37.02622], [74.56453, 37.03023], [74.53739, 36.96224], [74.43389, 37.00977], [74.04856, 36.82648], [73.82685, 36.91421], [72.6323, 36.84601], [72.18135, 36.71838], [71.80267, 36.49924], [71.60491, 36.39429], [71.19505, 36.04134], [71.37969, 35.95865], [71.55273, 35.71483], [71.49917, 35.6267], [71.65435, 35.4479], [71.54294, 35.31037], [71.5541, 35.28776], [71.67495, 35.21262], [71.52938, 35.09023], [71.55273, 35.02615], [71.49917, 35.00478], [71.50329, 34.97328], [71.29472, 34.87728], [71.28356, 34.80882], [71.08718, 34.69034], [71.11602, 34.63047], [71.0089, 34.54568], [71.02401, 34.44835], [71.17662, 34.36769], [71.12815, 34.26619], [71.13078, 34.16503], [71.09453, 34.13524], [71.09307, 34.11961], [71.06933, 34.10564], [71.07345, 34.06242], [70.88119, 33.97933], [70.54336, 33.9463], [69.90203, 34.04194], [69.87307, 33.9689], [69.85671, 33.93719], [70.00503, 33.73528], [70.14236, 33.71701], [70.14785, 33.6553], [70.20141, 33.64387], [70.17062, 33.53535], [70.32775, 33.34496], [70.13686, 33.21064], [70.07369, 33.22557], [70.02563, 33.14282], [69.85259, 33.09451], [69.79766, 33.13247], [69.71526, 33.09911], [69.57656, 33.09911], [69.49004, 33.01509], [69.49854, 32.88843], [69.5436, 32.8768], [69.47082, 32.85834], [69.38018, 32.76601], [69.43649, 32.7302], [69.44747, 32.6678], [69.38155, 32.56601], [69.2868, 32.53938], [69.23599, 32.45946], [69.27932, 32.29119], [69.27032, 32.14141], [69.3225, 31.93186], [69.20577, 31.85957], [69.11514, 31.70782], [69.00939, 31.62249], [68.95995, 31.64822], [68.91078, 31.59687], [68.79997, 31.61665], [68.6956, 31.75687], [68.57475, 31.83158], [68.44222, 31.76446], [68.27605, 31.75863], [68.25614, 31.80357], [68.1655, 31.82691], [68.00071, 31.6564], [67.86887, 31.63536], [67.72056, 31.52304], [67.58323, 31.52772], [67.62374, 31.40473], [67.7748, 31.4188], [67.78854, 31.33203], [67.29964, 31.19586], [67.03323, 31.24519], [67.04147, 31.31561], [66.83273, 31.26867], [66.72561, 31.20526], [66.68166, 31.07597], [66.58175, 30.97532], [66.42645, 30.95309], [66.39194, 30.9408], [66.28413, 30.57001], [66.34869, 30.404], [66.23609, 30.06321], [66.36042, 29.9583], [66.24175, 29.85181], [65.04005, 29.53957], [64.62116, 29.58903], [64.19796, 29.50407], [64.12966, 29.39157], [63.5876, 29.50456], [62.47751, 29.40782], [60.87231, 29.86514], [61.31508, 29.38903], [61.53765, 29.00507], [61.65978, 28.77937], [61.93581, 28.55284], [62.40259, 28.42703], [62.59499, 28.24842], [62.79412, 28.28108], [62.7638, 28.02992], [62.84905, 27.47627], [62.79684, 27.34381], [62.80604, 27.22412], [63.19649, 27.25674], [63.32283, 27.14437], [63.25005, 27.08692], [63.25005, 26.84212], [63.18688, 26.83844], [63.1889, 26.65072], [62.77352, 26.64099], [62.31484, 26.528], [62.21304, 26.26601], [62.05117, 26.31647], [61.89391, 26.26251], [61.83831, 26.07249], [61.83968, 25.7538], [61.683, 25.66638], [61.6433, 25.27541], [61.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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "PL", + iso1A3: "POL", + iso1N3: "616", + wikidata: "Q36", + nameEn: "Poland", + groups: ["EU", "151", "150", "UN"], + callingCodes: ["48"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[18.57853, 55.25302], [14.20811, 54.12784], [14.22634, 53.9291], [14.20647, 53.91671], [14.18544, 53.91258], [14.20823, 53.90776], [14.21323, 53.8664], [14.27249, 53.74464], [14.26782, 53.69866], [14.2836, 53.67721], [14.27133, 53.66613], [14.28477, 53.65955], [14.2853, 53.63392], [14.31904, 53.61581], [14.30416, 53.55499], [14.3273, 53.50587], [14.35209, 53.49506], [14.4215, 53.27724], [14.44133, 53.27427], [14.45125, 53.26241], [14.40662, 53.21098], [14.37853, 53.20405], [14.36696, 53.16444], [14.38679, 53.13669], [14.35044, 53.05829], [14.25954, 53.00264], [14.14056, 52.95786], [14.15873, 52.87715], [14.12256, 52.84311], [14.13806, 52.82392], [14.22071, 52.81175], [14.61073, 52.59847], [14.6289, 52.57136], [14.60081, 52.53116], [14.63056, 52.48993], [14.54423, 52.42568], [14.55228, 52.35264], [14.56378, 52.33838], [14.58149, 52.28007], [14.70139, 52.25038], [14.71319, 52.22144], [14.68344, 52.19612], [14.70616, 52.16927], [14.67683, 52.13936], [14.6917, 52.10283], [14.72971, 52.09167], [14.76026, 52.06624], [14.71339, 52.00337], [14.70488, 51.97679], [14.7139, 51.95643], [14.71836, 51.95606], [14.72163, 51.95188], [14.7177, 51.94048], [14.70601, 51.92944], [14.6933, 51.9044], [14.6588, 51.88359], [14.59089, 51.83302], [14.60493, 51.80473], [14.64625, 51.79472], [14.66386, 51.73282], [14.69065, 51.70842], [14.75392, 51.67445], [14.75759, 51.62318], [14.7727, 51.61263], [14.71125, 51.56209], [14.73047, 51.54606], [14.72652, 51.53902], [14.73219, 51.52922], [14.94749, 51.47155], [14.9652, 51.44793], [14.96899, 51.38367], [14.98008, 51.33449], [15.04288, 51.28387], [15.01242, 51.21285], [15.0047, 51.16874], [14.99311, 51.16249], [14.99414, 51.15813], [15.00083, 51.14974], [14.99646, 51.14365], [14.99079, 51.14284], [14.99689, 51.12205], [14.98229, 51.11354], [14.97938, 51.07742], [14.95529, 51.04552], [14.92942, 50.99744], [14.89252, 50.94999], [14.89681, 50.9422], [14.81664, 50.88148], [14.82803, 50.86966], [14.99852, 50.86817], [15.01088, 50.97984], [14.96419, 50.99108], [15.02433, 51.0242], [15.03895, 51.0123], [15.06218, 51.02269], [15.10152, 51.01095], [15.11937, 50.99021], [15.16744, 51.01959], [15.1743, 50.9833], [15.2361, 50.99886], [15.27043, 50.97724], [15.2773, 50.8907], [15.36656, 50.83956], [15.3803, 50.77187], [15.43798, 50.80833], [15.73186, 50.73885], [15.81683, 50.75666], [15.87331, 50.67188], [15.97219, 50.69799], [16.0175, 50.63009], [15.98317, 50.61528], [16.02437, 50.60046], [16.10265, 50.66405], [16.20839, 50.63096], [16.23174, 50.67101], [16.33611, 50.66579], [16.44597, 50.58041], [16.34572, 50.49575], [16.31413, 50.50274], [16.19526, 50.43291], [16.21585, 50.40627], [16.22821, 50.41054], [16.28118, 50.36891], [16.30289, 50.38292], [16.36495, 50.37679], [16.3622, 50.34875], [16.39379, 50.3207], [16.42674, 50.32509], [16.56407, 50.21009], [16.55446, 50.16613], [16.63137, 50.1142], [16.7014, 50.09659], [16.8456, 50.20834], [16.98018, 50.24172], [17.00353, 50.21449], [17.02825, 50.23118], [16.99803, 50.25753], [17.02138, 50.27772], [16.99803, 50.30316], [16.94448, 50.31281], [16.90877, 50.38642], [16.85933, 50.41093], [16.89229, 50.45117], [17.1224, 50.39494], [17.14498, 50.38117], [17.19579, 50.38817], [17.19991, 50.3654], [17.27681, 50.32246], [17.34273, 50.32947], [17.34548, 50.2628], [17.3702, 50.28123], [17.58889, 50.27837], [17.67764, 50.28977], [17.69292, 50.32859], [17.74648, 50.29966], [17.72176, 50.25665], [17.76296, 50.23382], [17.70528, 50.18812], [17.59404, 50.16437], [17.66683, 50.10275], [17.6888, 50.12037], [17.7506, 50.07896], [17.77669, 50.02253], [17.86886, 49.97452], [18.00191, 50.01723], [18.04585, 50.01194], [18.04585, 50.03311], [18.00396, 50.04954], [18.03212, 50.06574], [18.07898, 50.04535], [18.10628, 50.00223], [18.20241, 49.99958], [18.21752, 49.97309], [18.27107, 49.96779], [18.27794, 49.93863], [18.31914, 49.91565], [18.33278, 49.92415], [18.33562, 49.94747], [18.41604, 49.93498], [18.53423, 49.89906], [18.54495, 49.9079], [18.54299, 49.92537], [18.57697, 49.91565], [18.57045, 49.87849], [18.60341, 49.86256], [18.57183, 49.83334], [18.61278, 49.7618], [18.61368, 49.75426], [18.62645, 49.75002], [18.62943, 49.74603], [18.62676, 49.71983], [18.69817, 49.70473], [18.72838, 49.68163], [18.80479, 49.6815], [18.84786, 49.5446], [18.84521, 49.51672], [18.94536, 49.52143], [18.97283, 49.49914], [18.9742, 49.39557], [19.18019, 49.41165], [19.25435, 49.53391], [19.36009, 49.53747], [19.37795, 49.574], [19.45348, 49.61583], [19.52626, 49.57311], [19.53313, 49.52856], [19.57845, 49.46077], [19.64162, 49.45184], [19.6375, 49.40897], [19.72127, 49.39288], [19.78581, 49.41701], [19.82237, 49.27806], [19.75286, 49.20751], [19.86409, 49.19316], [19.90529, 49.23532], [19.98494, 49.22904], [20.08238, 49.1813], [20.13738, 49.31685], [20.21977, 49.35265], [20.31453, 49.34817], [20.31728, 49.39914], [20.39939, 49.3896], [20.46422, 49.41612], [20.5631, 49.375], [20.61666, 49.41791], [20.72274, 49.41813], [20.77971, 49.35383], [20.9229, 49.29626], [20.98733, 49.30774], [21.09799, 49.37176], [21.041, 49.41791], [21.12477, 49.43666], [21.19756, 49.4054], [21.27858, 49.45988], [21.43376, 49.41433], [21.62328, 49.4447], [21.77983, 49.35443], [21.82927, 49.39467], [21.96385, 49.3437], [22.04427, 49.22136], [22.56155, 49.08865], [22.89122, 49.00725], [22.86336, 49.10513], [22.72009, 49.20288], [22.748, 49.32759], [22.69444, 49.49378], [22.64534, 49.53094], [22.78304, 49.65543], [22.80261, 49.69098], [22.83179, 49.69875], [22.99329, 49.84249], [23.28221, 50.0957], [23.67635, 50.33385], [23.71382, 50.38248], [23.79445, 50.40481], [23.99563, 50.41289], [24.03668, 50.44507], [24.07048, 50.5071], [24.0996, 50.60752], [24.0595, 50.71625], [23.95925, 50.79271], [23.99254, 50.83847], [24.0952, 50.83262], [24.14524, 50.86128], [24.04576, 50.90196], [23.92217, 51.00836], [23.90376, 51.07697], [23.80678, 51.18405], [23.63858, 51.32182], [23.69905, 51.40871], [23.62751, 51.50512], [23.56236, 51.53673], [23.57053, 51.55938], [23.53198, 51.74298], [23.62691, 51.78208], [23.61523, 51.92066], [23.68733, 51.9906], [23.64066, 52.07626], [23.61, 52.11264], [23.54314, 52.12148], [23.47859, 52.18215], [23.20071, 52.22848], [23.18196, 52.28812], [23.34141, 52.44845], [23.45112, 52.53774], [23.58296, 52.59868], [23.73615, 52.6149], [23.93763, 52.71332], [23.91805, 52.94016], [23.94689, 52.95919], [23.92184, 53.02079], [23.87548, 53.0831], [23.91393, 53.16469], [23.85657, 53.22923], [23.81995, 53.24131], [23.62004, 53.60942], [23.51284, 53.95052], [23.48261, 53.98855], [23.52702, 54.04622], [23.49196, 54.14764], [23.45223, 54.17775], [23.42418, 54.17911], [23.39525, 54.21672], [23.3494, 54.25155], [23.24656, 54.25701], [23.15938, 54.29894], [23.15526, 54.31076], [23.13905, 54.31567], [23.104, 54.29794], [23.04323, 54.31567], [23.05726, 54.34565], [22.99649, 54.35927], [23.00584, 54.38514], [22.83756, 54.40827], [22.79705, 54.36264], [21.41123, 54.32395], [20.63871, 54.3706], [19.8038, 54.44203], [19.64312, 54.45423], [18.57853, 55.25302]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "PM", + iso1A3: "SPM", + iso1N3: "666", + wikidata: "Q34617", + nameEn: "Saint Pierre and Miquelon", + country: "FR", + groups: ["Q1451600", "021", "003", "019", "UN"], + callingCodes: ["508"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-56.72993, 46.65575], [-55.90758, 46.6223], [-56.27503, 47.39728], [-56.72993, 46.65575]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "PN", + iso1A3: "PCN", + iso1N3: "612", + wikidata: "Q35672", + nameEn: "Pitcairn Islands", + country: "GB", + groups: ["BOTS", "061", "009", "UN"], + driveSide: "left", + callingCodes: ["64"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-133.59543, -28.4709], [-122.0366, -24.55017], [-133.61511, -21.93325], [-133.59543, -28.4709]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "PR", + iso1A3: "PRI", + iso1N3: "630", + wikidata: "Q1183", + nameEn: "Puerto Rico", + aliases: ["US-PR"], + country: "US", + groups: ["Q1352230", "029", "003", "419", "019", "UN"], + roadSpeedUnit: "mph", + callingCodes: ["1 787", "1 939"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-65.27974, 17.56928], [-65.02435, 18.73231], [-67.99519, 18.97186], [-68.23894, 17.84663], [-65.27974, 17.56928]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "PS", + iso1A3: "PSE", + iso1N3: "275", + wikidata: "Q219060", + nameEn: "Palestine" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "PT", + iso1A3: "PRT", + iso1N3: "620", + wikidata: "Q45", + nameEn: "Portugal" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "PW", + iso1A3: "PLW", + iso1N3: "585", + wikidata: "Q695", + nameEn: "Palau", + groups: ["057", "009", "UN"], + roadSpeedUnit: "mph", + callingCodes: ["680"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[128.97621, 3.08804], [136.39296, 1.54187], [136.04605, 12.45908], [128.97621, 3.08804]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "PY", + iso1A3: "PRY", + iso1N3: "600", + wikidata: "Q733", + nameEn: "Paraguay", + groups: ["005", "419", "019", "UN"], + callingCodes: ["595"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-58.16225, -20.16193], [-58.23216, -19.80058], [-59.06965, -19.29148], [-60.00638, -19.2981], [-61.73723, -19.63958], [-61.93912, -20.10053], [-62.26883, -20.55311], [-62.2757, -21.06657], [-62.64455, -22.25091], [-62.51761, -22.37684], [-62.22768, -22.55807], [-61.9756, -23.0507], [-61.0782, -23.62932], [-60.99754, -23.80934], [-60.28163, -24.04436], [-60.03367, -24.00701], [-59.45482, -24.34787], [-59.33886, -24.49935], [-58.33055, -24.97099], [-58.25492, -24.92528], [-57.80821, -25.13863], [-57.57431, -25.47269], [-57.87176, -25.93604], [-58.1188, -26.16704], [-58.3198, -26.83443], [-58.65321, -27.14028], [-58.59549, -27.29973], [-58.04205, -27.2387], [-56.85337, -27.5165], [-56.18313, -27.29851], [-55.89195, -27.3467], [-55.74475, -27.44485], [-55.59094, -27.32444], [-55.62322, -27.1941], [-55.39611, -26.97679], [-55.25243, -26.93808], [-55.16948, -26.96068], [-55.06351, -26.80195], [-55.00584, -26.78754], [-54.80868, -26.55669], [-54.70732, -26.45099], [-54.69333, -26.37705], [-54.67359, -25.98607], [-54.60664, -25.9691], [-54.62063, -25.91213], [-54.59398, -25.59224], [-54.59509, -25.53696], [-54.60196, -25.48397], [-54.62033, -25.46026], [-54.4423, -25.13381], [-54.28207, -24.07305], [-54.32807, -24.01865], [-54.6238, -23.83078], [-55.02691, -23.97317], [-55.0518, -23.98666], [-55.12292, -23.99669], [-55.41784, -23.9657], [-55.44117, -23.9185], [-55.43585, -23.87157], [-55.5555, -23.28237], [-55.52288, -23.2595], [-55.5446, -23.22811], [-55.63849, -22.95122], [-55.62493, -22.62765], [-55.68742, -22.58407], [-55.6986, -22.56268], [-55.72366, -22.5519], [-55.741, -22.52018], [-55.74941, -22.46436], [-55.8331, -22.29008], [-56.23206, -22.25347], [-56.45893, -22.08072], [-56.5212, -22.11556], [-56.6508, -22.28387], [-57.98625, -22.09157], [-57.94642, -21.73799], [-57.88239, -21.6868], [-57.93492, -21.65505], [-57.84536, -20.93155], [-58.16225, -20.16193]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "QA", + iso1A3: "QAT", + iso1N3: "634", + wikidata: "Q846", + nameEn: "Qatar", + groups: ["145", "142", "UN"], + callingCodes: ["974"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[50.92992, 24.54396], [51.09638, 24.46907], [51.29972, 24.50747], [51.39468, 24.62785], [51.58834, 24.66608], [51.83108, 24.71675], [51.83682, 26.70231], [50.93865, 26.30758], [50.81266, 25.88946], [50.86149, 25.6965], [50.7801, 25.595], [50.80824, 25.54641], [50.57069, 25.57887], [50.8133, 24.74049], [50.92992, 24.54396]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "RE", + iso1A3: "REU", + iso1N3: "638", + wikidata: "Q17070", + nameEn: "R\xE9union", + country: "FR", + groups: ["Q3320166", "EU", "014", "202", "002", "UN"], + callingCodes: ["262"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[53.37984, -21.23941], [56.73473, -21.9174], [56.62373, -20.2711], [53.37984, -21.23941]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "RO", + iso1A3: "ROU", + iso1N3: "642", + wikidata: "Q218", + nameEn: "Romania", + groups: ["EU", "151", "150", "UN"], + callingCodes: ["40"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[27.15622, 47.98538], [27.02985, 48.09083], [27.04118, 48.12522], [26.96119, 48.13003], [26.98042, 48.15752], [26.94265, 48.1969], [26.87708, 48.19919], [26.81161, 48.25049], [26.62823, 48.25804], [26.55202, 48.22445], [26.33504, 48.18418], [26.17711, 47.99246], [26.05901, 47.9897], [25.77723, 47.93919], [25.63878, 47.94924], [25.23778, 47.89403], [25.11144, 47.75203], [24.88896, 47.7234], [24.81893, 47.82031], [24.70632, 47.84428], [24.61994, 47.95062], [24.43578, 47.97131], [24.34926, 47.9244], [24.22566, 47.90231], [24.11281, 47.91487], [24.06466, 47.95317], [24.02999, 47.95087], [24.00801, 47.968], [23.98553, 47.96076], [23.96337, 47.96672], [23.94192, 47.94868], [23.89352, 47.94512], [23.8602, 47.9329], [23.80904, 47.98142], [23.75188, 47.99705], [23.66262, 47.98786], [23.63894, 48.00293], [23.5653, 48.00499], [23.52803, 48.01818], [23.4979, 47.96858], [23.33577, 48.0237], [23.27397, 48.08245], [23.15999, 48.12188], [23.1133, 48.08061], [23.08858, 48.00716], [23.0158, 47.99338], [22.92241, 48.02002], [22.94301, 47.96672], [22.89849, 47.95851], [22.77991, 47.87211], [22.76617, 47.8417], [22.67247, 47.7871], [22.46559, 47.76583], [22.41979, 47.7391], [22.31816, 47.76126], [22.00917, 47.50492], [22.03389, 47.42508], [22.01055, 47.37767], [21.94463, 47.38046], [21.78395, 47.11104], [21.648, 47.03902], [21.68645, 46.99595], [21.59581, 46.91628], [21.59307, 46.86935], [21.52028, 46.84118], [21.48935, 46.7577], [21.5151, 46.72147], [21.43926, 46.65109], [21.33214, 46.63035], [21.26929, 46.4993], [21.28061, 46.44941], [21.16872, 46.30118], [21.06572, 46.24897], [20.86797, 46.28884], [20.74574, 46.25467], [20.76085, 46.21002], [20.63863, 46.12728], [20.49718, 46.18721], [20.45377, 46.14405], [20.35573, 46.16629], [20.28324, 46.1438], [20.26068, 46.12332], [20.35862, 45.99356], [20.54818, 45.89939], [20.65645, 45.82801], [20.70069, 45.7493], [20.77416, 45.75601], [20.78446, 45.78522], [20.82364, 45.77738], [20.80361, 45.65875], [20.76798, 45.60969], [20.83321, 45.53567], [20.77217, 45.49788], [20.86026, 45.47295], [20.87948, 45.42743], [21.09894, 45.30144], [21.17612, 45.32566], [21.20392, 45.2677], [21.29398, 45.24148], [21.48278, 45.19557], [21.51299, 45.15345], [21.4505, 45.04294], [21.35855, 45.01941], [21.54938, 44.9327], [21.56328, 44.89502], [21.48202, 44.87199], [21.44013, 44.87613], [21.35643, 44.86364], [21.38802, 44.78133], [21.55007, 44.77304], [21.60019, 44.75208], [21.61942, 44.67059], [21.67504, 44.67107], [21.71692, 44.65349], [21.7795, 44.66165], [21.99364, 44.63395], [22.08016, 44.49844], [22.13234, 44.47444], [22.18315, 44.48179], [22.30844, 44.6619], [22.45301, 44.7194], [22.61917, 44.61489], [22.69196, 44.61587], [22.76749, 44.54446], [22.70981, 44.51852], [22.61368, 44.55719], [22.56493, 44.53419], [22.54021, 44.47836], [22.45436, 44.47258], [22.56012, 44.30712], [22.68166, 44.28206], [22.67173, 44.21564], [23.04988, 44.07694], [23.01674, 44.01946], [22.87873, 43.9844], [22.83753, 43.88055], [22.85314, 43.84452], [23.05288, 43.79494], [23.26772, 43.84843], [23.4507, 43.84936], [23.61687, 43.79289], [23.73978, 43.80627], [24.18149, 43.68218], [24.35364, 43.70211], [24.50264, 43.76314], [24.62281, 43.74082], [24.73542, 43.68523], [24.96682, 43.72693], [25.10718, 43.6831], [25.17144, 43.70261], [25.39528, 43.61866], [25.72792, 43.69263], [25.94911, 43.85745], [26.05584, 43.90925], [26.10115, 43.96908], [26.38764, 44.04356], [26.62712, 44.05698], [26.95141, 44.13555], [27.26845, 44.12602], [27.39757, 44.0141], [27.60834, 44.01206], [27.64542, 44.04958], [27.73468, 43.95326], [27.92008, 44.00761], [27.99558, 43.84193], [28.23293, 43.76], [29.24336, 43.70874], [30.04414, 45.08461], [29.69272, 45.19227], [29.65428, 45.25629], [29.68175, 45.26885], [29.59798, 45.38857], [29.42632, 45.44545], [29.24779, 45.43388], [28.96077, 45.33164], [28.94292, 45.28045], [28.81383, 45.3384], [28.78911, 45.24179], [28.71358, 45.22631], [28.5735, 45.24759], [28.34554, 45.32102], [28.28504, 45.43907], [28.21139, 45.46895], [28.18741, 45.47358], [28.08927, 45.6051], [28.16568, 45.6421], [28.13111, 45.92819], [28.08612, 46.01105], [28.13684, 46.18099], [28.10937, 46.22852], [28.19864, 46.31869], [28.18902, 46.35283], [28.25769, 46.43334], [28.22281, 46.50481], [28.24808, 46.64305], [28.12173, 46.82283], [28.09095, 46.97621], [27.81892, 47.1381], [27.73172, 47.29248], [27.68706, 47.28962], [27.60263, 47.32507], [27.55731, 47.46637], [27.47942, 47.48113], [27.3979, 47.59473], [27.32202, 47.64009], [27.25519, 47.71366], [27.29069, 47.73722], [27.1618, 47.92391], [27.15622, 47.98538]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "RS", + iso1A3: "SRB", + iso1N3: "688", + wikidata: "Q403", + nameEn: "Serbia", + groups: ["039", "150", "UN"], + callingCodes: ["381"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[19.66007, 46.19005], [19.56113, 46.16824], [19.52473, 46.1171], [19.28826, 45.99694], [19.14543, 45.9998], [19.10388, 46.04015], [19.0791, 45.96458], [19.01284, 45.96529], [18.99712, 45.93537], [18.81394, 45.91329], [18.85783, 45.85493], [18.90305, 45.71863], [18.96691, 45.66731], [18.88776, 45.57253], [18.94562, 45.53712], [19.07471, 45.53086], [19.08364, 45.48804], [18.99918, 45.49333], [18.97446, 45.37528], [19.10774, 45.29547], [19.28208, 45.23813], [19.41941, 45.23475], [19.43589, 45.17137], [19.19144, 45.17863], [19.14063, 45.12972], [19.07952, 45.14668], [19.1011, 45.01191], [19.05205, 44.97692], [19.15573, 44.95409], [19.06853, 44.89915], [19.02871, 44.92541], [18.98957, 44.90645], [19.01994, 44.85493], [19.18183, 44.92055], [19.36722, 44.88164], [19.32543, 44.74058], [19.26388, 44.65412], [19.16699, 44.52197], [19.13369, 44.52521], [19.12278, 44.50132], [19.14837, 44.45253], [19.14681, 44.41463], [19.11785, 44.40313], [19.10749, 44.39421], [19.10704, 44.38249], [19.10365, 44.37795], [19.10298, 44.36924], [19.11865, 44.36712], [19.1083, 44.3558], [19.11547, 44.34218], [19.13556, 44.338], [19.13332, 44.31492], [19.16741, 44.28648], [19.18328, 44.28383], [19.20508, 44.2917], [19.23306, 44.26097], [19.26945, 44.26957], [19.32464, 44.27185], [19.34773, 44.23244], [19.3588, 44.18353], [19.40927, 44.16722], [19.43905, 44.13088], [19.47338, 44.15034], [19.48386, 44.14332], [19.47321, 44.1193], [19.51167, 44.08158], [19.55999, 44.06894], [19.57467, 44.04716], [19.61991, 44.05254], [19.61836, 44.01464], [19.56498, 43.99922], [19.52515, 43.95573], [19.38439, 43.96611], [19.24363, 44.01502], [19.23465, 43.98764], [19.3986, 43.79668], [19.5176, 43.71403], [19.50455, 43.58385], [19.42696, 43.57987], [19.41941, 43.54056], [19.36653, 43.60921], [19.33426, 43.58833], [19.2553, 43.5938], [19.24774, 43.53061], [19.22807, 43.5264], [19.22229, 43.47926], [19.44315, 43.38846], [19.48171, 43.32644], [19.52962, 43.31623], [19.54598, 43.25158], [19.62661, 43.2286], [19.64063, 43.19027], [19.76918, 43.16044], [19.79255, 43.11951], [19.92576, 43.08539], [19.96549, 43.11098], [19.98887, 43.0538], [20.04729, 43.02732], [20.05431, 42.99571], [20.12325, 42.96237], [20.14896, 42.99058], [20.16415, 42.97177], [20.34528, 42.90676], [20.35692, 42.8335], [20.40594, 42.84853], [20.43734, 42.83157], [20.53484, 42.8885], [20.48692, 42.93208], [20.59929, 43.01067], [20.64557, 43.00826], [20.69515, 43.09641], [20.59929, 43.20492], [20.68688, 43.21335], [20.73811, 43.25068], [20.82145, 43.26769], [20.88685, 43.21697], [20.83727, 43.17842], [20.96287, 43.12416], [21.00749, 43.13984], [21.05378, 43.10707], [21.08952, 43.13471], [21.14465, 43.11089], [21.16734, 42.99694], [21.2041, 43.02277], [21.23877, 43.00848], [21.23534, 42.95523], [21.2719, 42.8994], [21.32974, 42.90424], [21.36941, 42.87397], [21.44047, 42.87276], [21.39045, 42.74888], [21.47498, 42.74695], [21.59154, 42.72643], [21.58755, 42.70418], [21.6626, 42.67813], [21.75025, 42.70125], [21.79413, 42.65923], [21.75672, 42.62695], [21.7327, 42.55041], [21.70522, 42.54176], [21.7035, 42.51899], [21.62556, 42.45106], [21.64209, 42.41081], [21.62887, 42.37664], [21.59029, 42.38042], [21.57021, 42.3647], [21.53467, 42.36809], [21.5264, 42.33634], [21.56772, 42.30946], [21.58992, 42.25915], [21.70111, 42.23789], [21.77176, 42.2648], [21.84654, 42.3247], [21.91595, 42.30392], [21.94405, 42.34669], [22.02908, 42.29848], [22.16384, 42.32103], [22.29605, 42.37477], [22.29275, 42.34913], [22.34773, 42.31725], [22.45919, 42.33822], [22.47498, 42.3915], [22.51961, 42.3991], [22.55669, 42.50144], [22.43983, 42.56851], [22.4997, 42.74144], [22.43309, 42.82057], [22.54302, 42.87774], [22.74826, 42.88701], [22.78397, 42.98253], [22.89521, 43.03625], [22.98104, 43.11199], [23.00806, 43.19279], [22.89727, 43.22417], [22.82036, 43.33665], [22.53397, 43.47225], [22.47582, 43.6558], [22.41043, 43.69566], [22.35558, 43.81281], [22.41449, 44.00514], [22.61688, 44.06534], [22.61711, 44.16938], [22.67173, 44.21564], [22.68166, 44.28206], [22.56012, 44.30712], [22.45436, 44.47258], [22.54021, 44.47836], [22.56493, 44.53419], [22.61368, 44.55719], [22.70981, 44.51852], [22.76749, 44.54446], [22.69196, 44.61587], [22.61917, 44.61489], [22.45301, 44.7194], [22.30844, 44.6619], [22.18315, 44.48179], [22.13234, 44.47444], [22.08016, 44.49844], [21.99364, 44.63395], [21.7795, 44.66165], [21.71692, 44.65349], [21.67504, 44.67107], [21.61942, 44.67059], [21.60019, 44.75208], [21.55007, 44.77304], [21.38802, 44.78133], [21.35643, 44.86364], [21.44013, 44.87613], [21.48202, 44.87199], [21.56328, 44.89502], [21.54938, 44.9327], [21.35855, 45.01941], [21.4505, 45.04294], [21.51299, 45.15345], [21.48278, 45.19557], [21.29398, 45.24148], [21.20392, 45.2677], [21.17612, 45.32566], [21.09894, 45.30144], [20.87948, 45.42743], [20.86026, 45.47295], [20.77217, 45.49788], [20.83321, 45.53567], [20.76798, 45.60969], [20.80361, 45.65875], [20.82364, 45.77738], [20.78446, 45.78522], [20.77416, 45.75601], [20.70069, 45.7493], [20.65645, 45.82801], [20.54818, 45.89939], [20.35862, 45.99356], [20.26068, 46.12332], [20.09713, 46.17315], [20.03533, 46.14509], [20.01816, 46.17696], [19.93508, 46.17553], [19.81491, 46.1313], [19.66007, 46.19005]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "RU", + iso1A3: "RUS", + iso1N3: "643", + wikidata: "Q159", + nameEn: "Russia" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "RW", + iso1A3: "RWA", + iso1N3: "646", + wikidata: "Q1037", + nameEn: "Rwanda", + groups: ["014", "202", "002", "UN"], + callingCodes: ["250"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[30.47194, -1.0555], [30.35212, -1.06896], [30.16369, -1.34303], [29.912, -1.48269], [29.82657, -1.31187], [29.59061, -1.39016], [29.53062, -1.40499], [29.45038, -1.5054], [29.36322, -1.50887], [29.24323, -1.66826], [29.24458, -1.69663], [29.11847, -1.90576], [29.17562, -2.12278], [29.105, -2.27043], [29.00051, -2.29001], [28.95642, -2.37321], [28.89601, -2.37321], [28.86826, -2.41888], [28.86846, -2.44866], [28.89132, -2.47557], [28.89342, -2.49017], [28.88846, -2.50493], [28.87497, -2.50887], [28.86209, -2.5231], [28.86193, -2.53185], [28.87943, -2.55165], [28.89288, -2.55848], [28.90226, -2.62385], [28.89793, -2.66111], [28.94346, -2.69124], [29.00357, -2.70596], [29.04081, -2.7416], [29.0562, -2.58632], [29.32234, -2.6483], [29.36805, -2.82933], [29.88237, -2.75105], [29.95911, -2.33348], [30.14034, -2.43626], [30.42933, -2.31064], [30.54501, -2.41404], [30.83915, -2.35795], [30.89303, -2.08223], [30.80802, -1.91477], [30.84079, -1.64652], [30.71974, -1.43244], [30.57123, -1.33264], [30.50889, -1.16412], [30.45116, -1.10641], [30.47194, -1.0555]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SA", + iso1A3: "SAU", + iso1N3: "682", + wikidata: "Q851", + nameEn: "Saudi Arabia", + groups: ["145", "142", "UN"], + callingCodes: ["966"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[40.01521, 32.05667], [39.29903, 32.23259], [38.99233, 31.99721], [36.99791, 31.50081], [37.99354, 30.49998], [37.66395, 30.33245], [37.4971, 29.99949], [36.75083, 29.86903], [36.50005, 29.49696], [36.07081, 29.18469], [34.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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SB", + iso1A3: "SLB", + iso1N3: "090", + wikidata: "Q685", + nameEn: "Solomon Islands", + groups: ["054", "009", "UN"], + driveSide: "left", + callingCodes: ["677"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SC", + iso1A3: "SYC", + iso1N3: "690", + wikidata: "Q1042", + nameEn: "Seychelles", + groups: ["014", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["248"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[43.75112, -10.38913], [54.83239, -10.93575], [66.3222, 5.65313], [43.75112, -10.38913]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SD", + iso1A3: "SDN", + iso1N3: "729", + wikidata: "Q1049", + nameEn: "Sudan", + groups: ["015", "002", "UN"], + callingCodes: ["249"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[37.8565, 22.00903], [34.0765, 22.00501], [33.99686, 21.76784], [33.57251, 21.72406], [33.17563, 22.00405], [24.99885, 21.99535], [24.99794, 19.99661], [23.99715, 20.00038], [23.99539, 19.49944], [23.99997, 15.69575], [23.62785, 15.7804], [23.38812, 15.69649], [23.10792, 15.71297], [22.93201, 15.55107], [22.92579, 15.47007], [22.99584, 15.40105], [22.99584, 15.22989], [22.66115, 14.86308], [22.70474, 14.69149], [22.38562, 14.58907], [22.44944, 14.24986], [22.55997, 14.23024], [22.5553, 14.11704], [22.22995, 13.96754], [22.08674, 13.77863], [22.29689, 13.3731], [22.1599, 13.19281], [22.02914, 13.13976], [21.94819, 13.05637], [21.81432, 12.81362], [21.89371, 12.68001], [21.98711, 12.63292], [22.15679, 12.66634], [22.22684, 12.74682], [22.46345, 12.61925], [22.38873, 12.45514], [22.50548, 12.16769], [22.48369, 12.02766], [22.64092, 12.07485], [22.54907, 11.64372], [22.7997, 11.40424], [22.93124, 11.41645], [22.97249, 11.21955], [22.87758, 10.91915], [23.02221, 10.69235], [23.3128, 10.45214], [23.67164, 9.86923], [23.69155, 9.67566], [24.09319, 9.66572], [24.12744, 9.73784], [24.49389, 9.79962], [24.84653, 9.80643], [24.97739, 9.9081], [25.05688, 10.06776], [25.0918, 10.33718], [25.78141, 10.42599], [25.93163, 10.38159], [25.93241, 10.17941], [26.21338, 9.91545], [26.35815, 9.57946], [26.70685, 9.48735], [27.14427, 9.62858], [27.90704, 9.61323], [28.99983, 9.67155], [29.06988, 9.74826], [29.53844, 9.75133], [29.54, 10.07949], [29.94629, 10.29245], [30.00389, 10.28633], [30.53005, 9.95992], [30.82893, 9.71451], [30.84605, 9.7498], [31.28504, 9.75287], [31.77539, 10.28939], [31.99177, 10.65065], [32.46967, 11.04662], [32.39358, 11.18207], [32.39578, 11.70208], [32.10079, 11.95203], [32.73921, 11.95203], [32.73921, 12.22757], [33.25876, 12.22111], [33.13988, 11.43248], [33.26977, 10.83632], [33.24645, 10.77913], [33.52294, 10.64382], [33.66604, 10.44254], [33.80913, 10.32994], [33.90159, 10.17179], [33.96984, 10.15446], [33.99185, 9.99623], [33.96323, 9.80972], [33.9082, 9.762], [33.87958, 9.49937], [34.10229, 9.50238], [34.08717, 9.55243], [34.13186, 9.7492], [34.20484, 9.9033], [34.22718, 10.02506], [34.32102, 10.11599], [34.34783, 10.23914], [34.2823, 10.53508], [34.4372, 10.781], [34.59062, 10.89072], [34.77383, 10.74588], [34.77532, 10.69027], [34.86618, 10.74588], [34.86916, 10.78832], [34.97491, 10.86147], [34.97789, 10.91559], [34.93172, 10.95946], [35.01215, 11.19626], [34.95704, 11.24448], [35.09556, 11.56278], [35.05832, 11.71158], [35.11492, 11.85156], [35.24302, 11.91132], [35.70476, 12.67101], [36.01458, 12.72478], [36.14268, 12.70879], [36.16651, 12.88019], [36.13374, 12.92665], [36.24545, 13.36759], [36.38993, 13.56459], [36.48824, 13.83954], [36.44653, 13.95666], [36.54376, 14.25597], [36.44337, 15.14963], [36.54276, 15.23478], [36.69761, 15.75323], [36.76371, 15.80831], [36.92193, 16.23451], [36.99777, 17.07172], [37.42694, 17.04041], [37.50967, 17.32199], [38.13362, 17.53906], [38.37133, 17.66269], [38.45916, 17.87167], [38.57727, 17.98125], [39.63762, 18.37348], [37.8565, 22.00903]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SE", + iso1A3: "SWE", + iso1N3: "752", + wikidata: "Q34", + nameEn: "Sweden", + groups: ["EU", "154", "150", "UN"], + callingCodes: ["46"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[24.15791, 65.85385], [23.90497, 66.15802], [23.71339, 66.21299], [23.64982, 66.30603], [23.67591, 66.3862], [23.63776, 66.43568], [23.85959, 66.56434], [23.89488, 66.772], [23.98059, 66.79585], [23.98563, 66.84149], [23.56214, 67.17038], [23.58735, 67.20752], [23.54701, 67.25435], [23.75372, 67.29914], [23.75372, 67.43688], [23.39577, 67.46974], [23.54701, 67.59306], [23.45627, 67.85297], [23.65793, 67.9497], [23.40081, 68.05545], [23.26469, 68.15134], [23.15377, 68.14759], [23.10336, 68.26551], [22.73028, 68.40881], [22.00429, 68.50692], [21.03001, 68.88969], [20.90649, 68.89696], [20.85104, 68.93142], [20.91658, 68.96764], [20.78802, 69.03087], [20.55258, 69.06069], [20.0695, 69.04469], [20.28444, 68.93283], [20.33435, 68.80174], [20.22027, 68.67246], [19.95647, 68.55546], [20.22027, 68.48759], [19.93508, 68.35911], [18.97255, 68.52416], [18.63032, 68.50849], [18.39503, 68.58672], [18.1241, 68.53721], [18.13836, 68.20874], [17.90787, 67.96537], [17.30416, 68.11591], [16.7409, 67.91037], [16.38441, 67.52923], [16.12774, 67.52106], [16.09922, 67.4364], [16.39154, 67.21653], [16.35589, 67.06419], [15.37197, 66.48217], [15.49318, 66.28509], [15.05113, 66.15572], [14.53778, 66.12399], [14.50926, 65.31786], [13.64276, 64.58402], [14.11117, 64.46674], [14.16051, 64.18725], [13.98222, 64.00953], [13.23411, 64.09087], [12.74105, 64.02171], [12.14928, 63.59373], [12.19919, 63.47935], [11.98529, 63.27487], [12.19919, 63.00104], [12.07085, 62.6297], [12.29187, 62.25699], [12.14746, 61.7147], [12.40595, 61.57226], [12.57707, 61.56547], [12.86939, 61.35427], [12.69115, 61.06584], [12.2277, 61.02442], [12.59133, 60.50559], [12.52003, 60.13846], [12.36317, 59.99259], [12.15641, 59.8926], [11.87121, 59.86039], [11.92112, 59.69531], [11.69297, 59.59442], [11.8213, 59.24985], [11.65732, 58.90177], [11.45199, 58.89604], [11.4601, 58.99022], [11.34459, 59.11672], [11.15367, 59.07862], [11.08911, 58.98745], [10.64958, 58.89391], [10.40861, 58.38489], [12.16597, 56.60205], [12.07466, 56.29488], [12.65312, 56.04345], [12.6372, 55.91371], [12.88472, 55.63369], [12.60345, 55.42675], [12.84405, 55.13257], [14.28399, 55.1553], [14.89259, 55.5623], [15.79951, 55.54655], [19.64795, 57.06466], [19.84909, 57.57876], [20.5104, 59.15546], [19.08191, 60.19152], [19.23413, 60.61414], [20.15877, 63.06556], [24.14112, 65.39731], [24.15107, 65.81427], [24.14798, 65.83466], [24.15791, 65.85385]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SG", + iso1A3: "SGP", + iso1N3: "702", + wikidata: "Q334", + nameEn: "Singapore", + groups: ["035", "142", "UN"], + driveSide: "left", + callingCodes: ["65"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[104.00131, 1.42405], [103.93384, 1.42926], [103.89565, 1.42841], [103.86383, 1.46288], [103.81181, 1.47953], [103.76395, 1.45183], [103.74161, 1.4502], [103.7219, 1.46108], [103.67468, 1.43166], [103.62738, 1.35255], [103.56591, 1.19719], [103.66049, 1.18825], [103.74084, 1.12902], [104.03085, 1.26954], [104.12282, 1.27714], [104.08072, 1.35998], [104.09162, 1.39694], [104.08871, 1.42015], [104.07348, 1.43322], [104.04622, 1.44691], [104.02277, 1.4438], [104.00131, 1.42405]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SH", + iso1A3: "SHN", + iso1N3: "654", + wikidata: "Q192184", + nameEn: "Saint Helena, Ascension and Tristan da Cunha", + country: "GB" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "SI", + iso1A3: "SVN", + iso1N3: "705", + wikidata: "Q215", + nameEn: "Slovenia", + groups: ["EU", "039", "150", "UN"], + callingCodes: ["386"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[16.50139, 46.56684], [16.39217, 46.63673], [16.38594, 46.6549], [16.41863, 46.66238], [16.42641, 46.69228], [16.37816, 46.69975], [16.30966, 46.7787], [16.31303, 46.79838], [16.3408, 46.80641], [16.34547, 46.83836], [16.2941, 46.87137], [16.2365, 46.87775], [16.21892, 46.86961], [16.15711, 46.85434], [16.14365, 46.8547], [16.10983, 46.867], [16.05786, 46.83927], [15.99054, 46.82772], [15.99126, 46.78199], [15.98432, 46.74991], [15.99769, 46.7266], [16.02808, 46.71094], [16.04347, 46.68694], [16.04036, 46.6549], [15.99988, 46.67947], [15.98512, 46.68463], [15.94864, 46.68769], [15.87691, 46.7211], [15.8162, 46.71897], [15.78518, 46.70712], [15.76771, 46.69863], [15.73823, 46.70011], [15.72279, 46.69548], [15.69523, 46.69823], [15.67411, 46.70735], [15.6543, 46.70616], [15.6543, 46.69228], [15.6365, 46.6894], [15.63255, 46.68069], [15.62317, 46.67947], [15.59826, 46.68908], [15.54533, 46.66985], [15.55333, 46.64988], [15.54431, 46.6312], [15.46906, 46.61321], [15.45514, 46.63697], [15.41235, 46.65556], [15.23711, 46.63994], [15.14215, 46.66131], [15.01451, 46.641], [14.98024, 46.6009], [14.96002, 46.63459], [14.92283, 46.60848], [14.87129, 46.61], [14.86419, 46.59411], [14.83549, 46.56614], [14.81836, 46.51046], [14.72185, 46.49974], [14.66892, 46.44936], [14.5942, 46.43434], [14.56463, 46.37208], [14.52176, 46.42617], [14.45877, 46.41717], [14.42608, 46.44614], [14.314, 46.43327], [14.28326, 46.44315], [14.15989, 46.43327], [14.12097, 46.47724], [14.04002, 46.49117], [14.00422, 46.48474], [13.89837, 46.52331], [13.7148, 46.5222], [13.68684, 46.43881], [13.59777, 46.44137], [13.5763, 46.42613], [13.5763, 46.40915], [13.47019, 46.3621], [13.43418, 46.35992], [13.44808, 46.33507], [13.37671, 46.29668], [13.42218, 46.20758], [13.47587, 46.22725], [13.56114, 46.2054], [13.56682, 46.18703], [13.64451, 46.18966], [13.66472, 46.17392], [13.64053, 46.13587], [13.57072, 46.09022], [13.50104, 46.05986], [13.49568, 46.04839], [13.50998, 46.04498], [13.49702, 46.01832], [13.47474, 46.00546], [13.50104, 45.98078], [13.52963, 45.96588], [13.56759, 45.96991], [13.58903, 45.99009], [13.62074, 45.98388], [13.63458, 45.98947], [13.64307, 45.98326], [13.6329, 45.94894], [13.63815, 45.93607], [13.61931, 45.91782], [13.60857, 45.89907], [13.59565, 45.89446], [13.58644, 45.88173], [13.57563, 45.8425], [13.58858, 45.83503], [13.59784, 45.8072], [13.66986, 45.79955], [13.8235, 45.7176], [13.83332, 45.70855], [13.83422, 45.68703], [13.87933, 45.65207], [13.9191, 45.6322], [13.8695, 45.60835], [13.86771, 45.59898], [13.84106, 45.58185], [13.78445, 45.5825], [13.74587, 45.59811], [13.7198, 45.59352], [13.6076, 45.64761], [13.45644, 45.59464], [13.56979, 45.4895], [13.62902, 45.45898], [13.67398, 45.4436], [13.7785, 45.46787], [13.81742, 45.43729], [13.88124, 45.42637], [13.90771, 45.45149], [13.97309, 45.45258], [13.99488, 45.47551], [13.96063, 45.50825], [14.00578, 45.52352], [14.07116, 45.48752], [14.20348, 45.46896], [14.22371, 45.50388], [14.24239, 45.50607], [14.26611, 45.48239], [14.27681, 45.4902], [14.32487, 45.47142], [14.36693, 45.48642], [14.49769, 45.54424], [14.5008, 45.60852], [14.53816, 45.6205], [14.57397, 45.67165], [14.60977, 45.66403], [14.59576, 45.62812], [14.69694, 45.57366], [14.68605, 45.53006], [14.71718, 45.53442], [14.80124, 45.49515], [14.81992, 45.45913], [14.90554, 45.47769], [14.92266, 45.52788], [15.02385, 45.48533], [15.05187, 45.49079], [15.16862, 45.42309], [15.27758, 45.46678], [15.33051, 45.45258], [15.38188, 45.48752], [15.30249, 45.53224], [15.29837, 45.5841], [15.27747, 45.60504], [15.31027, 45.6303], [15.34695, 45.63382], [15.34214, 45.64702], [15.38952, 45.63682], [15.4057, 45.64727], [15.34919, 45.71623], [15.30872, 45.69014], [15.25423, 45.72275], [15.40836, 45.79491], [15.47531, 45.79802], [15.47325, 45.8253], [15.52234, 45.82195], [15.57952, 45.84953], [15.64185, 45.82915], [15.66662, 45.84085], [15.70411, 45.8465], [15.68232, 45.86819], [15.68383, 45.88867], [15.67967, 45.90455], [15.70636, 45.92116], [15.70327, 46.00015], [15.71246, 46.01196], [15.72977, 46.04682], [15.62317, 46.09103], [15.6083, 46.11992], [15.59909, 46.14761], [15.64904, 46.19229], [15.6434, 46.21396], [15.67395, 46.22478], [15.75436, 46.21969], [15.75479, 46.20336], [15.78817, 46.21719], [15.79284, 46.25811], [15.97965, 46.30652], [16.07616, 46.3463], [16.07314, 46.36458], [16.05065, 46.3833], [16.05281, 46.39141], [16.14859, 46.40547], [16.18824, 46.38282], [16.30233, 46.37837], [16.30162, 46.40437], [16.27329, 46.41467], [16.27398, 46.42875], [16.25124, 46.48067], [16.23961, 46.49653], [16.26759, 46.50566], [16.26733, 46.51505], [16.29793, 46.5121], [16.37193, 46.55008], [16.38771, 46.53608], [16.44036, 46.5171], [16.5007, 46.49644], [16.52604, 46.47831], [16.59527, 46.47524], [16.52604, 46.5051], [16.52885, 46.53303], [16.50139, 46.56684]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SJ", + iso1A3: "SJM", + iso1N3: "744", + wikidata: "Q842829", + nameEn: "Svalbard and Jan Mayen", + country: "NO" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "SK", + iso1A3: "SVK", + iso1N3: "703", + wikidata: "Q214", + nameEn: "Slovakia", + groups: ["EU", "151", "150", "UN"], + callingCodes: ["421"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[19.82237, 49.27806], [19.78581, 49.41701], [19.72127, 49.39288], [19.6375, 49.40897], [19.64162, 49.45184], [19.57845, 49.46077], [19.53313, 49.52856], [19.52626, 49.57311], [19.45348, 49.61583], [19.37795, 49.574], [19.36009, 49.53747], [19.25435, 49.53391], [19.18019, 49.41165], [18.9742, 49.39557], [18.97283, 49.49914], [18.94536, 49.52143], [18.84521, 49.51672], [18.74761, 49.492], [18.67757, 49.50895], [18.6144, 49.49824], [18.57183, 49.51162], [18.53063, 49.49022], [18.54848, 49.47059], [18.44686, 49.39467], [18.4084, 49.40003], [18.4139, 49.36517], [18.36446, 49.3267], [18.18456, 49.28909], [18.15022, 49.24518], [18.1104, 49.08624], [18.06885, 49.03157], [17.91814, 49.01784], [17.87831, 48.92679], [17.77944, 48.92318], [17.73126, 48.87885], [17.7094, 48.86721], [17.5295, 48.81117], [17.45671, 48.85004], [17.3853, 48.80936], [17.29054, 48.85546], [17.19355, 48.87602], [17.11202, 48.82925], [17.00215, 48.70887], [16.93955, 48.60371], [16.94611, 48.53614], [16.85204, 48.44968], [16.8497, 48.38321], [16.83588, 48.3844], [16.83317, 48.38138], [16.84243, 48.35258], [16.90903, 48.32519], [16.89461, 48.31332], [16.97701, 48.17385], [17.02919, 48.13996], [17.05735, 48.14179], [17.09168, 48.09366], [17.07039, 48.0317], [17.16001, 48.00636], [17.23699, 48.02094], [17.71215, 47.7548], [18.02938, 47.75665], [18.29305, 47.73541], [18.56496, 47.76588], [18.66521, 47.76772], [18.74074, 47.8157], [18.8506, 47.82308], [18.76821, 47.87469], [18.76134, 47.97499], [18.82176, 48.04206], [19.01952, 48.07052], [19.23924, 48.0595], [19.28182, 48.08336], [19.47957, 48.09437], [19.52489, 48.19791], [19.63338, 48.25006], [19.92452, 48.1283], [20.24312, 48.2784], [20.29943, 48.26104], [20.5215, 48.53336], [20.83248, 48.5824], [21.11516, 48.49546], [21.44063, 48.58456], [21.6068, 48.50365], [21.67134, 48.3989], [21.72525, 48.34628], [21.8279, 48.33321], [21.83339, 48.36242], [22.14689, 48.4005], [22.16023, 48.56548], [22.21379, 48.6218], [22.34151, 48.68893], [22.42934, 48.92857], [22.48296, 48.99172], [22.54338, 49.01424], [22.56155, 49.08865], [22.04427, 49.22136], [21.96385, 49.3437], [21.82927, 49.39467], [21.77983, 49.35443], [21.62328, 49.4447], [21.43376, 49.41433], [21.27858, 49.45988], [21.19756, 49.4054], [21.12477, 49.43666], [21.041, 49.41791], [21.09799, 49.37176], [20.98733, 49.30774], [20.9229, 49.29626], [20.77971, 49.35383], [20.72274, 49.41813], [20.61666, 49.41791], [20.5631, 49.375], [20.46422, 49.41612], [20.39939, 49.3896], [20.31728, 49.39914], [20.31453, 49.34817], [20.21977, 49.35265], [20.13738, 49.31685], [20.08238, 49.1813], [19.98494, 49.22904], [19.90529, 49.23532], [19.86409, 49.19316], [19.75286, 49.20751], [19.82237, 49.27806]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SL", + iso1A3: "SLE", + iso1N3: "694", + wikidata: "Q1044", + nameEn: "Sierra Leone", + groups: ["011", "202", "002", "UN"], + callingCodes: ["232"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-10.27575, 8.48711], [-10.37257, 8.48941], [-10.54891, 8.31174], [-10.63934, 8.35326], [-10.70565, 8.29235], [-10.61422, 8.5314], [-10.47707, 8.67669], [-10.56197, 8.81225], [-10.5783, 9.06386], [-10.74484, 9.07998], [-10.6534, 9.29919], [-11.2118, 10.00098], [-11.89624, 9.99763], [-11.91023, 9.93927], [-12.12634, 9.87203], [-12.24262, 9.92386], [-12.47254, 9.86834], [-12.76788, 9.3133], [-12.94095, 9.26335], [-13.08953, 9.0409], [-13.18586, 9.0925], [-13.29911, 9.04245], [-14.36218, 8.64107], [-12.15048, 6.15992], [-11.50429, 6.92704], [-11.4027, 6.97746], [-11.29417, 7.21576], [-10.60422, 7.7739], [-10.60492, 8.04072], [-10.57523, 8.04829], [-10.51554, 8.1393], [-10.45023, 8.15627], [-10.35227, 8.15223], [-10.29839, 8.21283], [-10.31635, 8.28554], [-10.30084, 8.30008], [-10.27575, 8.48711]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SM", + iso1A3: "SMR", + iso1N3: "674", + wikidata: "Q238", + nameEn: "San Marino", + groups: ["039", "150", "UN"], + callingCodes: ["378"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[12.45648, 43.89369], [12.48771, 43.89706], [12.49429, 43.90973], [12.49247, 43.91774], [12.49724, 43.92248], [12.50269, 43.92363], [12.50496, 43.93017], [12.51553, 43.94096], [12.51427, 43.94897], [12.50655, 43.95796], [12.50875, 43.96198], [12.50622, 43.97131], [12.51109, 43.97201], [12.51064, 43.98165], [12.5154, 43.98508], [12.51463, 43.99122], [12.50678, 43.99113], [12.49406, 43.98492], [12.47853, 43.98052], [12.46205, 43.97463], [12.44684, 43.96597], [12.43662, 43.95698], [12.42005, 43.9578], [12.41414, 43.95273], [12.40415, 43.95485], [12.40506, 43.94325], [12.41165, 43.93769], [12.41551, 43.92984], [12.40733, 43.92379], [12.41233, 43.90956], [12.40935, 43.9024], [12.41641, 43.89991], [12.44184, 43.90498], [12.45648, 43.89369]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SN", + iso1A3: "SEN", + iso1N3: "686", + wikidata: "Q1041", + nameEn: "Senegal", + groups: ["011", "202", "002", "UN"], + callingCodes: ["221"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-14.32144, 16.61495], [-15.00557, 16.64997], [-15.6509, 16.50315], [-16.27016, 16.51565], [-16.4429, 16.20605], [-16.44814, 16.09753], [-16.48967, 16.0496], [-16.50854, 16.09032], [-17.15288, 16.07139], [-18.35085, 14.63444], [-17.43598, 13.59273], [-15.47902, 13.58758], [-15.36504, 13.79313], [-14.93719, 13.80173], [-14.34721, 13.46578], [-13.8955, 13.59126], [-13.79409, 13.34472], [-14.36795, 13.23033], [-15.14917, 13.57989], [-15.26908, 13.37768], [-15.80478, 13.34832], [-15.80355, 13.16729], [-16.69343, 13.16791], [-16.74676, 13.06025], [-17.43966, 13.04579], [-17.4623, 11.92379], [-16.70562, 12.34803], [-16.38191, 12.36449], [-16.20591, 12.46157], [-15.67302, 12.42974], [-15.17582, 12.6847], [-13.70523, 12.68013], [-13.05296, 12.64003], [-13.06603, 12.49342], [-12.87336, 12.51892], [-12.35415, 12.32758], [-11.91331, 12.42008], [-11.46267, 12.44559], [-11.37536, 12.40788], [-11.39935, 12.97808], [-11.63025, 13.39174], [-11.83345, 13.33333], [-12.06897, 13.71049], [-11.93043, 13.84505], [-12.23936, 14.76324], [-13.11029, 15.52116], [-13.43135, 16.09022], [-13.80075, 16.13961], [-14.32144, 16.61495]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SO", + iso1A3: "SOM", + iso1N3: "706", + wikidata: "Q1045", + nameEn: "Somalia", + groups: ["014", "202", "002", "UN"], + callingCodes: ["252"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SR", + iso1A3: "SUR", + iso1N3: "740", + wikidata: "Q730", + nameEn: "Suriname", + groups: ["005", "419", "019", "UN"], + driveSide: "left", + callingCodes: ["597"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-54.26916, 5.26909], [-54.01877, 5.52789], [-54.01074, 5.68785], [-53.7094, 6.2264], [-56.84822, 6.73257], [-57.31629, 5.33714], [-57.22536, 5.15605], [-57.37442, 5.0208], [-57.8699, 4.89394], [-58.0307, 3.95513], [-57.35891, 3.32121], [-56.70519, 2.02964], [-56.55439, 2.02003], [-56.47045, 1.95135], [-55.99278, 1.83137], [-55.89863, 1.89861], [-55.92159, 2.05236], [-56.13054, 2.27723], [-55.96292, 2.53188], [-55.71493, 2.40342], [-55.01919, 2.564], [-54.6084, 2.32856], [-54.42864, 2.42442], [-54.28534, 2.67798], [-53.9849, 3.58697], [-53.98914, 3.627], [-54.05128, 3.63557], [-54.19367, 3.84387], [-54.38444, 4.13222], [-54.4717, 4.91964], [-54.26916, 5.26909]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SS", + iso1A3: "SSD", + iso1N3: "728", + wikidata: "Q958", + nameEn: "South Sudan", + groups: ["014", "202", "002", "UN"], + callingCodes: ["211"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[34.10229, 9.50238], [33.87958, 9.49937], [33.9082, 9.762], [33.96323, 9.80972], [33.99185, 9.99623], [33.96984, 10.15446], [33.90159, 10.17179], [33.80913, 10.32994], [33.66604, 10.44254], [33.52294, 10.64382], [33.24645, 10.77913], [33.26977, 10.83632], [33.13988, 11.43248], [33.25876, 12.22111], [32.73921, 12.22757], [32.73921, 11.95203], [32.10079, 11.95203], [32.39578, 11.70208], [32.39358, 11.18207], [32.46967, 11.04662], [31.99177, 10.65065], [31.77539, 10.28939], [31.28504, 9.75287], [30.84605, 9.7498], [30.82893, 9.71451], [30.53005, 9.95992], [30.00389, 10.28633], [29.94629, 10.29245], [29.54, 10.07949], [29.53844, 9.75133], [29.06988, 9.74826], [28.99983, 9.67155], [27.90704, 9.61323], [27.14427, 9.62858], [26.70685, 9.48735], [26.35815, 9.57946], [26.21338, 9.91545], [25.93241, 10.17941], [25.93163, 10.38159], [25.78141, 10.42599], [25.0918, 10.33718], [25.05688, 10.06776], [24.97739, 9.9081], [24.84653, 9.80643], [24.49389, 9.79962], [24.12744, 9.73784], [24.09319, 9.66572], [23.69155, 9.67566], [23.62179, 9.53823], [23.64981, 9.44303], [23.64358, 9.28637], [23.56263, 9.19418], [23.4848, 9.16959], [23.44744, 8.99128], [23.59065, 8.99743], [23.51905, 8.71749], [24.25691, 8.69288], [24.13238, 8.36959], [24.35965, 8.26177], [24.85156, 8.16933], [24.98855, 7.96588], [25.25319, 7.8487], [25.29214, 7.66675], [25.20649, 7.61115], [25.20337, 7.50312], [25.35281, 7.42595], [25.37461, 7.33024], [25.90076, 7.09549], [26.38022, 6.63493], [26.32729, 6.36272], [26.58259, 6.1987], [26.51721, 6.09655], [27.22705, 5.71254], [27.22705, 5.62889], [27.28621, 5.56382], [27.23017, 5.37167], [27.26886, 5.25876], [27.44012, 5.07349], [27.56656, 4.89375], [27.65462, 4.89375], [27.76469, 4.79284], [27.79551, 4.59976], [28.20719, 4.35614], [28.6651, 4.42638], [28.8126, 4.48784], [29.03054, 4.48784], [29.22207, 4.34297], [29.43341, 4.50101], [29.49726, 4.7007], [29.82087, 4.56246], [29.79666, 4.37809], [30.06964, 4.13221], [30.1621, 4.10586], [30.22374, 3.93896], [30.27658, 3.95653], [30.47691, 3.83353], [30.55396, 3.84451], [30.57378, 3.74567], [30.56277, 3.62703], [30.78512, 3.67097], [30.80713, 3.60506], [30.85997, 3.5743], [30.85153, 3.48867], [30.97601, 3.693], [31.16666, 3.79853], [31.29476, 3.8015], [31.50478, 3.67814], [31.50776, 3.63652], [31.72075, 3.74354], [31.81459, 3.82083], [31.86821, 3.78664], [31.96205, 3.6499], [31.95907, 3.57408], [32.05187, 3.589], [32.08491, 3.56287], [32.08866, 3.53543], [32.19888, 3.50867], [32.20782, 3.6053], [32.41337, 3.748], [32.72021, 3.77327], [32.89746, 3.81339], [33.02852, 3.89296], [33.18356, 3.77812], [33.51264, 3.75068], [33.9873, 4.23316], [34.47601, 4.72162], [35.34151, 5.02364], [35.30992, 4.90402], [35.47843, 4.91872], [35.42366, 4.76969], [35.51424, 4.61643], [35.9419, 4.61933], [35.82118, 4.77382], [35.81968, 5.10757], [35.8576, 5.33413], [35.50792, 5.42431], [35.29938, 5.34042], [35.31188, 5.50106], [35.13058, 5.62118], [35.12611, 5.68937], [35.00546, 5.89387], [34.96227, 6.26415], [35.01738, 6.46991], [34.87736, 6.60161], [34.77459, 6.5957], [34.65096, 6.72589], [34.53776, 6.74808], [34.53925, 6.82794], [34.47669, 6.91076], [34.35753, 6.91963], [34.19369, 7.04382], [34.19369, 7.12807], [34.01495, 7.25664], [34.03878, 7.27437], [34.02984, 7.36449], [33.87642, 7.5491], [33.71407, 7.65983], [33.44745, 7.7543], [33.32531, 7.71297], [33.24637, 7.77939], [33.04944, 7.78989], [33.0006, 7.90333], [33.08401, 8.05822], [33.18083, 8.13047], [33.1853, 8.29264], [33.19721, 8.40317], [33.3119, 8.45474], [33.54575, 8.47094], [33.66938, 8.44442], [33.71407, 8.3678], [33.87195, 8.41938], [33.89579, 8.4842], [34.01346, 8.50041], [34.14453, 8.60204], [34.14304, 9.04654], [34.10229, 9.50238]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "ST", + iso1A3: "STP", + iso1N3: "678", + wikidata: "Q1039", + nameEn: "S\xE3o Tom\xE9 and Principe", + groups: ["017", "202", "002", "UN"], + callingCodes: ["239"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[4.34149, 1.91417], [6.6507, -0.28606], [7.9035, 1.92304], [4.34149, 1.91417]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SV", + iso1A3: "SLV", + iso1N3: "222", + wikidata: "Q792", + nameEn: "El Salvador", + groups: ["013", "003", "419", "019", "UN"], + callingCodes: ["503"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-89.34776, 14.43013], [-89.39028, 14.44561], [-89.57441, 14.41637], [-89.58814, 14.33165], [-89.50614, 14.26084], [-89.52397, 14.22628], [-89.61844, 14.21937], [-89.70756, 14.1537], [-89.75569, 14.07073], [-89.73251, 14.04133], [-89.76103, 14.02923], [-89.81807, 14.07073], [-89.88937, 14.0396], [-90.10505, 13.85104], [-90.11344, 13.73679], [-90.55276, 12.8866], [-88.11443, 12.63306], [-87.7346, 13.13228], [-87.55124, 13.12523], [-87.69751, 13.25228], [-87.73714, 13.32715], [-87.80177, 13.35689], [-87.84675, 13.41078], [-87.83467, 13.44655], [-87.77354, 13.45767], [-87.73841, 13.44169], [-87.72115, 13.46083], [-87.71657, 13.50577], [-87.78148, 13.52906], [-87.73106, 13.75443], [-87.68821, 13.80829], [-87.7966, 13.91353], [-88.00331, 13.86948], [-88.07641, 13.98447], [-88.23018, 13.99915], [-88.25791, 13.91108], [-88.48982, 13.86458], [-88.49738, 13.97224], [-88.70661, 14.04317], [-88.73182, 14.10919], [-88.815, 14.11652], [-88.85785, 14.17763], [-88.94608, 14.20207], [-89.04187, 14.33644], [-89.34776, 14.43013]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SX", + iso1A3: "SXM", + iso1N3: "534", + wikidata: "Q26273", + nameEn: "Sint Maarten", + aliases: ["NL-SX"], + country: "NL", + groups: ["Q1451600", "029", "003", "419", "019", "UN"], + callingCodes: ["1 721"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SY", + iso1A3: "SYR", + iso1N3: "760", + wikidata: "Q858", + nameEn: "Syria", + groups: ["145", "142", "UN"], + callingCodes: ["963"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[42.23683, 37.2863], [42.21548, 37.28026], [42.20454, 37.28715], [42.22381, 37.30238], [42.22257, 37.31395], [42.2112, 37.32491], [42.19301, 37.31323], [42.18225, 37.28569], [42.00894, 37.17209], [41.515, 37.08084], [41.21937, 37.07665], [40.90856, 37.13147], [40.69136, 37.0996], [39.81589, 36.75538], [39.21538, 36.66834], [39.03217, 36.70911], [38.74042, 36.70629], [38.55908, 36.84429], [38.38859, 36.90064], [38.21064, 36.91842], [37.81974, 36.76055], [37.68048, 36.75065], [37.49103, 36.66904], [37.47253, 36.63243], [37.21988, 36.6736], [37.16177, 36.66069], [37.10894, 36.6704], [37.08279, 36.63495], [37.02088, 36.66422], [37.01647, 36.69512], [37.04619, 36.71101], [37.04399, 36.73483], [36.99886, 36.74012], [36.99557, 36.75997], [36.66727, 36.82901], [36.61581, 36.74629], [36.62681, 36.71189], [36.57398, 36.65186], [36.58829, 36.58295], [36.54206, 36.49539], [36.6081, 36.33772], [36.65653, 36.33861], [36.68672, 36.23677], [36.6125, 36.22592], [36.50463, 36.2419], [36.4617, 36.20461], [36.39206, 36.22088], [36.37474, 36.01163], [36.33956, 35.98687], [36.30099, 36.00985], [36.28338, 36.00273], [36.29769, 35.96086], [36.27678, 35.94839], [36.25366, 35.96264], [36.19973, 35.95195], [36.17441, 35.92076], [36.1623, 35.80925], [36.14029, 35.81015], [36.13919, 35.83692], [36.11827, 35.85923], [35.99829, 35.88242], [36.01844, 35.92403], [36.00514, 35.94113], [35.98499, 35.94107], [35.931, 35.92109], [35.51152, 36.10954], [35.48515, 34.70851], [35.97386, 34.63322], [35.98718, 34.64977], [36.29165, 34.62991], [36.32399, 34.69334], [36.35135, 34.68516], [36.35384, 34.65447], [36.42941, 34.62505], [36.46003, 34.6378], [36.45299, 34.59438], [36.41429, 34.61175], [36.39846, 34.55672], [36.3369, 34.52629], [36.34745, 34.5002], [36.4442, 34.50165], [36.46179, 34.46541], [36.55853, 34.41609], [36.53039, 34.3798], [36.56556, 34.31881], [36.60778, 34.31009], [36.58667, 34.27667], [36.59195, 34.2316], [36.62537, 34.20251], [36.5128, 34.09916], [36.50576, 34.05982], [36.41078, 34.05253], [36.28589, 33.91981], [36.38263, 33.86579], [36.3967, 33.83365], [36.14517, 33.85118], [36.06778, 33.82927], [35.9341, 33.6596], [36.05723, 33.57904], [35.94465, 33.52774], [35.94816, 33.47886], [35.88668, 33.43183], [35.82577, 33.40479], [35.81324, 33.36354], [35.77477, 33.33609], [35.813, 33.3172], [35.77513, 33.27342], [35.81295, 33.24841], [35.81647, 33.2028], [35.83846, 33.19397], [35.84285, 33.16673], [35.81911, 33.1336], [35.81911, 33.11077], [35.84802, 33.1031], [35.87188, 32.98028], [35.89298, 32.9456], [35.87012, 32.91976], [35.84021, 32.8725], [35.83758, 32.82817], [35.78745, 32.77938], [35.75983, 32.74803], [35.88405, 32.71321], [35.93307, 32.71966], [35.96633, 32.66237], [36.02239, 32.65911], [36.08074, 32.51463], [36.20379, 32.52751], [36.20875, 32.49529], [36.23948, 32.50108], [36.40959, 32.37908], [36.83946, 32.31293], [38.79171, 33.37328], [40.64314, 34.31604], [40.97676, 34.39788], [41.12388, 34.65742], [41.2345, 34.80049], [41.21654, 35.1508], [41.26569, 35.42708], [41.38184, 35.62502], [41.37027, 35.84095], [41.2564, 36.06012], [41.28864, 36.35368], [41.40058, 36.52502], [41.81736, 36.58782], [42.36697, 37.0627], [42.35724, 37.10998], [42.32313, 37.17814], [42.34735, 37.22548], [42.2824, 37.2798], [42.26039, 37.27017], [42.23683, 37.2863]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "SZ", + iso1A3: "SWZ", + iso1N3: "748", + wikidata: "Q1050", + nameEn: "Eswatini", + aliases: ["Swaziland"], + groups: ["018", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["268"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[31.86881, -25.99973], [31.4175, -25.71886], [31.31237, -25.7431], [31.13073, -25.91558], [30.95819, -26.26303], [30.78927, -26.48271], [30.81101, -26.84722], [30.88826, -26.79622], [30.97757, -26.92706], [30.96088, -27.0245], [31.15027, -27.20151], [31.49834, -27.31549], [31.97592, -27.31675], [31.97463, -27.11057], [32.00893, -26.8096], [32.09664, -26.80721], [32.13315, -26.84345], [32.13409, -26.5317], [32.07352, -26.40185], [32.10435, -26.15656], [32.08599, -26.00978], [32.00916, -25.999], [31.974, -25.95387], [31.86881, -25.99973]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TA", + iso1A3: "TAA", + wikidata: "Q220982", + nameEn: "Tristan da Cunha", + aliases: ["SH-TA"], + country: "GB", + groups: ["SH", "BOTS", "011", "202", "002", "UN"], + isoStatus: "excRes", + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["290 8", "44 20"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-13.38232, -34.07258], [-16.67337, -41.9188], [-5.88482, -41.4829], [-13.38232, -34.07258]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TC", + iso1A3: "TCA", + iso1N3: "796", + wikidata: "Q18221", + nameEn: "Turks and Caicos Islands", + country: "GB", + groups: ["BOTS", "029", "003", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1 649"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-71.70065, 25.7637], [-72.98446, 20.4801], [-69.80718, 21.35956], [-71.70065, 25.7637]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TD", + iso1A3: "TCD", + iso1N3: "148", + wikidata: "Q657", + nameEn: "Chad", + groups: ["017", "202", "002", "UN"], + callingCodes: ["235"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[23.99539, 19.49944], [15.99566, 23.49639], [14.99751, 23.00539], [15.19692, 21.99339], [15.20213, 21.49365], [15.28332, 21.44557], [15.62515, 20.95395], [15.57248, 20.92138], [15.55382, 20.86507], [15.56004, 20.79488], [15.59841, 20.74039], [15.6721, 20.70069], [15.99632, 20.35364], [15.75098, 19.93002], [15.6032, 18.77402], [15.50373, 16.89649], [14.37425, 15.72591], [13.86301, 15.04043], [13.78991, 14.87519], [13.809, 14.72915], [13.67878, 14.64013], [13.68573, 14.55276], [13.48259, 14.46704], [13.47559, 14.40881], [13.6302, 13.71094], [14.08251, 13.0797], [14.46881, 13.08259], [14.56101, 12.91036], [14.55058, 12.78256], [14.83314, 12.62963], [14.90827, 12.3269], [14.89019, 12.16593], [14.96952, 12.0925], [15.00146, 12.1223], [15.0349, 12.10698], [15.05786, 12.0608], [15.04808, 11.8731], [15.11579, 11.79313], [15.06595, 11.71126], [15.13149, 11.5537], [15.0585, 11.40481], [15.10021, 11.04101], [15.04957, 11.02347], [15.09127, 10.87431], [15.06737, 10.80921], [15.15532, 10.62846], [15.14936, 10.53915], [15.23724, 10.47764], [15.30874, 10.31063], [15.50535, 10.1098], [15.68761, 9.99344], [15.41408, 9.92876], [15.24618, 9.99246], [15.14043, 9.99246], [15.05999, 9.94845], [14.95722, 9.97926], [14.80082, 9.93818], [14.4673, 10.00264], [14.20411, 10.00055], [14.1317, 9.82413], [14.01793, 9.73169], [13.97544, 9.6365], [14.37094, 9.2954], [14.35707, 9.19611], [14.83566, 8.80557], [15.09484, 8.65982], [15.20426, 8.50892], [15.50743, 7.79302], [15.59272, 7.7696], [15.56964, 7.58936], [15.49743, 7.52179], [15.73118, 7.52006], [15.79942, 7.44149], [16.40703, 7.68809], [16.41583, 7.77971], [16.58315, 7.88657], [16.59415, 7.76444], [16.658, 7.75353], [16.6668, 7.67281], [16.8143, 7.53971], [17.67288, 7.98905], [17.93926, 7.95853], [18.02731, 8.01085], [18.6085, 8.05009], [18.64153, 8.08714], [18.62612, 8.14163], [18.67455, 8.22226], [18.79783, 8.25929], [19.11044, 8.68172], [18.86388, 8.87971], [19.06421, 9.00367], [20.36748, 9.11019], [20.82979, 9.44696], [21.26348, 9.97642], [21.34934, 9.95907], [21.52766, 10.2105], [21.63553, 10.217], [21.71479, 10.29932], [21.72139, 10.64136], [22.45889, 11.00246], [22.87758, 10.91915], [22.97249, 11.21955], [22.93124, 11.41645], [22.7997, 11.40424], [22.54907, 11.64372], [22.64092, 12.07485], [22.48369, 12.02766], [22.50548, 12.16769], [22.38873, 12.45514], [22.46345, 12.61925], [22.22684, 12.74682], [22.15679, 12.66634], [21.98711, 12.63292], [21.89371, 12.68001], [21.81432, 12.81362], [21.94819, 13.05637], [22.02914, 13.13976], [22.1599, 13.19281], [22.29689, 13.3731], [22.08674, 13.77863], [22.22995, 13.96754], [22.5553, 14.11704], [22.55997, 14.23024], [22.44944, 14.24986], [22.38562, 14.58907], [22.70474, 14.69149], [22.66115, 14.86308], [22.99584, 15.22989], [22.99584, 15.40105], [22.92579, 15.47007], [22.93201, 15.55107], [23.10792, 15.71297], [23.38812, 15.69649], [23.62785, 15.7804], [23.99997, 15.69575], [23.99539, 19.49944]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TF", + iso1A3: "ATF", + iso1N3: "260", + wikidata: "Q129003", + nameEn: "French Southern Territories", + country: "FR" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "TG", + iso1A3: "TGO", + iso1N3: "768", + wikidata: "Q945", + nameEn: "Togo", + groups: ["011", "202", "002", "UN"], + callingCodes: ["228"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[0.50388, 11.01011], [-0.13493, 11.14075], [-0.14462, 11.10811], [-0.05733, 11.08628], [-0.0275, 11.11202], [-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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TH", + iso1A3: "THA", + iso1N3: "764", + wikidata: "Q869", + nameEn: "Thailand", + groups: ["035", "142", "UN"], + driveSide: "left", + callingCodes: ["66"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[100.08404, 20.36626], [99.95721, 20.46301], [99.91616, 20.44986], [99.90499, 20.4487], [99.89692, 20.44789], [99.89301, 20.44311], [99.89168, 20.44548], [99.88451, 20.44596], [99.88211, 20.44488], [99.86383, 20.44371], [99.81096, 20.33687], [99.68255, 20.32077], [99.46008, 20.39673], [99.46077, 20.36198], [99.5569, 20.20676], [99.52943, 20.14811], [99.416, 20.08614], [99.20328, 20.12877], [99.0735, 20.10298], [98.98679, 19.7419], [98.83661, 19.80931], [98.56065, 19.67807], [98.51182, 19.71303], [98.24884, 19.67876], [98.13829, 19.78541], [98.03314, 19.80941], [98.04364, 19.65755], [97.84715, 19.55782], [97.88423, 19.5041], [97.78769, 19.39429], [97.84186, 19.29526], [97.78606, 19.26769], [97.84024, 19.22217], [97.83479, 19.09972], [97.73797, 19.04261], [97.73654, 18.9812], [97.66487, 18.9371], [97.73836, 18.88478], [97.76752, 18.58097], [97.5258, 18.4939], [97.36444, 18.57138], [97.34522, 18.54596], [97.50383, 18.26844], [97.56219, 18.33885], [97.64116, 18.29778], [97.60841, 18.23846], [97.73723, 17.97912], [97.66794, 17.88005], [97.76407, 17.71595], [97.91829, 17.54504], [98.11185, 17.36829], [98.10439, 17.33847], [98.34566, 17.04822], [98.39441, 17.06266], [98.52624, 16.89979], [98.49603, 16.8446], [98.53833, 16.81934], [98.46994, 16.73613], [98.50253, 16.7139], [98.49713, 16.69022], [98.51043, 16.70107], [98.51579, 16.69433], [98.51472, 16.68521], [98.51833, 16.676], [98.51113, 16.64503], [98.5695, 16.62826], [98.57912, 16.55983], [98.63817, 16.47424], [98.68074, 16.27068], [98.84485, 16.42354], [98.92656, 16.36425], [98.8376, 16.11706], [98.69585, 16.13353], [98.57019, 16.04578], [98.59853, 15.87197], [98.541, 15.65406], [98.58598, 15.46821], [98.56027, 15.33471], [98.4866, 15.39154], [98.39351, 15.34177], [98.41906, 15.27103], [98.40522, 15.25268], [98.30446, 15.30667], [98.22, 15.21327], [98.18821, 15.13125], [98.24874, 14.83013], [98.56762, 14.37701], [98.97356, 14.04868], [99.16695, 13.72621], [99.20617, 13.20575], [99.12225, 13.19847], [99.10646, 13.05804], [99.18748, 12.9898], [99.18905, 12.84799], [99.29254, 12.68921], [99.409, 12.60603], [99.47519, 12.1353], [99.56445, 12.14805], [99.53424, 12.02317], [99.64891, 11.82699], [99.64108, 11.78948], [99.5672, 11.62732], [99.47598, 11.62434], [99.39485, 11.3925], [99.31573, 11.32081], [99.32756, 11.28545], [99.06938, 10.94857], [99.02337, 10.97217], [98.99701, 10.92962], [99.0069, 10.85485], [98.86819, 10.78336], [98.78511, 10.68351], [98.77275, 10.62548], [98.81944, 10.52761], [98.7391, 10.31488], [98.55174, 9.92804], [98.52291, 9.92389], [98.47298, 9.95782], [98.33094, 9.91973], [98.12555, 9.44056], [97.63455, 9.60854], [97.19814, 8.18901], [99.31854, 5.99868], [99.50117, 6.44501], [99.91873, 6.50233], [100.0756, 6.4045], [100.12, 6.42105], [100.19511, 6.72559], [100.29651, 6.68439], [100.30828, 6.66462], [100.31618, 6.66781], [100.31884, 6.66423], [100.32671, 6.66526], [100.32607, 6.65933], [100.31929, 6.65413], [100.35413, 6.54932], [100.41152, 6.52299], [100.41791, 6.5189], [100.42351, 6.51762], [100.43027, 6.52389], [100.66986, 6.45086], [100.74361, 6.50811], [100.74822, 6.46231], [100.81045, 6.45086], [100.85884, 6.24929], [101.10313, 6.25617], [101.12618, 6.19431], [101.06165, 6.14161], [101.12388, 6.11411], [101.087, 5.9193], [101.02708, 5.91013], [100.98815, 5.79464], [101.14062, 5.61613], [101.25755, 5.71065], [101.25524, 5.78633], [101.58019, 5.93534], [101.69773, 5.75881], [101.75074, 5.79091], [101.80144, 5.74505], [101.89188, 5.8386], [101.91776, 5.84269], [101.92819, 5.85511], [101.94712, 5.98421], [101.9714, 6.00575], [101.97114, 6.01992], [101.99209, 6.04075], [102.01835, 6.05407], [102.09182, 6.14161], [102.07732, 6.193], [102.08127, 6.22679], [102.09086, 6.23546], [102.46318, 7.22462], [102.47649, 9.66162], [102.52395, 11.25257], [102.91449, 11.65512], [102.90973, 11.75613], [102.83957, 11.8519], [102.78427, 11.98746], [102.77026, 12.06815], [102.70176, 12.1686], [102.73134, 12.37091], [102.78116, 12.40284], [102.7796, 12.43781], [102.57567, 12.65358], [102.51963, 12.66117], [102.4994, 12.71736], [102.53053, 12.77506], [102.49335, 12.92711], [102.48694, 12.97537], [102.52275, 12.99813], [102.46011, 13.08057], [102.43422, 13.09061], [102.36146, 13.26006], [102.36001, 13.31142], [102.34611, 13.35618], [102.35692, 13.38274], [102.35563, 13.47307], [102.361, 13.50551], [102.33828, 13.55613], [102.36859, 13.57488], [102.44601, 13.5637], [102.5358, 13.56933], [102.57573, 13.60461], [102.62483, 13.60883], [102.58635, 13.6286], [102.5481, 13.6589], [102.56848, 13.69366], [102.72727, 13.77806], [102.77864, 13.93374], [102.91251, 14.01531], [102.93275, 14.19044], [103.16469, 14.33075], [103.39353, 14.35639], [103.53518, 14.42575], [103.71109, 14.4348], [103.70175, 14.38052], [103.93836, 14.3398], [104.27616, 14.39861], [104.55014, 14.36091], [104.69335, 14.42726], [104.97667, 14.38806], [105.02804, 14.23722], [105.08408, 14.20402], [105.14012, 14.23873], [105.17748, 14.34432], [105.20894, 14.34967], [105.43783, 14.43865], [105.53864, 14.55731], [105.5121, 14.80802], [105.61162, 15.00037], [105.46661, 15.13132], [105.58043, 15.32724], [105.50662, 15.32054], [105.4692, 15.33709], [105.47635, 15.3796], [105.58191, 15.41031], [105.60446, 15.53301], [105.61756, 15.68792], [105.46573, 15.74742], [105.42285, 15.76971], [105.37959, 15.84074], [105.34115, 15.92737], [105.38508, 15.987], [105.42001, 16.00657], [105.06204, 16.09792], [105.00262, 16.25627], [104.88057, 16.37311], [104.73349, 16.565], [104.76099, 16.69302], [104.7397, 16.81005], [104.76442, 16.84752], [104.7373, 16.91125], [104.73712, 17.01404], [104.80716, 17.19025], [104.80061, 17.39367], [104.69867, 17.53038], [104.45404, 17.66788], [104.35432, 17.82871], [104.2757, 17.86139], [104.21776, 17.99335], [104.10927, 18.10826], [104.06533, 18.21656], [103.97725, 18.33631], [103.93916, 18.33914], [103.85642, 18.28666], [103.82449, 18.33979], [103.699, 18.34125], [103.60957, 18.40528], [103.47773, 18.42841], [103.41044, 18.4486], [103.30977, 18.4341], [103.24779, 18.37807], [103.23818, 18.34875], [103.29757, 18.30475], [103.17093, 18.2618], [103.14994, 18.23172], [103.1493, 18.17799], [103.07343, 18.12351], [103.07823, 18.03833], [103.0566, 18.00144], [103.01998, 17.97095], [102.9912, 17.9949], [102.95812, 18.0054], [102.86323, 17.97531], [102.81988, 17.94233], [102.79044, 17.93612], [102.75954, 17.89561], [102.68538, 17.86653], [102.67543, 17.84529], [102.69946, 17.81686], [102.68194, 17.80151], [102.59485, 17.83537], [102.5896, 17.84889], [102.61432, 17.92273], [102.60971, 17.95411], [102.59234, 17.96127], [102.45523, 17.97106], [102.11359, 18.21532], [101.88485, 18.02474], [101.78087, 18.07559], [101.72294, 17.92867], [101.44667, 17.7392], [101.15108, 17.47586], [100.96541, 17.57926], [101.02185, 17.87637], [101.1793, 18.0544], [101.19118, 18.2125], [101.15108, 18.25624], [101.18227, 18.34367], [101.06047, 18.43247], [101.27585, 18.68875], [101.22832, 18.73377], [101.25803, 18.89545], [101.35606, 19.04716], [101.261, 19.12717], [101.24911, 19.33334], [101.20604, 19.35296], [101.21347, 19.46223], [101.26991, 19.48324], [101.26545, 19.59242], [101.08928, 19.59748], [100.90302, 19.61901], [100.77231, 19.48324], [100.64606, 19.55884], [100.58219, 19.49164], [100.49604, 19.53504], [100.398, 19.75047], [100.5094, 19.87904], [100.58808, 20.15791], [100.55218, 20.17741], [100.51052, 20.14928], [100.47567, 20.19133], [100.4537, 20.19971], [100.44992, 20.23644], [100.41473, 20.25625], [100.37439, 20.35156], [100.33383, 20.4028], [100.25769, 20.3992], [100.22076, 20.31598], [100.16668, 20.2986], [100.1712, 20.24324], [100.11785, 20.24787], [100.09337, 20.26293], [100.09999, 20.31614], [100.08404, 20.36626]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TJ", + iso1A3: "TJK", + iso1N3: "762", + wikidata: "Q863", + nameEn: "Tajikistan", + groups: ["143", "142", "UN"], + callingCodes: ["992"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[70.45251, 41.04438], [70.38028, 41.02014], [70.36655, 40.90296], [69.69434, 40.62615], [69.59441, 40.70181], [69.53021, 40.77621], [69.38327, 40.7918], [69.32834, 40.70233], [69.3455, 40.57988], [69.2643, 40.57506], [69.21063, 40.54469], [69.27066, 40.49274], [69.28525, 40.41894], [69.30774, 40.36102], [69.33794, 40.34819], [69.32833, 40.29794], [69.30808, 40.2821], [69.24817, 40.30357], [69.25229, 40.26362], [69.30104, 40.24502], [69.30448, 40.18774], [69.2074, 40.21488], [69.15659, 40.2162], [69.04544, 40.22904], [68.85832, 40.20885], [68.84357, 40.18604], [68.79276, 40.17555], [68.77902, 40.20492], [68.5332, 40.14826], [68.52771, 40.11676], [68.62796, 40.07789], [69.01523, 40.15771], [69.01935, 40.11466], [68.96579, 40.06949], [68.84906, 40.04952], [68.93695, 39.91167], [68.88889, 39.87163], [68.63071, 39.85265], [68.61972, 39.68905], [68.54166, 39.53929], [68.12053, 39.56317], [67.70992, 39.66156], [67.62889, 39.60234], [67.44899, 39.57799], [67.46547, 39.53564], [67.39681, 39.52505], [67.46822, 39.46146], [67.45998, 39.315], [67.36522, 39.31287], [67.33226, 39.23739], [67.67833, 39.14479], [67.68915, 39.00775], [68.09704, 39.02589], [68.19743, 38.85985], [68.06948, 38.82115], [68.12877, 38.73677], [68.05598, 38.71641], [68.0807, 38.64136], [68.05873, 38.56087], [68.11366, 38.47169], [68.06274, 38.39435], [68.13289, 38.40822], [68.40343, 38.19484], [68.27159, 37.91477], [68.12635, 37.93], [67.81566, 37.43107], [67.8474, 37.31594], [67.78329, 37.1834], [67.7803, 37.08978], [67.87917, 37.0591], [68.02194, 36.91923], [68.18542, 37.02074], [68.27605, 37.00977], [68.29253, 37.10621], [68.41201, 37.10402], [68.41888, 37.13906], [68.61851, 37.19815], [68.6798, 37.27906], [68.81438, 37.23862], [68.80889, 37.32494], [68.91189, 37.26704], [68.88168, 37.33368], [68.96407, 37.32603], [69.03274, 37.25174], [69.25152, 37.09426], [69.39529, 37.16752], [69.45022, 37.23315], [69.36645, 37.40462], [69.44954, 37.4869], [69.51888, 37.5844], [69.80041, 37.5746], [69.84435, 37.60616], [69.93362, 37.61378], [69.95971, 37.5659], [70.15015, 37.52519], [70.28243, 37.66706], [70.27694, 37.81258], [70.1863, 37.84296], [70.17206, 37.93276], [70.4898, 38.12546], [70.54673, 38.24541], [70.60407, 38.28046], [70.61526, 38.34774], [70.64966, 38.34999], [70.69189, 38.37031], [70.6761, 38.39144], [70.67438, 38.40597], [70.69807, 38.41861], [70.72485, 38.4131], [70.75455, 38.4252], [70.77132, 38.45548], [70.78581, 38.45502], [70.78702, 38.45031], [70.79766, 38.44944], [70.80521, 38.44447], [70.81697, 38.44507], [70.82538, 38.45394], [70.84376, 38.44688], [70.88719, 38.46826], [70.92728, 38.43021], [70.98693, 38.48862], [71.03545, 38.44779], [71.0556, 38.40176], [71.09542, 38.42517], [71.10592, 38.42077], [71.10957, 38.40671], [71.1451, 38.40106], [71.21291, 38.32797], [71.33114, 38.30339], [71.33869, 38.27335], [71.37803, 38.25641], [71.36444, 38.15358], [71.29878, 38.04429], [71.28922, 38.01272], [71.27622, 37.99946], [71.27278, 37.96496], [71.24969, 37.93031], [71.2809, 37.91995], [71.296, 37.93403], [71.32871, 37.88564], [71.51565, 37.95349], [71.58843, 37.92425], [71.59255, 37.79956], [71.55752, 37.78677], [71.54324, 37.77104], [71.53053, 37.76534], [71.55234, 37.73209], [71.54186, 37.69691], [71.51972, 37.61945], [71.5065, 37.60912], [71.49693, 37.53527], [71.50616, 37.50733], [71.5256, 37.47971], [71.49612, 37.4279], [71.47685, 37.40281], [71.4862, 37.33405], [71.49821, 37.31975], [71.50674, 37.31502], [71.48536, 37.26017], [71.4824, 37.24921], [71.48339, 37.23937], [71.47386, 37.2269], [71.4555, 37.21418], [71.4494, 37.18137], [71.44127, 37.11856], [71.43097, 37.05855], [71.45578, 37.03094], [71.46923, 36.99925], [71.48481, 36.93218], [71.51502, 36.89128], [71.57195, 36.74943], [71.67083, 36.67346], [71.83229, 36.68084], [72.31676, 36.98115], [72.54095, 37.00007], [72.66381, 37.02014], [72.79693, 37.22222], [73.06884, 37.31729], [73.29633, 37.46495], [73.77197, 37.4417], [73.76647, 37.33913], [73.61129, 37.27469], [73.64974, 37.23643], [73.82552, 37.22659], [73.8564, 37.26158], [74.20308, 37.34208], [74.23339, 37.41116], [74.41055, 37.3948], [74.56161, 37.37734], [74.68383, 37.3948], [74.8294, 37.3435], [74.88887, 37.23275], [75.12328, 37.31839], [75.09719, 37.37297], [75.15899, 37.41443], [75.06011, 37.52779], [74.94338, 37.55501], [74.8912, 37.67576], [75.00935, 37.77486], [74.92416, 37.83428], [74.9063, 38.03033], [74.82665, 38.07359], [74.80331, 38.19889], [74.69894, 38.22155], [74.69619, 38.42947], [74.51217, 38.47034], [74.17022, 38.65504], [73.97933, 38.52945], [73.79806, 38.61106], [73.80656, 38.66449], [73.7033, 38.84782], [73.7445, 38.93867], [73.82964, 38.91517], [73.81728, 39.04007], [73.75823, 39.023], [73.60638, 39.24534], [73.54572, 39.27567], [73.55396, 39.3543], [73.5004, 39.38402], [73.59241, 39.40843], [73.59831, 39.46425], [73.45096, 39.46677], [73.31912, 39.38615], [73.18454, 39.35536], [72.85934, 39.35116], [72.62027, 39.39696], [72.33173, 39.33093], [72.23834, 39.17248], [72.17242, 39.2661], [72.09689, 39.26823], [72.04059, 39.36704], [71.90601, 39.27674], [71.79202, 39.27355], [71.7522, 39.32031], [71.80164, 39.40631], [71.76816, 39.45456], [71.62688, 39.44056], [71.5517, 39.45722], [71.55856, 39.57588], [71.49814, 39.61397], [71.08752, 39.50704], [71.06418, 39.41586], [70.7854, 39.38933], [70.64087, 39.58792], [70.44757, 39.60128], [70.2869, 39.53141], [70.11111, 39.58223], [69.87491, 39.53882], [69.68677, 39.59281], [69.3594, 39.52516], [69.26938, 39.8127], [69.35649, 40.01994], [69.43134, 39.98431], [69.43557, 39.92877], [69.53615, 39.93991], [69.5057, 40.03277], [69.53855, 40.0887], [69.53794, 40.11833], [69.55555, 40.12296], [69.57615, 40.10524], [69.64704, 40.12165], [69.67001, 40.10639], [70.01283, 40.23288], [70.58297, 40.00891], [70.57384, 39.99394], [70.47557, 39.93216], [70.55033, 39.96619], [70.58912, 39.95211], [70.65946, 39.9878], [70.65827, 40.0981], [70.7928, 40.12797], [70.80495, 40.16813], [70.9818, 40.22392], [70.8607, 40.217], [70.62342, 40.17396], [70.56394, 40.26421], [70.57149, 40.3442], [70.37511, 40.38605], [70.32626, 40.45174], [70.49871, 40.52503], [70.80009, 40.72825], [70.45251, 41.04438]]], [[[70.68112, 40.90612], [70.6158, 40.97661], [70.56077, 41.00642], [70.54223, 40.98787], [70.57501, 40.98941], [70.6721, 40.90555], [70.68112, 40.90612]]], [[[70.74189, 39.86319], [70.53651, 39.89155], [70.52631, 39.86989], [70.54998, 39.85137], [70.59667, 39.83542], [70.63105, 39.77923], [70.74189, 39.86319]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TK", + iso1A3: "TKL", + iso1N3: "772", + wikidata: "Q36823", + nameEn: "Tokelau", + country: "NZ", + groups: ["061", "009", "UN"], + driveSide: "left", + callingCodes: ["690"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-168.251, -9.44289], [-174.18635, -7.80441], [-174.17993, -10.13616], [-168.251, -9.44289]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TL", + iso1A3: "TLS", + iso1N3: "626", + wikidata: "Q574", + nameEn: "East Timor", + aliases: ["Timor-Leste", "TP"], + groups: ["035", "142", "UN"], + driveSide: "left", + callingCodes: ["670"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[124.46701, -9.13002], [124.94011, -8.85617], [124.97742, -9.08128], [125.11764, -8.96359], [125.18632, -9.03142], [125.18907, -9.16434], [125.09434, -9.19669], [125.04044, -9.17093], [124.97892, -9.19281], [125.09025, -9.46406], [125.68138, -9.85176], [127.55165, -9.05052], [127.42116, -8.22471], [125.87691, -8.31789], [125.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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TM", + iso1A3: "TKM", + iso1N3: "795", + wikidata: "Q874", + nameEn: "Turkmenistan", + groups: ["143", "142", "UN"], + callingCodes: ["993"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[60.5078, 41.21694], [60.06581, 41.4363], [60.18117, 41.60082], [60.06032, 41.76287], [60.08504, 41.80997], [60.33223, 41.75058], [59.95046, 41.97966], [60.0356, 42.01028], [60.04659, 42.08982], [59.96419, 42.1428], [60.00539, 42.212], [59.94633, 42.27655], [59.4341, 42.29738], [59.2955, 42.37064], [59.17317, 42.52248], [58.93422, 42.5407], [58.6266, 42.79314], [58.57991, 42.64988], [58.27504, 42.69632], [58.14321, 42.62159], [58.29427, 42.56497], [58.51674, 42.30348], [58.40688, 42.29535], [58.3492, 42.43335], [57.99214, 42.50021], [57.90975, 42.4374], [57.92897, 42.24047], [57.84932, 42.18555], [57.6296, 42.16519], [57.30275, 42.14076], [57.03633, 41.92043], [56.96218, 41.80383], [57.03359, 41.41777], [57.13796, 41.36625], [57.03423, 41.25435], [56.00314, 41.32584], [55.45471, 41.25609], [54.95182, 41.92424], [54.20635, 42.38477], [52.97575, 42.1308], [52.47884, 41.78034], [52.26048, 41.69249], [51.7708, 40.29239], [53.89734, 37.3464], [54.24565, 37.32047], [54.36211, 37.34912], [54.58664, 37.45809], [54.67247, 37.43532], [54.77822, 37.51597], [54.81804, 37.61285], [54.77684, 37.62264], [54.851, 37.75739], [55.13412, 37.94705], [55.44152, 38.08564], [55.76561, 38.12238], [55.97847, 38.08024], [56.33278, 38.08132], [56.32454, 38.18502], [56.43303, 38.26054], [56.62255, 38.24005], [56.73928, 38.27887], [57.03453, 38.18717], [57.21169, 38.28965], [57.37236, 38.09321], [57.35042, 37.98546], [57.79534, 37.89299], [58.21399, 37.77281], [58.22999, 37.6856], [58.39959, 37.63134], [58.47786, 37.6433], [58.5479, 37.70526], [58.6921, 37.64548], [58.9338, 37.67374], [59.22905, 37.51161], [59.33507, 37.53146], [59.39797, 37.47892], [59.39385, 37.34257], [59.55178, 37.13594], [59.74678, 37.12499], [60.00768, 37.04102], [60.34767, 36.63214], [61.14516, 36.64644], [61.18187, 36.55348], [61.1393, 36.38782], [61.22719, 36.12759], [61.12007, 35.95992], [61.22444, 35.92879], [61.26152, 35.80749], [61.22719, 35.67038], [61.27371, 35.61482], [61.58742, 35.43803], [61.77693, 35.41341], [61.97743, 35.4604], [62.05709, 35.43803], [62.15871, 35.33278], [62.29191, 35.25964], [62.29878, 35.13312], [62.48006, 35.28796], [62.62288, 35.22067], [62.74098, 35.25432], [62.90853, 35.37086], [63.0898, 35.43131], [63.12276, 35.53196], [63.10079, 35.63024], [63.23262, 35.67487], [63.10318, 35.81782], [63.12276, 35.86208], [63.29579, 35.85985], [63.53475, 35.90881], [63.56496, 35.95106], [63.98519, 36.03773], [64.05385, 36.10433], [64.43288, 36.24401], [64.57295, 36.34362], [64.62514, 36.44311], [64.61141, 36.6351], [64.97945, 37.21913], [65.51778, 37.23881], [65.64263, 37.34388], [65.64137, 37.45061], [65.72274, 37.55438], [66.30993, 37.32409], [66.55743, 37.35409], [66.52303, 37.39827], [66.65761, 37.45497], [66.52852, 37.58568], [66.53676, 37.80084], [66.67684, 37.96776], [66.56697, 38.0435], [66.41042, 38.02403], [66.24013, 38.16238], [65.83913, 38.25733], [65.55873, 38.29052], [64.32576, 38.98691], [64.19086, 38.95561], [63.70778, 39.22349], [63.6913, 39.27666], [62.43337, 39.98528], [62.34273, 40.43206], [62.11751, 40.58242], [61.87856, 41.12257], [61.4446, 41.29407], [61.39732, 41.19873], [61.33199, 41.14946], [61.22212, 41.14946], [61.03261, 41.25691], [60.5078, 41.21694]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TN", + iso1A3: "TUN", + iso1N3: "788", + wikidata: "Q948", + nameEn: "Tunisia", + groups: ["015", "002", "UN"], + callingCodes: ["216"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[11.2718, 37.6713], [7.89009, 38.19924], [8.59123, 37.14286], [8.64044, 36.9401], [8.62972, 36.86499], [8.67706, 36.8364], [8.57613, 36.78062], [8.46537, 36.7706], [8.47609, 36.66607], [8.16167, 36.48817], [8.18936, 36.44939], [8.40731, 36.42208], [8.2626, 35.91733], [8.26472, 35.73669], [8.35371, 35.66373], [8.36086, 35.47774], [8.30329, 35.29884], [8.47318, 35.23376], [8.3555, 35.10007], [8.30727, 34.95378], [8.25189, 34.92009], [8.29655, 34.72798], [8.20482, 34.57575], [7.86264, 34.3987], [7.81242, 34.21841], [7.74207, 34.16492], [7.66174, 34.20167], [7.52851, 34.06493], [7.54088, 33.7726], [7.73687, 33.42114], [7.83028, 33.18851], [8.11433, 33.10175], [8.1179, 33.05086], [8.31895, 32.83483], [8.35999, 32.50101], [9.07483, 32.07865], [9.55544, 30.23971], [9.76848, 30.34366], [9.88152, 30.34074], [10.29516, 30.90337], [10.12239, 31.42098], [10.31364, 31.72648], [10.48497, 31.72956], [10.62788, 31.96629], [10.7315, 31.97235], [11.04234, 32.2145], [11.53898, 32.4138], [11.57828, 32.48013], [11.46037, 32.6307], [11.51549, 33.09826], [11.55852, 33.1409], [11.58941, 33.36891], [11.2718, 37.6713]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TO", + iso1A3: "TON", + iso1N3: "776", + wikidata: "Q678", + nameEn: "Tonga", + groups: ["061", "009", "UN"], + driveSide: "left", + callingCodes: ["676"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TR", + iso1A3: "TUR", + iso1N3: "792", + wikidata: "Q43", + nameEn: "Turkey", + groups: ["145", "142", "UN"], + callingCodes: ["90"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[41.54366, 41.52185], [40.89217, 41.72528], [34.8305, 42.4581], [28.32297, 41.98371], [28.02971, 41.98066], [27.91479, 41.97902], [27.83492, 41.99709], [27.81235, 41.94803], [27.69949, 41.97515], [27.55191, 41.90928], [27.52379, 41.93756], [27.45478, 41.96591], [27.27411, 42.10409], [27.22376, 42.10152], [27.19251, 42.06028], [27.08486, 42.08735], [27.03277, 42.0809], [26.95638, 42.00741], [26.79143, 41.97386], [26.62996, 41.97644], [26.56051, 41.92995], [26.57961, 41.90024], [26.53968, 41.82653], [26.36952, 41.82265], [26.33589, 41.76802], [26.32952, 41.73637], [26.35957, 41.71149], [26.47958, 41.67037], [26.5209, 41.62592], [26.59196, 41.60491], [26.59742, 41.48058], [26.61767, 41.42281], [26.62997, 41.34613], [26.5837, 41.32131], [26.5209, 41.33993], [26.39861, 41.25053], [26.32259, 41.24929], [26.31928, 41.07386], [26.3606, 41.02027], [26.33297, 40.98388], [26.35894, 40.94292], [26.32259, 40.94042], [26.28623, 40.93005], [26.29441, 40.89119], [26.26169, 40.9168], [26.20856, 40.86048], [26.21351, 40.83298], [26.15685, 40.80709], [26.12854, 40.77339], [26.12495, 40.74283], [26.08638, 40.73214], [26.0754, 40.72772], [26.03489, 40.73051], [25.94795, 40.72797], [26.04292, 40.3958], [25.61285, 40.17161], [25.94257, 39.39358], [26.43357, 39.43096], [26.70773, 39.0312], [26.61814, 38.81372], [26.21136, 38.65436], [26.32173, 38.48731], [26.24183, 38.44695], [26.21136, 38.17558], [27.05537, 37.9131], [27.16428, 37.72343], [26.99377, 37.69034], [26.95583, 37.64989], [27.14757, 37.32], [27.20312, 36.94571], [27.45627, 36.9008], [27.24613, 36.71622], [27.46117, 36.53789], [27.89482, 36.69898], [27.95037, 36.46155], [28.23708, 36.56812], [29.30783, 36.01033], [29.48192, 36.18377], [29.61002, 36.1731], [29.61805, 36.14179], [29.69611, 36.10365], [29.73302, 35.92555], [32.82353, 35.70297], [35.51152, 36.10954], [35.931, 35.92109], [35.98499, 35.94107], [36.00514, 35.94113], [36.01844, 35.92403], [35.99829, 35.88242], [36.11827, 35.85923], [36.13919, 35.83692], [36.14029, 35.81015], [36.1623, 35.80925], [36.17441, 35.92076], [36.19973, 35.95195], [36.25366, 35.96264], [36.27678, 35.94839], [36.29769, 35.96086], [36.28338, 36.00273], [36.30099, 36.00985], [36.33956, 35.98687], [36.37474, 36.01163], [36.39206, 36.22088], [36.4617, 36.20461], [36.50463, 36.2419], [36.6125, 36.22592], [36.68672, 36.23677], [36.65653, 36.33861], [36.6081, 36.33772], [36.54206, 36.49539], [36.58829, 36.58295], [36.57398, 36.65186], [36.62681, 36.71189], [36.61581, 36.74629], [36.66727, 36.82901], [36.99557, 36.75997], [36.99886, 36.74012], [37.04399, 36.73483], [37.04619, 36.71101], [37.01647, 36.69512], [37.02088, 36.66422], [37.08279, 36.63495], [37.10894, 36.6704], [37.16177, 36.66069], [37.21988, 36.6736], [37.47253, 36.63243], [37.49103, 36.66904], [37.68048, 36.75065], [37.81974, 36.76055], [38.21064, 36.91842], [38.38859, 36.90064], [38.55908, 36.84429], [38.74042, 36.70629], [39.03217, 36.70911], [39.21538, 36.66834], [39.81589, 36.75538], [40.69136, 37.0996], [40.90856, 37.13147], [41.21937, 37.07665], [41.515, 37.08084], [42.00894, 37.17209], [42.18225, 37.28569], [42.19301, 37.31323], [42.2112, 37.32491], [42.22257, 37.31395], [42.22381, 37.30238], [42.20454, 37.28715], [42.21548, 37.28026], [42.23683, 37.2863], [42.26039, 37.27017], [42.2824, 37.2798], [42.34735, 37.22548], [42.32313, 37.17814], [42.35724, 37.10998], [42.56725, 37.14878], [42.78887, 37.38615], [42.93705, 37.32015], [43.11403, 37.37436], [43.30083, 37.30629], [43.33508, 37.33105], [43.50787, 37.24436], [43.56702, 37.25675], [43.63085, 37.21957], [43.7009, 37.23692], [43.8052, 37.22825], [43.82699, 37.19477], [43.84878, 37.22205], [43.90949, 37.22453], [44.02002, 37.33229], [44.13521, 37.32486], [44.2613, 37.25055], [44.27998, 37.16501], [44.22239, 37.15756], [44.18503, 37.09551], [44.25975, 36.98119], [44.30645, 36.97373], [44.35937, 37.02843], [44.35315, 37.04955], [44.38117, 37.05825], [44.42631, 37.05825], [44.63179, 37.19229], [44.76698, 37.16162], [44.78319, 37.1431], [44.7868, 37.16644], [44.75986, 37.21549], [44.81021, 37.2915], [44.58449, 37.45018], [44.61401, 37.60165], [44.56887, 37.6429], [44.62096, 37.71985], [44.55498, 37.783], [44.45948, 37.77065], [44.3883, 37.85433], [44.22509, 37.88859], [44.42476, 38.25763], [44.50115, 38.33939], [44.44386, 38.38295], [44.38309, 38.36117], [44.3119, 38.37887], [44.3207, 38.49799], [44.32058, 38.62752], [44.28065, 38.6465], [44.26155, 38.71427], [44.30322, 38.81581], [44.18863, 38.93881], [44.20946, 39.13975], [44.1043, 39.19842], [44.03667, 39.39223], [44.22452, 39.4169], [44.29818, 39.378], [44.37921, 39.4131], [44.42832, 39.4131], [44.41849, 39.56659], [44.48111, 39.61579], [44.47298, 39.68788], [44.6137, 39.78393], [44.65422, 39.72163], [44.71806, 39.71124], [44.81043, 39.62677], [44.80977, 39.65768], [44.75779, 39.7148], [44.61845, 39.8281], [44.46635, 39.97733], [44.26973, 40.04866], [44.1778, 40.02845], [44.1057, 40.03555], [43.92307, 40.01787], [43.65688, 40.11199], [43.65221, 40.14889], [43.71136, 40.16673], [43.59928, 40.34019], [43.60862, 40.43267], [43.54791, 40.47413], [43.63664, 40.54159], [43.7425, 40.66805], [43.74872, 40.7365], [43.67712, 40.84846], [43.67712, 40.93084], [43.58683, 40.98961], [43.47319, 41.02251], [43.44984, 41.0988], [43.4717, 41.12611], [43.44973, 41.17666], [43.36118, 41.2028], [43.23096, 41.17536], [43.1945, 41.25242], [43.13373, 41.25503], [43.21707, 41.30331], [43.02956, 41.37891], [42.8785, 41.50516], [42.84899, 41.47265], [42.78995, 41.50126], [42.84471, 41.58912], [42.72794, 41.59714], [42.59202, 41.58183], [42.51772, 41.43606], [42.26387, 41.49346], [41.95134, 41.52466], [41.81939, 41.43621], [41.7124, 41.47417], [41.7148, 41.4932], [41.54366, 41.52185]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TT", + iso1A3: "TTO", + iso1N3: "780", + wikidata: "Q754", + nameEn: "Trinidad and Tobago", + groups: ["029", "003", "419", "019", "UN"], + driveSide: "left", + callingCodes: ["1 868"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-61.62505, 11.18974], [-62.08693, 10.04435], [-60.89962, 9.81445], [-60.07172, 11.77667], [-61.62505, 11.18974]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TV", + iso1A3: "TUV", + iso1N3: "798", + wikidata: "Q672", + nameEn: "Tuvalu", + groups: ["061", "009", "UN"], + driveSide: "left", + callingCodes: ["688"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[174, -5], [174, -11.5], [179.99999, -11.5], [179.99999, -5], [174, -5]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TW", + iso1A3: "TWN", + iso1N3: "158", + wikidata: "Q865", + nameEn: "Taiwan", + aliases: ["RC"], + groups: ["030", "142"], + callingCodes: ["886"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "TZ", + iso1A3: "TZA", + iso1N3: "834", + wikidata: "Q924", + nameEn: "Tanzania", + groups: ["014", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["255"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[30.80408, -0.99911], [30.76635, -0.9852], [30.70631, -1.01175], [30.64166, -1.06601], [30.47194, -1.0555], [30.45116, -1.10641], [30.50889, -1.16412], [30.57123, -1.33264], [30.71974, -1.43244], [30.84079, -1.64652], [30.80802, -1.91477], [30.89303, -2.08223], [30.83915, -2.35795], [30.54501, -2.41404], [30.41789, -2.66266], [30.52747, -2.65841], [30.40662, -2.86151], [30.4987, -2.9573], [30.57926, -2.89791], [30.6675, -2.98987], [30.83823, -2.97837], [30.84165, -3.25152], [30.45915, -3.56532], [30.22042, -4.01738], [30.03323, -4.26631], [29.88172, -4.35743], [29.82885, -4.36153], [29.77289, -4.41733], [29.75109, -4.45836], [29.63827, -4.44681], [29.43673, -4.44845], [29.52552, -6.2731], [30.2567, -7.14121], [30.79243, -8.27382], [31.00796, -8.58615], [31.37533, -8.60769], [31.57147, -8.70619], [31.57147, -8.81388], [31.71158, -8.91386], [31.81587, -8.88618], [31.94663, -8.93846], [31.94196, -9.02303], [31.98866, -9.07069], [32.08206, -9.04609], [32.16146, -9.05993], [32.25486, -9.13371], [32.43543, -9.11988], [32.49147, -9.14754], [32.53661, -9.24281], [32.75611, -9.28583], [32.76233, -9.31963], [32.95389, -9.40138], [32.99397, -9.36712], [33.14925, -9.49322], [33.31581, -9.48554], [33.48052, -9.62442], [33.76677, -9.58516], [33.93298, -9.71647], [33.9638, -9.62206], [33.95829, -9.54066], [34.03865, -9.49398], [34.54499, -10.0678], [34.51911, -10.12279], [34.57581, -10.56271], [34.65946, -10.6828], [34.67047, -10.93796], [34.61161, -11.01611], [34.63305, -11.11731], [34.79375, -11.32245], [34.91153, -11.39799], [34.96296, -11.57354], [35.63599, -11.55927], [35.82767, -11.41081], [36.19094, -11.57593], [36.19094, -11.70008], [36.62068, -11.72884], [36.80309, -11.56836], [37.3936, -11.68949], [37.76614, -11.53352], [37.8388, -11.3123], [37.93618, -11.26228], [38.21598, -11.27289], [38.47258, -11.4199], [38.88996, -11.16978], [39.24395, -11.17433], [39.58249, -10.96043], [40.00295, -10.80255], [40.44265, -10.4618], [40.74206, -10.25691], [40.14328, -4.64201], [39.62121, -4.68136], [39.44306, -4.93877], [39.21631, -4.67835], [37.81321, -3.69179], [37.75036, -3.54243], [37.63099, -3.50723], [37.5903, -3.42735], [37.71745, -3.304], [37.67199, -3.06222], [34.0824, -1.02264], [34.03084, -1.05101], [34.02286, -1.00779], [33.93107, -0.99298], [30.80408, -0.99911]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "UA", + iso1A3: "UKR", + iso1N3: "804", + wikidata: "Q212", + nameEn: "Ukraine", + groups: ["151", "150", "UN"], + callingCodes: ["380"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[33.57318, 46.10317], [33.61467, 46.13561], [33.63854, 46.14147], [33.61517, 46.22615], [33.646, 46.23028], [33.74047, 46.18555], [33.79715, 46.20482], [33.85234, 46.19863], [33.91549, 46.15938], [34.05272, 46.10838], [34.07311, 46.11769], [34.12929, 46.10494], [34.181, 46.06804], [34.25111, 46.0532], [34.33912, 46.06114], [34.41221, 46.00245], [34.44155, 45.95995], [34.48729, 45.94267], [34.52011, 45.95097], [34.55889, 45.99347], [34.60861, 45.99347], [34.66679, 45.97136], [34.75479, 45.90705], [34.80153, 45.90047], [34.79905, 45.81009], [34.96015, 45.75634], [35.23066, 45.79231], [37.62608, 46.82615], [38.12112, 46.86078], [38.3384, 46.98085], [38.22955, 47.12069], [38.23049, 47.2324], [38.32112, 47.2585], [38.33074, 47.30508], [38.22225, 47.30788], [38.28954, 47.39255], [38.28679, 47.53552], [38.35062, 47.61631], [38.76379, 47.69346], [38.79628, 47.81109], [38.87979, 47.87719], [39.73935, 47.82876], [39.82213, 47.96396], [39.77544, 48.04206], [39.88256, 48.04482], [39.83724, 48.06501], [39.94847, 48.22811], [40.00752, 48.22445], [39.99241, 48.31768], [39.97325, 48.31399], [39.9693, 48.29904], [39.95248, 48.29972], [39.91465, 48.26743], [39.90041, 48.3049], [39.84273, 48.30947], [39.84136, 48.33321], [39.94847, 48.35055], [39.88794, 48.44226], [39.86196, 48.46633], [39.84548, 48.57821], [39.79764, 48.58668], [39.67226, 48.59368], [39.71765, 48.68673], [39.73104, 48.7325], [39.79466, 48.83739], [39.97182, 48.79398], [40.08168, 48.87443], [40.03636, 48.91957], [39.98967, 48.86901], [39.78368, 48.91596], [39.74874, 48.98675], [39.72649, 48.9754], [39.71353, 48.98959], [39.6683, 48.99454], [39.6836, 49.05121], [39.93437, 49.05709], [40.01988, 49.1761], [40.22176, 49.25683], [40.18331, 49.34996], [40.14912, 49.37681], [40.1141, 49.38798], [40.03087, 49.45452], [40.03636, 49.52321], [40.16683, 49.56865], [40.13249, 49.61672], [39.84548, 49.56064], [39.65047, 49.61761], [39.59142, 49.73758], [39.44496, 49.76067], [39.27968, 49.75976], [39.1808, 49.88911], [38.9391, 49.79524], [38.90477, 49.86787], [38.73311, 49.90238], [38.68677, 50.00904], [38.65688, 49.97176], [38.35408, 50.00664], [38.32524, 50.08866], [38.18517, 50.08161], [38.21675, 49.98104], [38.02999, 49.90592], [38.02999, 49.94482], [37.90776, 50.04194], [37.79515, 50.08425], [37.75807, 50.07896], [37.61113, 50.21976], [37.62879, 50.24481], [37.62486, 50.29966], [37.47243, 50.36277], [37.48204, 50.46079], [37.08468, 50.34935], [36.91762, 50.34963], [36.69377, 50.26982], [36.64571, 50.218], [36.56655, 50.2413], [36.58371, 50.28563], [36.47817, 50.31457], [36.30101, 50.29088], [36.20763, 50.3943], [36.06893, 50.45205], [35.8926, 50.43829], [35.80388, 50.41356], [35.73659, 50.35489], [35.61711, 50.35707], [35.58003, 50.45117], [35.47463, 50.49247], [35.39464, 50.64751], [35.48116, 50.66405], [35.47704, 50.77274], [35.41367, 50.80227], [35.39307, 50.92145], [35.32598, 50.94524], [35.40837, 51.04119], [35.31774, 51.08434], [35.20375, 51.04723], [35.12685, 51.16191], [35.14058, 51.23162], [34.97304, 51.2342], [34.82472, 51.17483], [34.6874, 51.18], [34.6613, 51.25053], [34.38802, 51.2746], [34.31661, 51.23936], [34.23009, 51.26429], [34.33446, 51.363], [34.22048, 51.4187], [34.30562, 51.5205], [34.17599, 51.63253], [34.07765, 51.67065], [34.42922, 51.72852], [34.41136, 51.82793], [34.09413, 52.00835], [34.11199, 52.14087], [34.05239, 52.20132], [33.78789, 52.37204], [33.55718, 52.30324], [33.48027, 52.31499], [33.51323, 52.35779], [33.18913, 52.3754], [32.89937, 52.2461], [32.85405, 52.27888], [32.69475, 52.25535], [32.54781, 52.32423], [32.3528, 52.32842], [32.38988, 52.24946], [32.33083, 52.23685], [32.34044, 52.1434], [32.2777, 52.10266], [32.23331, 52.08085], [32.08813, 52.03319], [31.92159, 52.05144], [31.96141, 52.08015], [31.85018, 52.11305], [31.81722, 52.09955], [31.7822, 52.11406], [31.38326, 52.12991], [31.25142, 52.04131], [31.13332, 52.1004], [30.95589, 52.07775], [30.90897, 52.00699], [30.76443, 51.89739], [30.68804, 51.82806], [30.51946, 51.59649], [30.64992, 51.35014], [30.56203, 51.25655], [30.36153, 51.33984], [30.34642, 51.42555], [30.17888, 51.51025], [29.77376, 51.4461], [29.7408, 51.53417], [29.54372, 51.48372], [29.49773, 51.39814], [29.42357, 51.4187], [29.32881, 51.37843], [29.25191, 51.49828], [29.25603, 51.57089], [29.20659, 51.56918], [29.16402, 51.64679], [29.1187, 51.65872], [28.99098, 51.56833], [28.95528, 51.59222], [28.81795, 51.55552], [28.76027, 51.48802], [28.78224, 51.45294], [28.75615, 51.41442], [28.73143, 51.46236], [28.69161, 51.44695], [28.64429, 51.5664], [28.47051, 51.59734], [28.37592, 51.54505], [28.23452, 51.66988], [28.10658, 51.57857], [27.95827, 51.56065], [27.91844, 51.61952], [27.85253, 51.62293], [27.76052, 51.47604], [27.67125, 51.50854], [27.71932, 51.60672], [27.55727, 51.63486], [27.51058, 51.5854], [27.47212, 51.61184], [27.24828, 51.60161], [27.26613, 51.65957], [27.20948, 51.66713], [27.20602, 51.77291], [26.99422, 51.76933], [26.9489, 51.73788], [26.80043, 51.75777], [26.69759, 51.82284], [26.46962, 51.80501], [26.39367, 51.87315], [26.19084, 51.86781], [26.00408, 51.92967], [25.83217, 51.92587], [25.80574, 51.94556], [25.73673, 51.91973], [25.46163, 51.92205], [25.20228, 51.97143], [24.98784, 51.91273], [24.37123, 51.88222], [24.29021, 51.80841], [24.3163, 51.75063], [24.13075, 51.66979], [23.99907, 51.58369], [23.8741, 51.59734], [23.91118, 51.63316], [23.7766, 51.66809], [23.60906, 51.62122], [23.6736, 51.50255], [23.62751, 51.50512], [23.69905, 51.40871], [23.63858, 51.32182], [23.80678, 51.18405], [23.90376, 51.07697], [23.92217, 51.00836], [24.04576, 50.90196], [24.14524, 50.86128], [24.0952, 50.83262], [23.99254, 50.83847], [23.95925, 50.79271], [24.0595, 50.71625], [24.0996, 50.60752], [24.07048, 50.5071], [24.03668, 50.44507], [23.99563, 50.41289], [23.79445, 50.40481], [23.71382, 50.38248], [23.67635, 50.33385], [23.28221, 50.0957], [22.99329, 49.84249], [22.83179, 49.69875], [22.80261, 49.69098], [22.78304, 49.65543], [22.64534, 49.53094], [22.69444, 49.49378], [22.748, 49.32759], [22.72009, 49.20288], [22.86336, 49.10513], [22.89122, 49.00725], [22.56155, 49.08865], [22.54338, 49.01424], [22.48296, 48.99172], [22.42934, 48.92857], [22.34151, 48.68893], [22.21379, 48.6218], [22.16023, 48.56548], [22.14689, 48.4005], [22.2083, 48.42534], [22.38133, 48.23726], [22.49806, 48.25189], [22.59007, 48.15121], [22.58733, 48.10813], [22.66835, 48.09162], [22.73427, 48.12005], [22.81804, 48.11363], [22.87847, 48.04665], [22.84276, 47.98602], [22.89849, 47.95851], [22.94301, 47.96672], [22.92241, 48.02002], [23.0158, 47.99338], [23.08858, 48.00716], [23.1133, 48.08061], [23.15999, 48.12188], [23.27397, 48.08245], [23.33577, 48.0237], [23.4979, 47.96858], [23.52803, 48.01818], [23.5653, 48.00499], [23.63894, 48.00293], [23.66262, 47.98786], [23.75188, 47.99705], [23.80904, 47.98142], [23.8602, 47.9329], [23.89352, 47.94512], [23.94192, 47.94868], [23.96337, 47.96672], [23.98553, 47.96076], [24.00801, 47.968], [24.02999, 47.95087], [24.06466, 47.95317], [24.11281, 47.91487], [24.22566, 47.90231], [24.34926, 47.9244], [24.43578, 47.97131], [24.61994, 47.95062], [24.70632, 47.84428], [24.81893, 47.82031], [24.88896, 47.7234], [25.11144, 47.75203], [25.23778, 47.89403], [25.63878, 47.94924], [25.77723, 47.93919], [26.05901, 47.9897], [26.17711, 47.99246], [26.33504, 48.18418], [26.55202, 48.22445], [26.62823, 48.25804], [26.6839, 48.35828], [26.79239, 48.29071], [26.82809, 48.31629], [26.71274, 48.40388], [26.85556, 48.41095], [26.93384, 48.36558], [27.03821, 48.37653], [27.0231, 48.42485], [27.08078, 48.43214], [27.13434, 48.37288], [27.27855, 48.37534], [27.32159, 48.4434], [27.37604, 48.44398], [27.37741, 48.41026], [27.44333, 48.41209], [27.46942, 48.454], [27.5889, 48.49224], [27.59027, 48.46311], [27.6658, 48.44034], [27.74422, 48.45926], [27.79225, 48.44244], [27.81902, 48.41874], [27.87533, 48.4037], [27.88391, 48.36699], [27.95883, 48.32368], [28.04527, 48.32661], [28.09873, 48.3124], [28.07504, 48.23494], [28.17666, 48.25963], [28.19314, 48.20749], [28.2856, 48.23202], [28.32508, 48.23384], [28.35519, 48.24957], [28.36996, 48.20543], [28.34912, 48.1787], [28.30586, 48.1597], [28.30609, 48.14018], [28.34009, 48.13147], [28.38712, 48.17567], [28.43701, 48.15832], [28.42454, 48.12047], [28.48428, 48.0737], [28.53921, 48.17453], [28.69896, 48.13106], [28.85232, 48.12506], [28.8414, 48.03392], [28.9306, 47.96255], [29.1723, 47.99013], [29.19839, 47.89261], [29.27804, 47.88893], [29.20663, 47.80367], [29.27255, 47.79953], [29.22242, 47.73607], [29.22414, 47.60012], [29.11743, 47.55001], [29.18603, 47.43387], [29.3261, 47.44664], [29.39889, 47.30179], [29.47854, 47.30366], [29.48678, 47.36043], [29.5733, 47.36508], [29.59665, 47.25521], [29.54996, 47.24962], [29.57696, 47.13581], [29.49732, 47.12878], [29.53044, 47.07851], [29.61038, 47.09932], [29.62137, 47.05069], [29.57056, 46.94766], [29.72986, 46.92234], [29.75458, 46.8604], [29.87405, 46.88199], [29.98814, 46.82358], [29.94522, 46.80055], [29.9743, 46.75325], [29.94409, 46.56002], [29.88916, 46.54302], [30.02511, 46.45132], [30.16794, 46.40967], [30.09103, 46.38694], [29.94114, 46.40114], [29.88329, 46.35851], [29.74496, 46.45605], [29.66359, 46.4215], [29.6763, 46.36041], [29.5939, 46.35472], [29.49914, 46.45889], [29.35357, 46.49505], [29.24886, 46.37912], [29.23547, 46.55435], [29.02409, 46.49582], [29.01241, 46.46177], [28.9306, 46.45699], [29.004, 46.31495], [28.98478, 46.31803], [28.94953, 46.25852], [29.06656, 46.19716], [28.94643, 46.09176], [29.00613, 46.04962], [28.98004, 46.00385], [28.74383, 45.96664], [28.78503, 45.83475], [28.69852, 45.81753], [28.70401, 45.78019], [28.52823, 45.73803], [28.47879, 45.66994], [28.51587, 45.6613], [28.54196, 45.58062], [28.49252, 45.56716], [28.51449, 45.49982], [28.43072, 45.48538], [28.41836, 45.51715], [28.30201, 45.54744], [28.21139, 45.46895], [28.28504, 45.43907], [28.34554, 45.32102], [28.5735, 45.24759], [28.71358, 45.22631], [28.78911, 45.24179], [28.81383, 45.3384], [28.94292, 45.28045], [28.96077, 45.33164], [29.24779, 45.43388], [29.42632, 45.44545], [29.59798, 45.38857], [29.68175, 45.26885], [29.65428, 45.25629], [29.69272, 45.19227], [30.04414, 45.08461], [31.62627, 45.50633], [33.54017, 46.0123], [33.59087, 46.06013], [33.57318, 46.10317]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "UG", + iso1A3: "UGA", + iso1N3: "800", + wikidata: "Q1036", + nameEn: "Uganda", + groups: ["014", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["256"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[33.93107, -0.99298], [33.9264, -0.54188], [33.98449, -0.13079], [33.90936, 0.10581], [34.10067, 0.36372], [34.08727, 0.44713], [34.11408, 0.48884], [34.13493, 0.58118], [34.20196, 0.62289], [34.27345, 0.63182], [34.31516, 0.75693], [34.40041, 0.80266], [34.43349, 0.85254], [34.52369, 1.10692], [34.57427, 1.09868], [34.58029, 1.14712], [34.67562, 1.21265], [34.80223, 1.22754], [34.82606, 1.26626], [34.82606, 1.30944], [34.7918, 1.36752], [34.87819, 1.5596], [34.92734, 1.56109], [34.9899, 1.6668], [34.98692, 1.97348], [34.90947, 2.42447], [34.95267, 2.47209], [34.77244, 2.70272], [34.78137, 2.76223], [34.73967, 2.85447], [34.65774, 2.8753], [34.60114, 2.93034], [34.56242, 3.11478], [34.45815, 3.18319], [34.40006, 3.37949], [34.41794, 3.44342], [34.39112, 3.48802], [34.44922, 3.51627], [34.45815, 3.67385], [34.15429, 3.80464], [34.06046, 4.15235], [33.9873, 4.23316], [33.51264, 3.75068], [33.18356, 3.77812], [33.02852, 3.89296], [32.89746, 3.81339], [32.72021, 3.77327], [32.41337, 3.748], [32.20782, 3.6053], [32.19888, 3.50867], [32.08866, 3.53543], [32.08491, 3.56287], [32.05187, 3.589], [31.95907, 3.57408], [31.96205, 3.6499], [31.86821, 3.78664], [31.81459, 3.82083], [31.72075, 3.74354], [31.50776, 3.63652], [31.50478, 3.67814], [31.29476, 3.8015], [31.16666, 3.79853], [30.97601, 3.693], [30.85153, 3.48867], [30.94081, 3.50847], [30.93486, 3.40737], [30.84251, 3.26908], [30.77101, 3.04897], [30.8574, 2.9508], [30.8857, 2.83923], [30.75612, 2.5863], [30.74271, 2.43601], [30.83059, 2.42559], [30.91102, 2.33332], [30.96911, 2.41071], [31.06593, 2.35862], [31.07934, 2.30207], [31.12104, 2.27676], [31.1985, 2.29462], [31.20148, 2.2217], [31.28042, 2.17853], [31.30127, 2.11006], [30.48503, 1.21675], [30.24671, 1.14974], [30.22139, 0.99635], [30.1484, 0.89805], [29.98307, 0.84295], [29.95477, 0.64486], [29.97413, 0.52124], [29.87284, 0.39166], [29.81922, 0.16824], [29.77454, 0.16675], [29.7224, 0.07291], [29.72687, -0.08051], [29.65091, -0.46777], [29.67474, -0.47969], [29.67176, -0.55714], [29.62708, -0.71055], [29.63006, -0.8997], [29.58388, -0.89821], [29.59061, -1.39016], [29.82657, -1.31187], [29.912, -1.48269], [30.16369, -1.34303], [30.35212, -1.06896], [30.47194, -1.0555], [30.64166, -1.06601], [30.70631, -1.01175], [30.76635, -0.9852], [30.80408, -0.99911], [33.93107, -0.99298]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "UM", + iso1A3: "UMI", + iso1N3: "581", + wikidata: "Q16645", + nameEn: "United States Minor Outlying Islands", + country: "US" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "UN", + wikidata: "Q1065", + nameEn: "United Nations", + level: "unitedNations", + isoStatus: "excRes" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "US", + iso1A3: "USA", + iso1N3: "840", + wikidata: "Q30", + nameEn: "United States of America" + }, + geometry: null + }, { + type: "Feature", + properties: { + iso1A2: "UY", + iso1A3: "URY", + iso1N3: "858", + wikidata: "Q77", + nameEn: "Uruguay", + groups: ["005", "419", "019", "UN"], + callingCodes: ["598"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-57.65132, -30.19229], [-57.61478, -30.25165], [-57.64859, -30.35095], [-57.89115, -30.49572], [-57.8024, -30.77193], [-57.89476, -30.95994], [-57.86729, -31.06352], [-57.9908, -31.34924], [-57.98127, -31.3872], [-58.07569, -31.44916], [-58.0023, -31.53084], [-58.00076, -31.65016], [-58.20252, -31.86966], [-58.10036, -32.25338], [-58.22362, -32.52416], [-58.1224, -32.98842], [-58.40475, -33.11777], [-58.44442, -33.84033], [-58.34425, -34.15035], [-57.83001, -34.69099], [-54.78916, -36.21945], [-52.83257, -34.01481], [-53.37138, -33.74313], [-53.39593, -33.75169], [-53.44031, -33.69344], [-53.52794, -33.68908], [-53.53459, -33.16843], [-53.1111, -32.71147], [-53.37671, -32.57005], [-53.39572, -32.58596], [-53.76024, -32.0751], [-54.17384, -31.86168], [-55.50821, -30.91349], [-55.50841, -30.9027], [-55.51862, -30.89828], [-55.52712, -30.89997], [-55.53276, -30.90218], [-55.53431, -30.89714], [-55.54572, -30.89051], [-55.55218, -30.88193], [-55.55373, -30.8732], [-55.5634, -30.8686], [-55.58866, -30.84117], [-55.87388, -31.05053], [-56.4619, -30.38457], [-56.4795, -30.3899], [-56.49267, -30.39471], [-56.90236, -30.02578], [-57.22502, -30.26121], [-57.65132, -30.19229]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "UZ", + iso1A3: "UZB", + iso1N3: "860", + wikidata: "Q265", + nameEn: "Uzbekistan", + groups: ["143", "142", "UN"], + callingCodes: ["998"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[65.85194, 42.85481], [65.53277, 43.31856], [65.18666, 43.48835], [64.96464, 43.74748], [64.53885, 43.56941], [63.34656, 43.64003], [62.01711, 43.51008], [61.01475, 44.41383], [58.59711, 45.58671], [55.97842, 44.99622], [55.97832, 44.99622], [55.97822, 44.99617], [55.97811, 44.99617], [55.97801, 44.99612], [55.97801, 44.99607], [55.97791, 44.99607], [55.9778, 44.99607], [55.9777, 44.99601], [55.9777, 44.99596], [55.9776, 44.99591], [55.97749, 44.99591], [55.97739, 44.99591], [55.97739, 44.99586], [55.97729, 44.99586], [55.97718, 44.99581], [55.97708, 44.99576], [55.97698, 44.9957], [55.97698, 44.99565], [55.97687, 44.9956], [55.97677, 44.9956], [55.97677, 44.99555], [55.97677, 44.9955], [55.97667, 44.99545], [55.97656, 44.99539], [55.97646, 44.99534], [55.97646, 44.99529], [55.97636, 44.99524], [55.97636, 44.99519], [55.97625, 44.99514], [55.97615, 44.99508], [55.97615, 44.99503], [55.97615, 44.99498], [55.97615, 44.99493], [55.97615, 44.99483], [55.97615, 44.99477], [55.97605, 44.99477], [55.97605, 44.99467], [55.97605, 44.99462], [55.97605, 44.99457], [55.97605, 44.99452], [55.97594, 44.99446], [55.97584, 44.99441], [55.97584, 44.99436], [55.97584, 44.99431], [55.97584, 44.99426], [55.97584, 44.99421], [55.97584, 44.99415], [55.97584, 44.99405], [55.97584, 44.994], [55.97584, 44.9939], [55.97584, 44.99384], [55.97584, 44.99374], [55.97584, 44.99369], [55.97584, 44.99359], [55.97584, 44.99353], [55.97584, 44.99348], [55.97584, 44.99343], [55.97584, 44.99338], [55.97584, 44.99328], [55.97584, 44.99322], [56.00314, 41.32584], [57.03423, 41.25435], [57.13796, 41.36625], [57.03359, 41.41777], [56.96218, 41.80383], [57.03633, 41.92043], [57.30275, 42.14076], [57.6296, 42.16519], [57.84932, 42.18555], [57.92897, 42.24047], [57.90975, 42.4374], [57.99214, 42.50021], [58.3492, 42.43335], [58.40688, 42.29535], [58.51674, 42.30348], [58.29427, 42.56497], [58.14321, 42.62159], [58.27504, 42.69632], [58.57991, 42.64988], [58.6266, 42.79314], [58.93422, 42.5407], [59.17317, 42.52248], [59.2955, 42.37064], [59.4341, 42.29738], [59.94633, 42.27655], [60.00539, 42.212], [59.96419, 42.1428], [60.04659, 42.08982], [60.0356, 42.01028], [59.95046, 41.97966], [60.33223, 41.75058], [60.08504, 41.80997], [60.06032, 41.76287], [60.18117, 41.60082], [60.06581, 41.4363], [60.5078, 41.21694], [61.03261, 41.25691], [61.22212, 41.14946], [61.33199, 41.14946], [61.39732, 41.19873], [61.4446, 41.29407], [61.87856, 41.12257], [62.11751, 40.58242], [62.34273, 40.43206], [62.43337, 39.98528], [63.6913, 39.27666], [63.70778, 39.22349], [64.19086, 38.95561], [64.32576, 38.98691], [65.55873, 38.29052], [65.83913, 38.25733], [66.24013, 38.16238], [66.41042, 38.02403], [66.56697, 38.0435], [66.67684, 37.96776], [66.53676, 37.80084], [66.52852, 37.58568], [66.65761, 37.45497], [66.52303, 37.39827], [66.55743, 37.35409], [66.64699, 37.32958], [66.95598, 37.40162], [67.08232, 37.35469], [67.13039, 37.27168], [67.2224, 37.24545], [67.2581, 37.17216], [67.51868, 37.26102], [67.78329, 37.1834], [67.8474, 37.31594], [67.81566, 37.43107], [68.12635, 37.93], [68.27159, 37.91477], [68.40343, 38.19484], [68.13289, 38.40822], [68.06274, 38.39435], [68.11366, 38.47169], [68.05873, 38.56087], [68.0807, 38.64136], [68.05598, 38.71641], [68.12877, 38.73677], [68.06948, 38.82115], [68.19743, 38.85985], [68.09704, 39.02589], [67.68915, 39.00775], [67.67833, 39.14479], [67.33226, 39.23739], [67.36522, 39.31287], [67.45998, 39.315], [67.46822, 39.46146], [67.39681, 39.52505], [67.46547, 39.53564], [67.44899, 39.57799], [67.62889, 39.60234], [67.70992, 39.66156], [68.12053, 39.56317], [68.54166, 39.53929], [68.61972, 39.68905], [68.63071, 39.85265], [68.88889, 39.87163], [68.93695, 39.91167], [68.84906, 40.04952], [68.96579, 40.06949], [69.01935, 40.11466], [69.01523, 40.15771], [68.62796, 40.07789], [68.52771, 40.11676], [68.5332, 40.14826], [68.77902, 40.20492], [68.79276, 40.17555], [68.84357, 40.18604], [68.85832, 40.20885], [69.04544, 40.22904], [69.15659, 40.2162], [69.2074, 40.21488], [69.30448, 40.18774], [69.30104, 40.24502], [69.25229, 40.26362], [69.24817, 40.30357], [69.30808, 40.2821], [69.32833, 40.29794], [69.33794, 40.34819], [69.30774, 40.36102], [69.28525, 40.41894], [69.27066, 40.49274], [69.21063, 40.54469], [69.2643, 40.57506], [69.3455, 40.57988], [69.32834, 40.70233], [69.38327, 40.7918], [69.53021, 40.77621], [69.59441, 40.70181], [69.69434, 40.62615], [70.36655, 40.90296], [70.38028, 41.02014], [70.45251, 41.04438], [70.80009, 40.72825], [70.49871, 40.52503], [70.32626, 40.45174], [70.37511, 40.38605], [70.57149, 40.3442], [70.56394, 40.26421], [70.62342, 40.17396], [70.8607, 40.217], [70.9818, 40.22392], [70.95789, 40.28761], [71.05901, 40.28765], [71.13042, 40.34106], [71.36663, 40.31593], [71.4246, 40.28619], [71.51215, 40.26943], [71.51549, 40.22986], [71.61725, 40.20615], [71.61931, 40.26775], [71.68386, 40.26984], [71.70569, 40.20391], [71.69621, 40.18492], [71.71719, 40.17886], [71.73054, 40.14818], [71.82646, 40.21872], [71.85002, 40.25647], [72.05464, 40.27586], [71.96401, 40.31907], [72.18648, 40.49893], [72.24368, 40.46091], [72.40346, 40.4007], [72.44191, 40.48222], [72.41513, 40.50856], [72.38384, 40.51535], [72.41714, 40.55736], [72.34406, 40.60144], [72.40517, 40.61917], [72.47795, 40.5532], [72.66713, 40.5219], [72.66713, 40.59076], [72.69579, 40.59778], [72.73995, 40.58409], [72.74768, 40.58051], [72.74862, 40.57131], [72.75982, 40.57273], [72.74894, 40.59592], [72.74866, 40.60873], [72.80137, 40.67856], [72.84754, 40.67229], [72.85372, 40.7116], [72.8722, 40.71111], [72.93296, 40.73089], [72.99133, 40.76457], [73.0612, 40.76678], [73.13412, 40.79122], [73.13267, 40.83512], [73.01869, 40.84681], [72.94454, 40.8094], [72.84291, 40.85512], [72.68157, 40.84942], [72.59136, 40.86947], [72.55109, 40.96046], [72.48742, 40.97136], [72.45206, 41.03018], [72.38511, 41.02785], [72.36138, 41.04384], [72.34757, 41.06104], [72.34026, 41.04539], [72.324, 41.03381], [72.18339, 40.99571], [72.17594, 41.02377], [72.21061, 41.05607], [72.1792, 41.10621], [72.14864, 41.13363], [72.17594, 41.15522], [72.16433, 41.16483], [72.10745, 41.15483], [72.07249, 41.11739], [71.85964, 41.19081], [71.91457, 41.2982], [71.83914, 41.3546], [71.76625, 41.4466], [71.71132, 41.43012], [71.73054, 41.54713], [71.65914, 41.49599], [71.6787, 41.42111], [71.57227, 41.29175], [71.46688, 41.31883], [71.43814, 41.19644], [71.46148, 41.13958], [71.40198, 41.09436], [71.34877, 41.16807], [71.27187, 41.11015], [71.25813, 41.18796], [71.11806, 41.15359], [71.02193, 41.19494], [70.9615, 41.16393], [70.86263, 41.23833], [70.77885, 41.24813], [70.78572, 41.36419], [70.67586, 41.47953], [70.48909, 41.40335], [70.17682, 41.5455], [70.69777, 41.92554], [71.28719, 42.18033], [71.13263, 42.28356], [70.94483, 42.26238], [69.49545, 41.545], [69.45751, 41.56863], [69.39485, 41.51518], [69.45081, 41.46246], [69.37468, 41.46555], [69.35554, 41.47211], [69.29778, 41.43673], [69.25059, 41.46693], [69.23332, 41.45847], [69.22671, 41.46298], [69.20439, 41.45391], [69.18528, 41.45175], [69.17701, 41.43769], [69.15137, 41.43078], [69.05006, 41.36183], [69.01308, 41.22804], [68.7217, 41.05025], [68.73945, 40.96989], [68.65662, 40.93861], [68.62221, 41.03019], [68.49983, 40.99669], [68.58444, 40.91447], [68.63, 40.59358], [68.49983, 40.56437], [67.96736, 40.83798], [68.1271, 41.0324], [68.08273, 41.08148], [67.98511, 41.02794], [67.9644, 41.14611], [66.69129, 41.1311], [66.53302, 41.87388], [66.00546, 41.94455], [66.09482, 42.93426], [65.85194, 42.85481]], [[70.68112, 40.90612], [70.6721, 40.90555], [70.57501, 40.98941], [70.54223, 40.98787], [70.56077, 41.00642], [70.6158, 40.97661], [70.68112, 40.90612]]], [[[71.21139, 40.03369], [71.12218, 40.03052], [71.06305, 40.1771], [71.00236, 40.18154], [71.01035, 40.05481], [71.11037, 40.01984], [71.11668, 39.99291], [71.09063, 39.99], [71.10501, 39.95568], [71.04979, 39.89808], [71.10531, 39.91354], [71.16101, 39.88423], [71.23067, 39.93581], [71.1427, 39.95026], [71.21139, 40.03369]]], [[[71.86463, 39.98598], [71.78838, 40.01404], [71.71511, 39.96348], [71.7504, 39.93701], [71.84316, 39.95582], [71.86463, 39.98598]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "VA", + iso1A3: "VAT", + iso1N3: "336", + wikidata: "Q237", + nameEn: "Vatican City", + aliases: ["Holy See"], + groups: ["039", "150"], + callingCodes: ["379", "39 06"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[12.45181, 41.90056], [12.45446, 41.90028], [12.45435, 41.90143], [12.45626, 41.90172], [12.45691, 41.90125], [12.4577, 41.90115], [12.45834, 41.90174], [12.45826, 41.90281], [12.45755, 41.9033], [12.45762, 41.9058], [12.45561, 41.90629], [12.45543, 41.90738], [12.45091, 41.90625], [12.44984, 41.90545], [12.44815, 41.90326], [12.44582, 41.90194], [12.44834, 41.90095], [12.45181, 41.90056]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "VC", + iso1A3: "VCT", + iso1N3: "670", + wikidata: "Q757", + nameEn: "St. Vincent and the Grenadines", + aliases: ["WV"], + groups: ["029", "003", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + callingCodes: ["1 784"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-62.64026, 12.69984], [-59.94058, 12.34011], [-61.69315, 14.26451], [-62.64026, 12.69984]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "VE", + iso1A3: "VEN", + iso1N3: "862", + wikidata: "Q717", + nameEn: "Venezuela", + aliases: ["YV"], + groups: ["005", "419", "019", "UN"], + callingCodes: ["58"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-71.22331, 13.01387], [-70.92579, 11.96275], [-71.3275, 11.85], [-71.9675, 11.65536], [-72.24983, 11.14138], [-72.4767, 11.1117], [-72.88002, 10.44309], [-72.98085, 9.85253], [-73.36905, 9.16636], [-73.02119, 9.27584], [-72.94052, 9.10663], [-72.77415, 9.10165], [-72.65474, 8.61428], [-72.4042, 8.36513], [-72.36987, 8.19976], [-72.35163, 8.01163], [-72.39137, 8.03534], [-72.47213, 7.96106], [-72.48801, 7.94329], [-72.48183, 7.92909], [-72.47042, 7.92306], [-72.45806, 7.91141], [-72.46183, 7.90682], [-72.44454, 7.86031], [-72.46763, 7.79518], [-72.47827, 7.65604], [-72.45321, 7.57232], [-72.47415, 7.48928], [-72.43132, 7.40034], [-72.19437, 7.37034], [-72.04895, 7.03837], [-71.82441, 7.04314], [-71.44118, 7.02116], [-71.42212, 7.03854], [-71.37234, 7.01588], [-71.03941, 6.98163], [-70.7596, 7.09799], [-70.10716, 6.96516], [-69.41843, 6.1072], [-67.60654, 6.2891], [-67.4625, 6.20625], [-67.43513, 5.98835], [-67.58558, 5.84537], [-67.63914, 5.64963], [-67.59141, 5.5369], [-67.83341, 5.31104], [-67.85358, 4.53249], [-67.62671, 3.74303], [-67.50067, 3.75812], [-67.30945, 3.38393], [-67.85862, 2.86727], [-67.85862, 2.79173], [-67.65696, 2.81691], [-67.21967, 2.35778], [-66.85795, 1.22998], [-66.28507, 0.74585], [-65.6727, 1.01353], [-65.50158, 0.92086], [-65.57288, 0.62856], [-65.11657, 1.12046], [-64.38932, 1.5125], [-64.34654, 1.35569], [-64.08274, 1.64792], [-64.06135, 1.94722], [-63.39827, 2.16098], [-63.39114, 2.4317], [-64.0257, 2.48156], [-64.02908, 2.79797], [-64.48379, 3.7879], [-64.84028, 4.24665], [-64.72977, 4.28931], [-64.57648, 4.12576], [-64.14512, 4.12932], [-63.99183, 3.90172], [-63.86082, 3.94796], [-63.70218, 3.91417], [-63.67099, 4.01731], [-63.50611, 3.83592], [-63.42233, 3.89995], [-63.4464, 3.9693], [-63.21111, 3.96219], [-62.98296, 3.59935], [-62.7655, 3.73099], [-62.74411, 4.03331], [-62.57656, 4.04754], [-62.44822, 4.18621], [-62.13094, 4.08309], [-61.54629, 4.2822], [-61.48569, 4.43149], [-61.29675, 4.44216], [-61.31457, 4.54167], [-61.15703, 4.49839], [-60.98303, 4.54167], [-60.86539, 4.70512], [-60.5802, 4.94312], [-60.73204, 5.20931], [-61.4041, 5.95304], [-61.15058, 6.19558], [-61.20762, 6.58174], [-61.13632, 6.70922], [-60.54873, 6.8631], [-60.39419, 6.94847], [-60.28074, 7.1162], [-60.44116, 7.20817], [-60.54098, 7.14804], [-60.63367, 7.25061], [-60.59802, 7.33194], [-60.71923, 7.55817], [-60.64793, 7.56877], [-60.51959, 7.83373], [-60.38056, 7.8302], [-60.02407, 8.04557], [-59.97059, 8.20791], [-59.83156, 8.23261], [-59.80661, 8.28906], [-59.85562, 8.35213], [-59.98508, 8.53046], [-59.54058, 8.6862], [-60.89962, 9.81445], [-62.08693, 10.04435], [-61.62505, 11.18974], [-63.73917, 11.92623], [-63.19938, 16.44103], [-67.89186, 12.4116], [-68.01417, 11.77722], [-68.33524, 11.78151], [-68.99639, 11.79035], [-71.22331, 13.01387]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "VG", + iso1A3: "VGB", + iso1N3: "092", + wikidata: "Q25305", + nameEn: "British Virgin Islands", + country: "GB", + groups: ["BOTS", "029", "003", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1 284"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "VI", + iso1A3: "VIR", + iso1N3: "850", + wikidata: "Q11703", + nameEn: "United States Virgin Islands", + aliases: ["US-VI"], + country: "US", + groups: ["Q1352230", "029", "003", "419", "019", "UN"], + driveSide: "left", + roadSpeedUnit: "mph", + roadHeightUnit: "ft", + callingCodes: ["1 340"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "VN", + iso1A3: "VNM", + iso1N3: "704", + wikidata: "Q881", + nameEn: "Vietnam", + groups: ["035", "142", "UN"], + callingCodes: ["84"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[108.10003, 21.47338], [108.0569, 21.53604], [108.02926, 21.54997], [107.97932, 21.54503], [107.97383, 21.53961], [107.97074, 21.54072], [107.96774, 21.53601], [107.95232, 21.5388], [107.92652, 21.58906], [107.90006, 21.5905], [107.86114, 21.65128], [107.80355, 21.66141], [107.66967, 21.60787], [107.56537, 21.61945], [107.54047, 21.5934], [107.49065, 21.59774], [107.49532, 21.62958], [107.47197, 21.6672], [107.41593, 21.64839], [107.38636, 21.59774], [107.35989, 21.60063], [107.35834, 21.6672], [107.29296, 21.74674], [107.24625, 21.7077], [107.20734, 21.71493], [107.10771, 21.79879], [107.02615, 21.81981], [107.00964, 21.85948], [107.06101, 21.88982], [107.05634, 21.92303], [106.99252, 21.95191], [106.97228, 21.92592], [106.92714, 21.93459], [106.9178, 21.97357], [106.81038, 21.97934], [106.74345, 22.00965], [106.72551, 21.97923], [106.69276, 21.96013], [106.68274, 21.99811], [106.70142, 22.02409], [106.6983, 22.15102], [106.67495, 22.1885], [106.69986, 22.22309], [106.6516, 22.33977], [106.55976, 22.34841], [106.57221, 22.37], [106.55665, 22.46498], [106.58395, 22.474], [106.61269, 22.60301], [106.65316, 22.5757], [106.71698, 22.58432], [106.72321, 22.63606], [106.76293, 22.73491], [106.82404, 22.7881], [106.83685, 22.8098], [106.81271, 22.8226], [106.78422, 22.81532], [106.71128, 22.85982], [106.71387, 22.88296], [106.6734, 22.89587], [106.6516, 22.86862], [106.60179, 22.92884], [106.55976, 22.92311], [106.51306, 22.94891], [106.49749, 22.91164], [106.34961, 22.86718], [106.27022, 22.87722], [106.19705, 22.98475], [106.00179, 22.99049], [105.99568, 22.94178], [105.90119, 22.94168], [105.8726, 22.92756], [105.72382, 23.06641], [105.57594, 23.075], [105.56037, 23.16806], [105.49966, 23.20669], [105.42805, 23.30824], [105.40782, 23.28107], [105.32376, 23.39684], [105.22569, 23.27249], [105.17276, 23.28679], [105.11672, 23.25247], [105.07002, 23.26248], [104.98712, 23.19176], [104.96532, 23.20463], [104.9486, 23.17235], [104.91435, 23.18666], [104.87992, 23.17141], [104.87382, 23.12854], [104.79478, 23.12934], [104.8334, 23.01484], [104.86765, 22.95178], [104.84942, 22.93631], [104.77114, 22.90017], [104.72755, 22.81984], [104.65283, 22.83419], [104.60457, 22.81841], [104.58122, 22.85571], [104.47225, 22.75813], [104.35593, 22.69353], [104.25683, 22.76534], [104.27084, 22.8457], [104.11384, 22.80363], [104.03734, 22.72945], [104.01088, 22.51823], [103.99247, 22.51958], [103.97384, 22.50634], [103.96783, 22.51173], [103.96352, 22.50584], [103.95191, 22.5134], [103.94513, 22.52553], [103.93286, 22.52703], [103.87904, 22.56683], [103.64506, 22.79979], [103.56255, 22.69499], [103.57812, 22.65764], [103.52675, 22.59155], [103.43646, 22.70648], [103.43179, 22.75816], [103.32282, 22.8127], [103.28079, 22.68063], [103.18895, 22.64471], [103.15782, 22.59873], [103.17961, 22.55705], [103.07843, 22.50097], [103.0722, 22.44775], [102.9321, 22.48659], [102.8636, 22.60735], [102.60675, 22.73376], [102.57095, 22.7036], [102.51802, 22.77969], [102.46665, 22.77108], [102.42618, 22.69212], [102.38415, 22.67919], [102.41061, 22.64184], [102.25339, 22.4607], [102.26428, 22.41321], [102.16621, 22.43336], [102.14099, 22.40092], [102.18712, 22.30403], [102.51734, 22.02676], [102.49092, 21.99002], [102.62301, 21.91447], [102.67145, 21.65894], [102.74189, 21.66713], [102.82115, 21.73667], [102.81894, 21.83888], [102.85637, 21.84501], [102.86077, 21.71213], [102.97965, 21.74076], [102.98846, 21.58936], [102.86297, 21.4255], [102.94223, 21.46034], [102.88939, 21.3107], [102.80794, 21.25736], [102.89825, 21.24707], [102.97745, 21.05821], [103.03469, 21.05821], [103.12055, 20.89994], [103.21497, 20.89832], [103.38032, 20.79501], [103.45737, 20.82382], [103.68633, 20.66324], [103.73478, 20.6669], [103.82282, 20.8732], [103.98024, 20.91531], [104.11121, 20.96779], [104.27412, 20.91433], [104.63957, 20.6653], [104.38199, 20.47155], [104.40621, 20.3849], [104.47886, 20.37459], [104.66158, 20.47774], [104.72102, 20.40554], [104.62195, 20.36633], [104.61315, 20.24452], [104.86852, 20.14121], [104.91695, 20.15567], [104.9874, 20.09573], [104.8465, 19.91783], [104.8355, 19.80395], [104.68359, 19.72729], [104.64837, 19.62365], [104.53169, 19.61743], [104.41281, 19.70035], [104.23229, 19.70242], [104.06498, 19.66926], [104.05617, 19.61743], [104.10832, 19.51575], [104.06058, 19.43484], [103.87125, 19.31854], [104.5361, 18.97747], [104.64617, 18.85668], [105.12829, 18.70453], [105.19654, 18.64196], [105.1327, 18.58355], [105.10408, 18.43533], [105.15942, 18.38691], [105.38366, 18.15315], [105.46292, 18.22008], [105.64784, 17.96687], [105.60381, 17.89356], [105.76612, 17.67147], [105.85744, 17.63221], [106.09019, 17.36399], [106.18991, 17.28227], [106.24444, 17.24714], [106.29287, 17.3018], [106.31929, 17.20509], [106.43597, 17.01362], [106.50862, 16.9673], [106.55045, 17.0031], [106.54824, 16.92729], [106.51963, 16.92097], [106.52183, 16.87884], [106.55265, 16.86831], [106.55485, 16.68704], [106.59013, 16.62259], [106.58267, 16.6012], [106.61477, 16.60713], [106.66052, 16.56892], [106.65832, 16.47816], [106.74418, 16.41904], [106.84104, 16.55415], [106.88727, 16.52671], [106.88067, 16.43594], [106.96638, 16.34938], [106.97385, 16.30204], [107.02597, 16.31132], [107.09091, 16.3092], [107.15035, 16.26271], [107.14595, 16.17816], [107.25822, 16.13587], [107.33968, 16.05549], [107.44975, 16.08511], [107.46296, 16.01106], [107.39471, 15.88829], [107.34188, 15.89464], [107.21419, 15.83747], [107.21859, 15.74638], [107.27143, 15.71459], [107.27583, 15.62769], [107.34408, 15.62345], [107.3815, 15.49832], [107.50699, 15.48771], [107.53341, 15.40496], [107.62367, 15.42193], [107.60605, 15.37524], [107.62587, 15.2266], [107.58844, 15.20111], [107.61926, 15.13949], [107.61486, 15.0566], [107.46516, 15.00982], [107.48277, 14.93751], [107.59285, 14.87795], [107.51579, 14.79282], [107.54361, 14.69092], [107.55371, 14.628], [107.52102, 14.59034], [107.52569, 14.54665], [107.48521, 14.40346], [107.44941, 14.41552], [107.39493, 14.32655], [107.40427, 14.24509], [107.33577, 14.11832], [107.37158, 14.07906], [107.35757, 14.02319], [107.38247, 13.99147], [107.44318, 13.99751], [107.46498, 13.91593], [107.45252, 13.78897], [107.53503, 13.73908], [107.61909, 13.52577], [107.62843, 13.3668], [107.49144, 13.01215], [107.49611, 12.88926], [107.55993, 12.7982], [107.5755, 12.52177], [107.55059, 12.36824], [107.4463, 12.29373], [107.42917, 12.24657], [107.34511, 12.33327], [107.15831, 12.27547], [106.99953, 12.08983], [106.92325, 12.06548], [106.79405, 12.0807], [106.70687, 11.96956], [106.4111, 11.97413], [106.4687, 11.86751], [106.44068, 11.86294], [106.44535, 11.8279], [106.41577, 11.76999], [106.45158, 11.68616], [106.44691, 11.66787], [106.37219, 11.69836], [106.30525, 11.67549], [106.26478, 11.72122], [106.18539, 11.75171], [106.13158, 11.73283], [106.06708, 11.77761], [106.02038, 11.77457], [106.00792, 11.7197], [105.95188, 11.63738], [105.88962, 11.67854], [105.8507, 11.66635], [105.80867, 11.60536], [105.81645, 11.56876], [105.87328, 11.55953], [105.88962, 11.43605], [105.86782, 11.28343], [106.10444, 11.07879], [106.1527, 11.10476], [106.1757, 11.07301], [106.20095, 10.97795], [106.14301, 10.98176], [106.18539, 10.79451], [106.06708, 10.8098], [105.94535, 10.9168], [105.93403, 10.83853], [105.84603, 10.85873], [105.86376, 10.89839], [105.77751, 11.03671], [105.50045, 10.94586], [105.42884, 10.96878], [105.34011, 10.86179], [105.11449, 10.96332], [105.08326, 10.95656], [105.02722, 10.89236], [105.09571, 10.72722], [104.95094, 10.64003], [104.87933, 10.52833], [104.59018, 10.53073], [104.49869, 10.4057], [104.47963, 10.43046], [104.43778, 10.42386], [103.99198, 10.48391], [102.47649, 9.66162], [104.81582, 8.03101], [109.55486, 8.10026], [111.60491, 13.57105], [108.00365, 17.98159], [108.10003, 21.47338]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "VU", + iso1A3: "VUT", + iso1N3: "548", + wikidata: "Q686", + nameEn: "Vanuatu", + groups: ["054", "009", "UN"], + callingCodes: ["678"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[156.73836, -14.50464], [174.245, -23.1974], [172.71443, -12.01327], [156.73836, -14.50464]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "WF", + iso1A3: "WLF", + iso1N3: "876", + wikidata: "Q35555", + nameEn: "Wallis and Futuna", + country: "FR", + groups: ["Q1451600", "061", "009", "UN"], + callingCodes: ["681"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-178.66551, -14.32452], [-176.76826, -14.95183], [-175.59809, -12.61507], [-178.66551, -14.32452]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "WS", + iso1A3: "WSM", + iso1N3: "882", + wikidata: "Q683", + nameEn: "Samoa", + groups: ["061", "009", "UN"], + driveSide: "left", + callingCodes: ["685"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[-173.74402, -14.26669], [-170.99605, -15.1275], [-171.39864, -10.21587], [-173.74402, -14.26669]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "XK", + iso1A3: "XKX", + wikidata: "Q1246", + nameEn: "Kosovo", + aliases: ["KV"], + groups: ["039", "150"], + isoStatus: "usrAssn", + callingCodes: ["383"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[21.39045, 42.74888], [21.44047, 42.87276], [21.36941, 42.87397], [21.32974, 42.90424], [21.2719, 42.8994], [21.23534, 42.95523], [21.23877, 43.00848], [21.2041, 43.02277], [21.16734, 42.99694], [21.14465, 43.11089], [21.08952, 43.13471], [21.05378, 43.10707], [21.00749, 43.13984], [20.96287, 43.12416], [20.83727, 43.17842], [20.88685, 43.21697], [20.82145, 43.26769], [20.73811, 43.25068], [20.68688, 43.21335], [20.59929, 43.20492], [20.69515, 43.09641], [20.64557, 43.00826], [20.59929, 43.01067], [20.48692, 42.93208], [20.53484, 42.8885], [20.43734, 42.83157], [20.40594, 42.84853], [20.35692, 42.8335], [20.27869, 42.81945], [20.2539, 42.76245], [20.04898, 42.77701], [20.02088, 42.74789], [20.02915, 42.71147], [20.0969, 42.65559], [20.07761, 42.55582], [20.17127, 42.50469], [20.21797, 42.41237], [20.24399, 42.32168], [20.34479, 42.32656], [20.3819, 42.3029], [20.48857, 42.25444], [20.56955, 42.12097], [20.55633, 42.08173], [20.59434, 42.03879], [20.63069, 41.94913], [20.57946, 41.91593], [20.59524, 41.8818], [20.68523, 41.85318], [20.76786, 41.91839], [20.75464, 42.05229], [21.11491, 42.20794], [21.16614, 42.19815], [21.22728, 42.08909], [21.31983, 42.10993], [21.29913, 42.13954], [21.30496, 42.1418], [21.38428, 42.24465], [21.43882, 42.23609], [21.43882, 42.2789], [21.50823, 42.27156], [21.52145, 42.24465], [21.58992, 42.25915], [21.56772, 42.30946], [21.5264, 42.33634], [21.53467, 42.36809], [21.57021, 42.3647], [21.59029, 42.38042], [21.62887, 42.37664], [21.64209, 42.41081], [21.62556, 42.45106], [21.7035, 42.51899], [21.70522, 42.54176], [21.7327, 42.55041], [21.75672, 42.62695], [21.79413, 42.65923], [21.75025, 42.70125], [21.6626, 42.67813], [21.58755, 42.70418], [21.59154, 42.72643], [21.47498, 42.74695], [21.39045, 42.74888]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "YE", + iso1A3: "YEM", + iso1N3: "887", + wikidata: "Q805", + nameEn: "Yemen", + groups: ["145", "142", "UN"], + callingCodes: ["967"] + }, + geometry: { + type: "MultiPolygon", + 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]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "YT", + iso1A3: "MYT", + iso1N3: "175", + wikidata: "Q17063", + nameEn: "Mayotte", + country: "FR", + groups: ["Q3320166", "EU", "014", "202", "002", "UN"], + callingCodes: ["262"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[43.28731, -13.97126], [45.54824, -13.22353], [45.4971, -11.75965], [43.28731, -13.97126]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "ZA", + iso1A3: "ZAF", + iso1N3: "710", + wikidata: "Q258", + nameEn: "South Africa", + groups: ["018", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["27"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[31.30611, -22.422], [31.16344, -22.32599], [31.08932, -22.34884], [30.86696, -22.28907], [30.6294, -22.32599], [30.48686, -22.31368], [30.38614, -22.34533], [30.28351, -22.35587], [30.2265, -22.2961], [30.13147, -22.30841], [29.92242, -22.19408], [29.76848, -22.14128], [29.64609, -22.12917], [29.37703, -22.19581], [29.21955, -22.17771], [29.18974, -22.18599], [29.15268, -22.21399], [29.10881, -22.21202], [29.0151, -22.22907], [28.91889, -22.44299], [28.63287, -22.55887], [28.34874, -22.5694], [28.04562, -22.8394], [28.04752, -22.90243], [27.93729, -22.96194], [27.93539, -23.04941], [27.74154, -23.2137], [27.6066, -23.21894], [27.52393, -23.37952], [27.33768, -23.40917], [26.99749, -23.65486], [26.84165, -24.24885], [26.51667, -24.47219], [26.46346, -24.60358], [26.39409, -24.63468], [25.8515, -24.75727], [25.84295, -24.78661], [25.88571, -24.87802], [25.72702, -25.25503], [25.69661, -25.29284], [25.6643, -25.4491], [25.58543, -25.6343], [25.33076, -25.76616], [25.12266, -25.75931], [25.01718, -25.72507], [24.8946, -25.80723], [24.67319, -25.81749], [24.44703, -25.73021], [24.36531, -25.773], [24.18287, -25.62916], [23.9244, -25.64286], [23.47588, -25.29971], [23.03497, -25.29971], [22.86012, -25.50572], [22.70808, -25.99186], [22.56365, -26.19668], [22.41921, -26.23078], [22.21206, -26.3773], [22.06192, -26.61882], [21.90703, -26.66808], [21.83291, -26.65959], [21.77114, -26.69015], [21.7854, -26.79199], [21.69322, -26.86152], [21.37869, -26.82083], [21.13353, -26.86661], [20.87031, -26.80047], [20.68596, -26.9039], [20.63275, -26.78181], [20.61754, -26.4692], [20.86081, -26.14892], [20.64795, -25.47827], [20.29826, -24.94869], [20.03678, -24.81004], [20.02809, -24.78725], [19.99817, -24.76768], [19.99882, -28.42622], [18.99885, -28.89165], [17.4579, -28.68718], [17.15405, -28.08573], [16.90446, -28.057], [16.59922, -28.53246], [16.46592, -28.57126], [16.45332, -28.63117], [12.51595, -32.27486], [38.88176, -48.03306], [34.51034, -26.91792], [32.35222, -26.86027], [32.29584, -26.852], [32.22302, -26.84136], [32.19409, -26.84032], [32.13315, -26.84345], [32.09664, -26.80721], [32.00893, -26.8096], [31.97463, -27.11057], [31.97592, -27.31675], [31.49834, -27.31549], [31.15027, -27.20151], [30.96088, -27.0245], [30.97757, -26.92706], [30.88826, -26.79622], [30.81101, -26.84722], [30.78927, -26.48271], [30.95819, -26.26303], [31.13073, -25.91558], [31.31237, -25.7431], [31.4175, -25.71886], [31.86881, -25.99973], [31.974, -25.95387], [31.92649, -25.84216], [32.00631, -25.65044], [31.97875, -25.46356], [32.01676, -25.38117], [32.03196, -25.10785], [31.9835, -24.29983], [31.90368, -24.18892], [31.87707, -23.95293], [31.77445, -23.90082], [31.70223, -23.72695], [31.67942, -23.60858], [31.56539, -23.47268], [31.55779, -23.176], [31.30611, -22.422]], [[29.33204, -29.45598], [29.28545, -29.58456], [29.12553, -29.76266], [29.16548, -29.91706], [28.9338, -30.05072], [28.80222, -30.10579], [28.68627, -30.12885], [28.399, -30.1592], [28.2319, -30.28476], [28.12073, -30.68072], [27.74814, -30.60635], [27.69467, -30.55862], [27.67819, -30.53437], [27.6521, -30.51707], [27.62137, -30.50509], [27.56781, -30.44562], [27.56901, -30.42504], [27.45452, -30.32239], [27.38108, -30.33456], [27.36649, -30.27246], [27.37293, -30.19401], [27.40778, -30.14577], [27.32555, -30.14785], [27.29603, -30.05473], [27.22719, -30.00718], [27.09489, -29.72796], [27.01016, -29.65439], [27.33464, -29.48161], [27.4358, -29.33465], [27.47254, -29.31968], [27.45125, -29.29708], [27.48679, -29.29349], [27.54258, -29.25575], [27.5158, -29.2261], [27.55974, -29.18954], [27.75458, -28.89839], [27.8907, -28.91612], [27.88933, -28.88156], [27.9392, -28.84864], [27.98675, -28.8787], [28.02503, -28.85991], [28.1317, -28.7293], [28.2348, -28.69471], [28.30518, -28.69531], [28.40612, -28.6215], [28.65091, -28.57025], [28.68043, -28.58744], [29.40524, -29.21246], [29.44883, -29.3772], [29.33204, -29.45598]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "ZM", + iso1A3: "ZMB", + iso1N3: "894", + wikidata: "Q953", + nameEn: "Zambia", + groups: ["014", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["260"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[32.95389, -9.40138], [32.76233, -9.31963], [32.75611, -9.28583], [32.53661, -9.24281], [32.49147, -9.14754], [32.43543, -9.11988], [32.25486, -9.13371], [32.16146, -9.05993], [32.08206, -9.04609], [31.98866, -9.07069], [31.94196, -9.02303], [31.94663, -8.93846], [31.81587, -8.88618], [31.71158, -8.91386], [31.57147, -8.81388], [31.57147, -8.70619], [31.37533, -8.60769], [31.00796, -8.58615], [30.79243, -8.27382], [28.88917, -8.4831], [28.9711, -8.66935], [28.38526, -9.23393], [28.36562, -9.30091], [28.52636, -9.35379], [28.51627, -9.44726], [28.56208, -9.49122], [28.68532, -9.78], [28.62795, -9.92942], [28.65032, -10.65133], [28.37241, -11.57848], [28.48357, -11.87532], [29.18592, -12.37921], [29.4992, -12.43843], [29.48404, -12.23604], [29.8139, -12.14898], [29.81551, -13.44683], [29.65078, -13.41844], [29.60531, -13.21685], [29.01918, -13.41353], [28.33199, -12.41375], [27.59932, -12.22123], [27.21025, -11.76157], [27.22541, -11.60323], [27.04351, -11.61312], [26.88687, -12.01868], [26.01777, -11.91488], [25.33058, -11.65767], [25.34069, -11.19707], [24.42612, -11.44975], [24.34528, -11.06816], [24.00027, -10.89356], [24.02603, -11.15368], [23.98804, -12.13149], [24.06672, -12.29058], [23.90937, -12.844], [24.03339, -12.99091], [21.97988, -13.00148], [22.00323, -16.18028], [22.17217, -16.50269], [23.20038, -17.47563], [23.47474, -17.62877], [24.23619, -17.47489], [24.32811, -17.49082], [24.38712, -17.46818], [24.5621, -17.52963], [24.70864, -17.49501], [25.00198, -17.58221], [25.26433, -17.79571], [25.51646, -17.86232], [25.6827, -17.81987], [25.85738, -17.91403], [25.85892, -17.97726], [26.08925, -17.98168], [26.0908, -17.93021], [26.21601, -17.88608], [26.55918, -17.99638], [26.68403, -18.07411], [26.74314, -18.0199], [26.89926, -17.98756], [27.14196, -17.81398], [27.30736, -17.60487], [27.61377, -17.34378], [27.62795, -17.24365], [27.83141, -16.96274], [28.73725, -16.5528], [28.76199, -16.51575], [28.81454, -16.48611], [28.8501, -16.04537], [28.9243, -15.93987], [29.01298, -15.93805], [29.21955, -15.76589], [29.4437, -15.68702], [29.8317, -15.6126], [30.35574, -15.6513], [30.41902, -15.62269], [30.22098, -14.99447], [33.24249, -14.00019], [33.16749, -13.93992], [33.07568, -13.98447], [33.02977, -14.05022], [32.99042, -13.95689], [32.88985, -13.82956], [32.79015, -13.80755], [32.76962, -13.77224], [32.84528, -13.71576], [32.7828, -13.64805], [32.68654, -13.64268], [32.66468, -13.60019], [32.68436, -13.55769], [32.73683, -13.57682], [32.84176, -13.52794], [32.86113, -13.47292], [33.0078, -13.19492], [32.98289, -13.12671], [33.02181, -12.88707], [32.96733, -12.88251], [32.94397, -12.76868], [33.05917, -12.59554], [33.18837, -12.61377], [33.28177, -12.54692], [33.37517, -12.54085], [33.54485, -12.35996], [33.47636, -12.32498], [33.3705, -12.34931], [33.25998, -12.14242], [33.33937, -11.91252], [33.32692, -11.59248], [33.24252, -11.59302], [33.23663, -11.40637], [33.29267, -11.43536], [33.29267, -11.3789], [33.39697, -11.15296], [33.25998, -10.88862], [33.28022, -10.84428], [33.47636, -10.78465], [33.70675, -10.56896], [33.54797, -10.36077], [33.53863, -10.20148], [33.31297, -10.05133], [33.37902, -9.9104], [33.36581, -9.81063], [33.31517, -9.82364], [33.2095, -9.61099], [33.12144, -9.58929], [33.10163, -9.66525], [33.05485, -9.61316], [33.00256, -9.63053], [33.00476, -9.5133], [32.95389, -9.40138]]]] + } + }, { + type: "Feature", + properties: { + iso1A2: "ZW", + iso1A3: "ZWE", + iso1N3: "716", + wikidata: "Q954", + nameEn: "Zimbabwe", + groups: ["014", "202", "002", "UN"], + driveSide: "left", + callingCodes: ["263"] + }, + geometry: { + type: "MultiPolygon", + coordinates: [[[[30.41902, -15.62269], [30.35574, -15.6513], [29.8317, -15.6126], [29.4437, -15.68702], [29.21955, -15.76589], [29.01298, -15.93805], [28.9243, -15.93987], [28.8501, -16.04537], [28.81454, -16.48611], [28.76199, -16.51575], [28.73725, -16.5528], [27.83141, -16.96274], [27.62795, -17.24365], [27.61377, -17.34378], [27.30736, -17.60487], [27.14196, -17.81398], [26.89926, -17.98756], [26.74314, -18.0199], [26.68403, -18.07411], [26.55918, -17.99638], [26.21601, -17.88608], [26.0908, -17.93021], [26.08925, -17.98168], [25.85892, -17.97726], [25.85738, -17.91403], [25.6827, -17.81987], [25.51646, -17.86232], [25.26433, -17.79571], [25.23909, -17.90832], [25.31799, -18.07091], [25.39972, -18.12691], [25.53465, -18.39041], [25.68859, -18.56165], [25.79217, -18.6355], [25.82353, -18.82808], [25.94326, -18.90362], [25.99837, -19.02943], [25.96226, -19.08152], [26.17227, -19.53709], [26.72246, -19.92707], [27.21278, -20.08244], [27.29831, -20.28935], [27.28865, -20.49873], [27.69361, -20.48531], [27.72972, -20.51735], [27.69171, -21.08409], [27.91407, -21.31621], [28.01669, -21.57624], [28.29416, -21.59037], [28.49942, -21.66634], [28.58114, -21.63455], [29.07763, -21.81877], [29.04023, -21.85864], [29.02191, -21.90647], [29.02191, -21.95665], [29.04108, -22.00563], [29.08495, -22.04867], [29.14501, -22.07275], [29.1974, -22.07472], [29.24648, -22.05967], [29.3533, -22.18363], [29.37703, -22.19581], [29.64609, -22.12917], [29.76848, -22.14128], [29.92242, -22.19408], [30.13147, -22.30841], [30.2265, -22.2961], [30.28351, -22.35587], [30.38614, -22.34533], [30.48686, -22.31368], [30.6294, -22.32599], [30.86696, -22.28907], [31.08932, -22.34884], [31.16344, -22.32599], [31.30611, -22.422], [31.38336, -22.36919], [32.41234, -21.31246], [32.48236, -21.32873], [32.37115, -21.133], [32.51644, -20.91929], [32.48122, -20.63319], [32.55167, -20.56312], [32.66174, -20.56106], [32.85987, -20.27841], [32.85987, -20.16686], [32.93032, -20.03868], [33.01178, -20.02007], [33.06461, -19.77787], [32.95013, -19.67219], [32.84666, -19.68462], [32.84446, -19.48343], [32.78282, -19.47513], [32.77966, -19.36098], [32.85107, -19.29238], [32.87088, -19.09279], [32.84006, -19.0262], [32.72118, -19.02204], [32.69917, -18.94293], [32.73439, -18.92628], [32.70137, -18.84712], [32.82465, -18.77419], [32.9017, -18.7992], [32.95013, -18.69079], [32.88629, -18.58023], [32.88629, -18.51344], [33.02278, -18.4696], [33.03159, -18.35054], [32.94133, -17.99705], [33.0492, -17.60298], [32.98536, -17.55891], [32.96554, -17.48964], [33.0426, -17.3468], [33.00517, -17.30477], [32.96554, -17.11971], [32.84113, -16.92259], [32.91051, -16.89446], [32.97655, -16.70689], [32.78943, -16.70267], [32.69917, -16.66893], [32.71017, -16.59932], [32.42838, -16.4727], [32.28529, -16.43892], [32.02772, -16.43892], [31.91324, -16.41569], [31.90223, -16.34388], [31.67988, -16.19595], [31.42451, -16.15154], [31.30563, -16.01193], [31.13171, -15.98019], [30.97761, -16.05848], [30.91597, -15.99924], [30.42568, -15.9962], [30.41902, -15.62269]]]] + } + }]; + var borders_default = { + type: type, + features: features + }; // src/country-coder.ts + + var borders = borders_default; + var whichPolygonGetter = {}; + var featuresByCode = {}; + var idFilterRegex = /(?=(?!^(and|the|of|el|la|de)$))(\b(and|the|of|el|la|de)\b)|[-_ .,'()&[\]/]/gi; + + function canonicalID(id) { + var s = id || ""; + + if (s.charAt(0) === ".") { + return s.toUpperCase(); + } else { + return s.replace(idFilterRegex, "").toUpperCase(); + } + } + + var levels = ["subterritory", "territory", "subcountryGroup", "country", "sharedLandform", "intermediateRegion", "subregion", "region", "subunion", "union", "unitedNations", "world"]; + loadDerivedDataAndCaches(borders); + + function loadDerivedDataAndCaches(borders2) { + var identifierProps = ["iso1A2", "iso1A3", "m49", "wikidata", "emojiFlag", "ccTLD", "nameEn"]; + var geometryFeatures = []; + + for (var i in borders2.features) { + var feature2 = borders2.features[i]; + feature2.properties.id = feature2.properties.iso1A2 || feature2.properties.m49 || feature2.properties.wikidata; + loadM49(feature2); + loadTLD(feature2); + loadIsoStatus(feature2); + loadLevel(feature2); + loadGroups(feature2); + loadFlag(feature2); + cacheFeatureByIDs(feature2); + if (feature2.geometry) geometryFeatures.push(feature2); + } + + for (var _i in borders2.features) { + var _feature = borders2.features[_i]; + _feature.properties.groups = _feature.properties.groups.map(function (groupID) { + return featuresByCode[groupID].properties.id; + }); + loadMembersForGroupsOf(_feature); + } + + for (var _i2 in borders2.features) { + var _feature2 = borders2.features[_i2]; + loadRoadSpeedUnit(_feature2); + loadRoadHeightUnit(_feature2); + loadDriveSide(_feature2); + loadCallingCodes(_feature2); + loadGroupGroups(_feature2); + } + + for (var _i3 in borders2.features) { + var _feature3 = borders2.features[_i3]; + + _feature3.properties.groups.sort(function (groupID1, groupID2) { + return levels.indexOf(featuresByCode[groupID1].properties.level) - levels.indexOf(featuresByCode[groupID2].properties.level); + }); + + if (_feature3.properties.members) _feature3.properties.members.sort(function (id1, id2) { + var diff = levels.indexOf(featuresByCode[id1].properties.level) - levels.indexOf(featuresByCode[id2].properties.level); + + if (diff === 0) { + return borders2.features.indexOf(featuresByCode[id1]) - borders2.features.indexOf(featuresByCode[id2]); + } + + return diff; + }); + } + + var geometryOnlyCollection = { + type: "FeatureCollection", + features: geometryFeatures + }; + whichPolygonGetter = whichPolygon_1(geometryOnlyCollection); + + function loadGroups(feature2) { + var props = feature2.properties; + + if (!props.groups) { + props.groups = []; + } + + if (feature2.geometry && props.country) { + props.groups.push(props.country); + } + + if (props.m49 !== "001") { + props.groups.push("001"); + } + } + + function loadM49(feature2) { + var props = feature2.properties; + + if (!props.m49 && props.iso1N3) { + props.m49 = props.iso1N3; + } + } + + function loadTLD(feature2) { + var props = feature2.properties; + if (props.level === "unitedNations") return; + + if (!props.ccTLD && props.iso1A2) { + props.ccTLD = "." + props.iso1A2.toLowerCase(); + } + } + + function loadIsoStatus(feature2) { + var props = feature2.properties; + + if (!props.isoStatus && props.iso1A2) { + props.isoStatus = "official"; + } + } + + function loadLevel(feature2) { + var props = feature2.properties; + if (props.level) return; + + if (!props.country) { + props.level = "country"; + } else if (!props.iso1A2 || props.isoStatus === "official") { + props.level = "territory"; + } else { + props.level = "subterritory"; + } + } + + function loadGroupGroups(feature2) { + var props = feature2.properties; + if (feature2.geometry || !props.members) return; + var featureLevelIndex = levels.indexOf(props.level); + var sharedGroups = []; + + var _loop = function _loop(_i4) { + var memberID = props.members[_i4]; + var member = featuresByCode[memberID]; + var memberGroups = member.properties.groups.filter(function (groupID) { + return groupID !== feature2.properties.id && featureLevelIndex < levels.indexOf(featuresByCode[groupID].properties.level); + }); + + if (_i4 === "0") { + sharedGroups = memberGroups; + } else { + sharedGroups = sharedGroups.filter(function (groupID) { + return memberGroups.indexOf(groupID) !== -1; + }); + } + }; + + for (var _i4 in props.members) { + _loop(_i4); + } + + props.groups = props.groups.concat(sharedGroups.filter(function (groupID) { + return props.groups.indexOf(groupID) === -1; + })); + + for (var j in sharedGroups) { + var groupFeature = featuresByCode[sharedGroups[j]]; + + if (groupFeature.properties.members.indexOf(props.id) === -1) { + groupFeature.properties.members.push(props.id); + } + } + } + + function loadRoadSpeedUnit(feature2) { + var props = feature2.properties; + + if (feature2.geometry) { + if (!props.roadSpeedUnit) props.roadSpeedUnit = "km/h"; + } else if (props.members) { + var vals = Array.from(new Set(props.members.map(function (id) { + var member = featuresByCode[id]; + if (member.geometry) return member.properties.roadSpeedUnit || "km/h"; + }).filter(Boolean))); + if (vals.length === 1) props.roadSpeedUnit = vals[0]; + } + } + + function loadRoadHeightUnit(feature2) { + var props = feature2.properties; + + if (feature2.geometry) { + if (!props.roadHeightUnit) props.roadHeightUnit = "m"; + } else if (props.members) { + var vals = Array.from(new Set(props.members.map(function (id) { + var member = featuresByCode[id]; + if (member.geometry) return member.properties.roadHeightUnit || "m"; + }).filter(Boolean))); + if (vals.length === 1) props.roadHeightUnit = vals[0]; + } + } + + function loadDriveSide(feature2) { + var props = feature2.properties; + + if (feature2.geometry) { + if (!props.driveSide) props.driveSide = "right"; + } else if (props.members) { + var vals = Array.from(new Set(props.members.map(function (id) { + var member = featuresByCode[id]; + if (member.geometry) return member.properties.driveSide || "right"; + }).filter(Boolean))); + if (vals.length === 1) props.driveSide = vals[0]; + } + } + + function loadCallingCodes(feature2) { + var props = feature2.properties; + + if (!feature2.geometry && props.members) { + props.callingCodes = Array.from(new Set(props.members.reduce(function (array, id) { + var member = featuresByCode[id]; + if (member.geometry && member.properties.callingCodes) return array.concat(member.properties.callingCodes); + return array; + }, []))); + } + } + + function loadFlag(feature2) { + if (!feature2.properties.iso1A2) return; + var flag = feature2.properties.iso1A2.replace(/./g, function (_char) { + return String.fromCodePoint(_char.charCodeAt(0) + 127397); + }); + feature2.properties.emojiFlag = flag; + } + + function loadMembersForGroupsOf(feature2) { + for (var j in feature2.properties.groups) { + var groupID = feature2.properties.groups[j]; + var groupFeature = featuresByCode[groupID]; + if (!groupFeature.properties.members) groupFeature.properties.members = []; + groupFeature.properties.members.push(feature2.properties.id); + } + } + + function cacheFeatureByIDs(feature2) { + var ids = []; + + for (var k in identifierProps) { + var prop = identifierProps[k]; + var id = feature2.properties[prop]; + if (id) ids.push(id); + } + + if (feature2.properties.aliases) { + for (var j in feature2.properties.aliases) { + ids.push(feature2.properties.aliases[j]); + } + } + + for (var _i5 in ids) { + var _id = canonicalID(ids[_i5]); + + featuresByCode[_id] = feature2; + } + } + } + + function locArray(loc) { + if (Array.isArray(loc)) { + return loc; + } else if (loc.coordinates) { + return loc.coordinates; + } + + return loc.geometry.coordinates; + } + + function smallestFeature(loc) { + var query = locArray(loc); + var featureProperties = whichPolygonGetter(query); + if (!featureProperties) return null; + return featuresByCode[featureProperties.id]; + } + + function countryFeature(loc) { + var feature2 = smallestFeature(loc); + if (!feature2) return null; + var countryCode = feature2.properties.country || feature2.properties.iso1A2; + return featuresByCode[countryCode] || null; + } + + var defaultOpts = { + level: void 0, + maxLevel: void 0, + withProp: void 0 + }; + + function featureForLoc(loc, opts) { + var targetLevel = opts.level || "country"; + var maxLevel = opts.maxLevel || "world"; + var withProp = opts.withProp; + var targetLevelIndex = levels.indexOf(targetLevel); + if (targetLevelIndex === -1) return null; + var maxLevelIndex = levels.indexOf(maxLevel); + if (maxLevelIndex === -1) return null; + if (maxLevelIndex < targetLevelIndex) return null; + + if (targetLevel === "country") { + var fastFeature = countryFeature(loc); + + if (fastFeature) { + if (!withProp || fastFeature.properties[withProp]) { + return fastFeature; + } + } + } + + var features2 = featuresContaining(loc); + + for (var i in features2) { + var feature2 = features2[i]; + var levelIndex = levels.indexOf(feature2.properties.level); + + if (feature2.properties.level === targetLevel || levelIndex > targetLevelIndex && levelIndex <= maxLevelIndex) { + if (!withProp || feature2.properties[withProp]) { + return feature2; + } + } + } + + return null; + } + + function featureForID(id) { + var stringID; + + if (typeof id === "number") { + stringID = id.toString(); + + if (stringID.length === 1) { + stringID = "00" + stringID; + } else if (stringID.length === 2) { + stringID = "0" + stringID; + } + } else { + stringID = canonicalID(id); + } + + return featuresByCode[stringID] || null; + } + + function smallestFeaturesForBbox(bbox) { + return whichPolygonGetter.bbox(bbox).map(function (props) { + return featuresByCode[props.id]; + }); + } + + function smallestOrMatchingFeature(query) { + if (_typeof(query) === "object") { + return smallestFeature(query); + } + + return featureForID(query); + } + + function feature$1(query) { + var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultOpts; + + if (_typeof(query) === "object") { + return featureForLoc(query, opts); + } + + return featureForID(query); + } + + function iso1A2Code(query) { + var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultOpts; + opts.withProp = "iso1A2"; + var match = feature$1(query, opts); + if (!match) return null; + return match.properties.iso1A2 || null; + } + + function featuresContaining(query, strict) { + var matchingFeatures; + + if (Array.isArray(query) && query.length === 4) { + matchingFeatures = smallestFeaturesForBbox(query); + } else { + var smallestOrMatching = smallestOrMatchingFeature(query); + matchingFeatures = smallestOrMatching ? [smallestOrMatching] : []; + } + + if (!matchingFeatures.length) return []; + var returnFeatures; + + if (!strict || _typeof(query) === "object") { + returnFeatures = matchingFeatures.slice(); + } else { + returnFeatures = []; + } + + for (var j in matchingFeatures) { + var properties = matchingFeatures[j].properties; + + for (var i in properties.groups) { + var groupID = properties.groups[i]; + var groupFeature = featuresByCode[groupID]; + + if (returnFeatures.indexOf(groupFeature) === -1) { + returnFeatures.push(groupFeature); + } + } + } + + return returnFeatures; + } + + function featuresIn(id, strict) { + var feature2 = featureForID(id); + if (!feature2) return []; + var features2 = []; + + if (!strict) { + features2.push(feature2); + } + + var properties = feature2.properties; + + if (properties.members) { + for (var i in properties.members) { + var memberID = properties.members[i]; + features2.push(featuresByCode[memberID]); + } + } + + return features2; + } + + function aggregateFeature(id) { + var features2 = featuresIn(id, false); + if (features2.length === 0) return null; + var aggregateCoordinates = []; + + for (var i in features2) { + var feature2 = features2[i]; + + if (feature2.geometry && feature2.geometry.type === "MultiPolygon" && feature2.geometry.coordinates) { + aggregateCoordinates = aggregateCoordinates.concat(feature2.geometry.coordinates); + } + } + + return { + type: "Feature", + properties: features2[0].properties, + geometry: { + type: "MultiPolygon", + coordinates: aggregateCoordinates + } + }; + } + + function roadSpeedUnit(query) { + var feature2 = smallestOrMatchingFeature(query); + return feature2 && feature2.properties.roadSpeedUnit || null; + } + + function roadHeightUnit(query) { + var feature2 = smallestOrMatchingFeature(query); + return feature2 && feature2.properties.roadHeightUnit || null; + } + + var geojsonArea = {}; + + var wgs84$1 = {}; + + wgs84$1.RADIUS = 6378137; + wgs84$1.FLATTENING = 1 / 298.257223563; + wgs84$1.POLAR_RADIUS = 6356752.3142; + + var wgs84 = wgs84$1; + geojsonArea.geometry = geometry; + geojsonArea.ring = ringArea; + + function geometry(_) { + var area = 0, + i; + + switch (_.type) { + case 'Polygon': + return polygonArea(_.coordinates); + + case 'MultiPolygon': + for (i = 0; i < _.coordinates.length; i++) { + area += polygonArea(_.coordinates[i]); + } + + return area; + + case 'Point': + case 'MultiPoint': + case 'LineString': + case 'MultiLineString': + return 0; + + case 'GeometryCollection': + for (i = 0; i < _.geometries.length; i++) { + area += geometry(_.geometries[i]); + } + + return area; + } + } + + function polygonArea(coords) { + var area = 0; + + if (coords && coords.length > 0) { + area += Math.abs(ringArea(coords[0])); + + for (var i = 1; i < coords.length; i++) { + area -= Math.abs(ringArea(coords[i])); + } + } + + return area; + } + /** + * Calculate the approximate area of the polygon were it projected onto + * the earth. Note that this area will be positive if ring is oriented + * clockwise, otherwise it will be negative. + * + * Reference: + * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for + * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion + * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 + * + * Returns: + * {float} The approximate signed geodesic area of the polygon in square + * meters. + */ + + + function ringArea(coords) { + var p1, + p2, + p3, + lowerIndex, + middleIndex, + upperIndex, + i, + area = 0, + coordsLength = coords.length; + + if (coordsLength > 2) { + for (i = 0; i < coordsLength; i++) { + if (i === coordsLength - 2) { + // i = N-2 + lowerIndex = coordsLength - 2; + middleIndex = coordsLength - 1; + upperIndex = 0; + } else if (i === coordsLength - 1) { + // i = N-1 + lowerIndex = coordsLength - 1; + middleIndex = 0; + upperIndex = 1; + } else { + // i = 0 to N-3 + lowerIndex = i; + middleIndex = i + 1; + upperIndex = i + 2; + } + + p1 = coords[lowerIndex]; + p2 = coords[middleIndex]; + p3 = coords[upperIndex]; + area += (rad(p3[0]) - rad(p1[0])) * Math.sin(rad(p2[1])); + } + + area = area * wgs84.RADIUS * wgs84.RADIUS / 2; + } + + return area; + } + + function rad(_) { + return _ * Math.PI / 180; + } + + var inputValidation = {}; + + var $$g = _export; + var $includes = arrayIncludes.includes; + var addToUnscopables$1 = addToUnscopables$5; + + // `Array.prototype.includes` method + // https://tc39.es/ecma262/#sec-array.prototype.includes + $$g({ target: 'Array', proto: true }, { + includes: function includes(el /* , fromIndex = 0 */) { + return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined); + } + }); + + // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables + addToUnscopables$1('includes'); + + var validateCenter$1 = {}; + + validateCenter$1.validateCenter = function validateCenter(center) { + var validCenterLengths = [2, 3]; + + if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) { + throw new Error("ERROR! Center has to be an array of length two or three"); + } + + var _center = _slicedToArray(center, 2), + lng = _center[0], + lat = _center[1]; + + if (typeof lng !== "number" || typeof lat !== "number") { + throw new Error("ERROR! Longitude and Latitude has to be numbers but where ".concat(_typeof(lng), " and ").concat(_typeof(lat))); + } + + if (lng > 180 || lng < -180) { + throw new Error("ERROR! Longitude has to be between -180 and 180 but was ".concat(lng)); + } + + if (lat > 90 || lat < -90) { + throw new Error("ERROR! Latitude has to be between -90 and 90 but was ".concat(lat)); + } + }; + + var validateRadius$1 = {}; + + validateRadius$1.validateRadius = function validateRadius(radius) { + if (typeof radius !== "number") { + throw new Error("ERROR! Radius has to be a positive number but was: ".concat(_typeof(radius))); + } + + if (radius <= 0) { + throw new Error("ERROR! Radius has to be a positive number but was: ".concat(radius)); + } + }; + + var validateNumberOfEdges$1 = {}; + + validateNumberOfEdges$1.validateNumberOfEdges = function validateNumberOfEdges(numberOfEdges) { + if (typeof numberOfEdges !== "number") { + var ARGUMENT_TYPE = Array.isArray(numberOfEdges) ? "array" : _typeof(numberOfEdges); + throw new Error("ERROR! Number of edges has to be a number but was: ".concat(ARGUMENT_TYPE)); + } + + if (numberOfEdges < 3) { + throw new Error("ERROR! Number of edges has to be at least 3 but was: ".concat(numberOfEdges)); + } + }; + + var validateEarthRadius$1 = {}; + + validateEarthRadius$1.validateEarthRadius = function validateEarthRadius(earthRadius) { + if (typeof earthRadius !== "number") { + var ARGUMENT_TYPE = Array.isArray(earthRadius) ? "array" : _typeof(earthRadius); + throw new Error("ERROR! Earth radius has to be a number but was: ".concat(ARGUMENT_TYPE)); + } + + if (earthRadius <= 0) { + throw new Error("ERROR! Earth radius has to be a positive number but was: ".concat(earthRadius)); + } + }; + + var validateBearing$1 = {}; + + validateBearing$1.validateBearing = function validateBearing(bearing) { + if (typeof bearing !== "number") { + var ARGUMENT_TYPE = Array.isArray(bearing) ? "array" : _typeof(bearing); + throw new Error("ERROR! Bearing has to be a number but was: ".concat(ARGUMENT_TYPE)); + } + }; + + var validateCenter = validateCenter$1.validateCenter; + var validateRadius = validateRadius$1.validateRadius; + var validateNumberOfEdges = validateNumberOfEdges$1.validateNumberOfEdges; + var validateEarthRadius = validateEarthRadius$1.validateEarthRadius; + var validateBearing = validateBearing$1.validateBearing; + + function validateInput$1(_ref) { + var center = _ref.center, + radius = _ref.radius, + numberOfEdges = _ref.numberOfEdges, + earthRadius = _ref.earthRadius, + bearing = _ref.bearing; + validateCenter(center); + validateRadius(radius); + validateNumberOfEdges(numberOfEdges); + validateEarthRadius(earthRadius); + validateBearing(bearing); + } + + inputValidation.validateCenter = validateCenter; + inputValidation.validateRadius = validateRadius; + inputValidation.validateNumberOfEdges = validateNumberOfEdges; + inputValidation.validateEarthRadius = validateEarthRadius; + inputValidation.validateBearing = validateBearing; + inputValidation.validateInput = validateInput$1; + + var validateInput = inputValidation.validateInput; + var defaultEarthRadius = 6378137; // equatorial Earth radius + + function toRadians(angleInDegrees) { + return angleInDegrees * Math.PI / 180; + } + + function toDegrees(angleInRadians) { + return angleInRadians * 180 / Math.PI; + } + + function offset(c1, distance, earthRadius, bearing) { + var lat1 = toRadians(c1[1]); + var lon1 = toRadians(c1[0]); + var dByR = distance / earthRadius; + var lat = Math.asin(Math.sin(lat1) * Math.cos(dByR) + Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing)); + var lon = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat)); + return [toDegrees(lon), toDegrees(lat)]; + } + + var circleToPolygon = function circleToPolygon(center, radius, options) { + var n = getNumberOfEdges(options); + var earthRadius = getEarthRadius(options); + var bearing = getBearing(options); + var direction = getDirection(options); // validateInput() throws error on invalid input and do nothing on valid input + + validateInput({ + center: center, + radius: radius, + numberOfEdges: n, + earthRadius: earthRadius, + bearing: bearing + }); + var start = toRadians(bearing); + var coordinates = []; + + for (var i = 0; i < n; ++i) { + coordinates.push(offset(center, radius, earthRadius, start + direction * 2 * Math.PI * -i / n)); + } + + coordinates.push(coordinates[0]); + return { + type: "Polygon", + coordinates: [coordinates] + }; + }; + + function getNumberOfEdges(options) { + if (isUndefinedOrNull(options)) { + return 32; + } else if (isObjectNotArray(options)) { + var numberOfEdges = options.numberOfEdges; + return numberOfEdges === undefined ? 32 : numberOfEdges; + } + + return options; + } + + function getEarthRadius(options) { + if (isUndefinedOrNull(options)) { + return defaultEarthRadius; + } else if (isObjectNotArray(options)) { + var earthRadius = options.earthRadius; + return earthRadius === undefined ? defaultEarthRadius : earthRadius; + } + + return defaultEarthRadius; + } + + function getDirection(options) { + if (isObjectNotArray(options) && options.rightHandRule) { + return -1; + } + + return 1; + } + + function getBearing(options) { + if (isUndefinedOrNull(options)) { + return 0; + } else if (isObjectNotArray(options)) { + var bearing = options.bearing; + return bearing === undefined ? 0 : bearing; + } + + return 0; + } + + function isObjectNotArray(argument) { + return argument !== null && _typeof(argument) === "object" && !Array.isArray(argument); + } + + function isUndefinedOrNull(argument) { + return argument === null || argument === undefined; + } + + var $$f = _export; + + // `Number.EPSILON` constant + // https://tc39.es/ecma262/#sec-number.epsilon + $$f({ target: 'Number', stat: true }, { + EPSILON: Math.pow(2, -52) + }); + + var requireObjectCoercible$4 = requireObjectCoercible$e; + + var quot = /"/g; + + // `CreateHTML` abstract operation + // https://tc39.es/ecma262/#sec-createhtml + var createHtml = function (string, tag, attribute, value) { + var S = String(requireObjectCoercible$4(string)); + var p1 = '<' + tag; + if (attribute !== '') p1 += ' ' + attribute + '="' + String(value).replace(quot, '"') + '"'; + return p1 + '>' + S + ''; + }; + + var fails$6 = fails$N; + + // check the existence of a method, lowercase + // of a tag and escaping quotes in arguments + var stringHtmlForced = function (METHOD_NAME) { + return fails$6(function () { + var test = ''[METHOD_NAME]('"'); + return test !== test.toLowerCase() || test.split('"').length > 3; + }); + }; + + var $$e = _export; + var createHTML = createHtml; + var forcedStringHTMLMethod = stringHtmlForced; + + // `String.prototype.link` method + // https://tc39.es/ecma262/#sec-string.prototype.link + $$e({ target: 'String', proto: true, forced: forcedStringHTMLMethod('link') }, { + link: function link(url) { + return createHTML(this, 'a', 'href', url); + } + }); + + /** + * splaytree v3.1.0 + * Fast Splay tree for Node and browser + * + * @author Alexander Milevski + * @license MIT + * @preserve + */ + var Node = + /** @class */ + function () { + function Node(key, data) { + this.next = null; + this.key = key; + this.data = data; + this.left = null; + this.right = null; + } + + return Node; + }(); + /* follows "An implementation of top-down splaying" + * by D. Sleator March 1992 + */ + + + function DEFAULT_COMPARE(a, b) { + return a > b ? 1 : a < b ? -1 : 0; + } + /** + * Simple top down splay, not requiring i to be in the tree t. + */ + + + function splay(i, t, comparator) { + var N = new Node(null, null); + var l = N; + var r = N; + + while (true) { + var cmp = comparator(i, t.key); //if (i < t.key) { + + if (cmp < 0) { + if (t.left === null) break; //if (i < t.left.key) { + + if (comparator(i, t.left.key) < 0) { + var y = t.left; + /* rotate right */ + + t.left = y.right; + y.right = t; + t = y; + if (t.left === null) break; + } + + r.left = t; + /* link right */ + + r = t; + t = t.left; //} else if (i > t.key) { + } else if (cmp > 0) { + if (t.right === null) break; //if (i > t.right.key) { + + if (comparator(i, t.right.key) > 0) { + var y = t.right; + /* rotate left */ + + t.right = y.left; + y.left = t; + t = y; + if (t.right === null) break; + } + + l.right = t; + /* link left */ + + l = t; + t = t.right; + } else break; + } + /* assemble */ + + + l.right = t.left; + r.left = t.right; + t.left = N.right; + t.right = N.left; + return t; + } + + function insert(i, data, t, comparator) { + var node = new Node(i, data); + + if (t === null) { + node.left = node.right = null; + return node; + } + + t = splay(i, t, comparator); + var cmp = comparator(i, t.key); + + if (cmp < 0) { + node.left = t.left; + node.right = t; + t.left = null; + } else if (cmp >= 0) { + node.right = t.right; + node.left = t; + t.right = null; + } + + return node; + } + + function split(key, v, comparator) { + var left = null; + var right = null; + + if (v) { + v = splay(key, v, comparator); + var cmp = comparator(v.key, key); + + if (cmp === 0) { + left = v.left; + right = v.right; + } else if (cmp < 0) { + right = v.right; + v.right = null; + left = v; + } else { + left = v.left; + v.left = null; + right = v; + } + } + + return { + left: left, + right: right + }; + } + + function merge$3(left, right, comparator) { + if (right === null) return left; + if (left === null) return right; + right = splay(left.key, right, comparator); + right.left = left; + return right; + } + /** + * Prints level of the tree + */ + + + function printRow(root, prefix, isTail, out, printNode) { + if (root) { + out("" + prefix + (isTail ? '└── ' : '├── ') + printNode(root) + "\n"); + var indent = prefix + (isTail ? ' ' : '│ '); + if (root.left) printRow(root.left, indent, false, out, printNode); + if (root.right) printRow(root.right, indent, true, out, printNode); + } + } + + var Tree = + /** @class */ + function () { + function Tree(comparator) { + if (comparator === void 0) { + comparator = DEFAULT_COMPARE; + } + + this._root = null; + this._size = 0; + this._comparator = comparator; + } + /** + * Inserts a key, allows duplicates + */ + + + Tree.prototype.insert = function (key, data) { + this._size++; + return this._root = insert(key, data, this._root, this._comparator); + }; + /** + * Adds a key, if it is not present in the tree + */ + + + Tree.prototype.add = function (key, data) { + var node = new Node(key, data); + + if (this._root === null) { + node.left = node.right = null; + this._size++; + this._root = node; + } + + var comparator = this._comparator; + var t = splay(key, this._root, comparator); + var cmp = comparator(key, t.key); + if (cmp === 0) this._root = t;else { + if (cmp < 0) { + node.left = t.left; + node.right = t; + t.left = null; + } else if (cmp > 0) { + node.right = t.right; + node.left = t; + t.right = null; + } + + this._size++; + this._root = node; + } + return this._root; + }; + /** + * @param {Key} key + * @return {Node|null} + */ + + + Tree.prototype.remove = function (key) { + this._root = this._remove(key, this._root, this._comparator); + }; + /** + * Deletes i from the tree if it's there + */ + + + Tree.prototype._remove = function (i, t, comparator) { + var x; + if (t === null) return null; + t = splay(i, t, comparator); + var cmp = comparator(i, t.key); + + if (cmp === 0) { + /* found it */ + if (t.left === null) { + x = t.right; + } else { + x = splay(i, t.left, comparator); + x.right = t.right; + } + + this._size--; + return x; + } + + return t; + /* It wasn't there */ + }; + /** + * Removes and returns the node with smallest key + */ + + + Tree.prototype.pop = function () { + var node = this._root; + + if (node) { + while (node.left) { + node = node.left; + } + + this._root = splay(node.key, this._root, this._comparator); + this._root = this._remove(node.key, this._root, this._comparator); + return { + key: node.key, + data: node.data + }; + } + + return null; + }; + /** + * Find without splaying + */ + + + Tree.prototype.findStatic = function (key) { + var current = this._root; + var compare = this._comparator; + + while (current) { + var cmp = compare(key, current.key); + if (cmp === 0) return current;else if (cmp < 0) current = current.left;else current = current.right; + } + + return null; + }; + + Tree.prototype.find = function (key) { + if (this._root) { + this._root = splay(key, this._root, this._comparator); + if (this._comparator(key, this._root.key) !== 0) return null; + } + + return this._root; + }; + + Tree.prototype.contains = function (key) { + var current = this._root; + var compare = this._comparator; + + while (current) { + var cmp = compare(key, current.key); + if (cmp === 0) return true;else if (cmp < 0) current = current.left;else current = current.right; + } + + return false; + }; + + Tree.prototype.forEach = function (visitor, ctx) { + var current = this._root; + var Q = []; + /* Initialize stack s */ + + var done = false; + + while (!done) { + if (current !== null) { + Q.push(current); + current = current.left; + } else { + if (Q.length !== 0) { + current = Q.pop(); + visitor.call(ctx, current); + current = current.right; + } else done = true; + } + } + + return this; + }; + /** + * Walk key range from `low` to `high`. Stops if `fn` returns a value. + */ + + + Tree.prototype.range = function (low, high, fn, ctx) { + var Q = []; + var compare = this._comparator; + var node = this._root; + var cmp; + + while (Q.length !== 0 || node) { + if (node) { + Q.push(node); + node = node.left; + } else { + node = Q.pop(); + cmp = compare(node.key, high); + + if (cmp > 0) { + break; + } else if (compare(node.key, low) >= 0) { + if (fn.call(ctx, node)) return this; // stop if smth is returned + } + + node = node.right; + } + } + + return this; + }; + /** + * Returns array of keys + */ + + + Tree.prototype.keys = function () { + var keys = []; + this.forEach(function (_a) { + var key = _a.key; + return keys.push(key); + }); + return keys; + }; + /** + * Returns array of all the data in the nodes + */ + + + Tree.prototype.values = function () { + var values = []; + this.forEach(function (_a) { + var data = _a.data; + return values.push(data); + }); + return values; + }; + + Tree.prototype.min = function () { + if (this._root) return this.minNode(this._root).key; + return null; + }; + + Tree.prototype.max = function () { + if (this._root) return this.maxNode(this._root).key; + return null; + }; + + Tree.prototype.minNode = function (t) { + if (t === void 0) { + t = this._root; + } + + if (t) while (t.left) { + t = t.left; + } + return t; + }; + + Tree.prototype.maxNode = function (t) { + if (t === void 0) { + t = this._root; + } + + if (t) while (t.right) { + t = t.right; + } + return t; + }; + /** + * Returns node at given index + */ + + + Tree.prototype.at = function (index) { + var current = this._root; + var done = false; + var i = 0; + var Q = []; + + while (!done) { + if (current) { + Q.push(current); + current = current.left; + } else { + if (Q.length > 0) { + current = Q.pop(); + if (i === index) return current; + i++; + current = current.right; + } else done = true; + } + } + + return null; + }; + + Tree.prototype.next = function (d) { + var root = this._root; + var successor = null; + + if (d.right) { + successor = d.right; + + while (successor.left) { + successor = successor.left; + } + + return successor; + } + + var comparator = this._comparator; + + while (root) { + var cmp = comparator(d.key, root.key); + if (cmp === 0) break;else if (cmp < 0) { + successor = root; + root = root.left; + } else root = root.right; + } + + return successor; + }; + + Tree.prototype.prev = function (d) { + var root = this._root; + var predecessor = null; + + if (d.left !== null) { + predecessor = d.left; + + while (predecessor.right) { + predecessor = predecessor.right; + } + + return predecessor; + } + + var comparator = this._comparator; + + while (root) { + var cmp = comparator(d.key, root.key); + if (cmp === 0) break;else if (cmp < 0) root = root.left;else { + predecessor = root; + root = root.right; + } + } + + return predecessor; + }; + + Tree.prototype.clear = function () { + this._root = null; + this._size = 0; + return this; + }; + + Tree.prototype.toList = function () { + return toList(this._root); + }; + /** + * Bulk-load items. Both array have to be same size + */ + + + Tree.prototype.load = function (keys, values, presort) { + if (values === void 0) { + values = []; + } + + if (presort === void 0) { + presort = false; + } + + var size = keys.length; + var comparator = this._comparator; // sort if needed + + if (presort) sort(keys, values, 0, size - 1, comparator); + + if (this._root === null) { + // empty tree + this._root = loadRecursive(keys, values, 0, size); + this._size = size; + } else { + // that re-builds the whole tree from two in-order traversals + var mergedList = mergeLists(this.toList(), createList(keys, values), comparator); + size = this._size + size; + this._root = sortedListToBST({ + head: mergedList + }, 0, size); + } + + return this; + }; + + Tree.prototype.isEmpty = function () { + return this._root === null; + }; + + Object.defineProperty(Tree.prototype, "size", { + get: function get() { + return this._size; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Tree.prototype, "root", { + get: function get() { + return this._root; + }, + enumerable: true, + configurable: true + }); + + Tree.prototype.toString = function (printNode) { + if (printNode === void 0) { + printNode = function printNode(n) { + return String(n.key); + }; + } + + var out = []; + printRow(this._root, '', true, function (v) { + return out.push(v); + }, printNode); + return out.join(''); + }; + + Tree.prototype.update = function (key, newKey, newData) { + var comparator = this._comparator; + + var _a = split(key, this._root, comparator), + left = _a.left, + right = _a.right; + + if (comparator(key, newKey) < 0) { + right = insert(newKey, newData, right, comparator); + } else { + left = insert(newKey, newData, left, comparator); + } + + this._root = merge$3(left, right, comparator); + }; + + Tree.prototype.split = function (key) { + return split(key, this._root, this._comparator); + }; + + return Tree; + }(); + + function loadRecursive(keys, values, start, end) { + var size = end - start; + + if (size > 0) { + var middle = start + Math.floor(size / 2); + var key = keys[middle]; + var data = values[middle]; + var node = new Node(key, data); + node.left = loadRecursive(keys, values, start, middle); + node.right = loadRecursive(keys, values, middle + 1, end); + return node; + } + + return null; + } + + function createList(keys, values) { + var head = new Node(null, null); + var p = head; + + for (var i = 0; i < keys.length; i++) { + p = p.next = new Node(keys[i], values[i]); + } + + p.next = null; + return head.next; + } + + function toList(root) { + var current = root; + var Q = []; + var done = false; + var head = new Node(null, null); + var p = head; + + while (!done) { + if (current) { + Q.push(current); + current = current.left; + } else { + if (Q.length > 0) { + current = p = p.next = Q.pop(); + current = current.right; + } else done = true; + } + } + + p.next = null; // that'll work even if the tree was empty + + return head.next; + } + + function sortedListToBST(list, start, end) { + var size = end - start; + + if (size > 0) { + var middle = start + Math.floor(size / 2); + var left = sortedListToBST(list, start, middle); + var root = list.head; + root.left = left; + list.head = list.head.next; + root.right = sortedListToBST(list, middle + 1, end); + return root; + } + + return null; + } + + function mergeLists(l1, l2, compare) { + var head = new Node(null, null); // dummy + + var p = head; + var p1 = l1; + var p2 = l2; + + while (p1 !== null && p2 !== null) { + if (compare(p1.key, p2.key) < 0) { + p.next = p1; + p1 = p1.next; + } else { + p.next = p2; + p2 = p2.next; + } + + p = p.next; + } + + if (p1 !== null) { + p.next = p1; + } else if (p2 !== null) { + p.next = p2; + } + + return head.next; + } + + function sort(keys, values, left, right, compare) { + if (left >= right) return; + var pivot = keys[left + right >> 1]; + var i = left - 1; + var j = right + 1; + + while (true) { + do { + i++; + } while (compare(keys[i], pivot) < 0); + + do { + j--; + } while (compare(keys[j], pivot) > 0); + + if (i >= j) break; + var tmp = keys[i]; + keys[i] = keys[j]; + keys[j] = tmp; + tmp = values[i]; + values[i] = values[j]; + values[j] = tmp; + } + + sort(keys, values, left, j, compare); + sort(keys, values, j + 1, right, compare); + } + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + /** + * A bounding box has the format: + * + * { ll: { x: xmin, y: ymin }, ur: { x: xmax, y: ymax } } + * + */ + + + var isInBbox = function isInBbox(bbox, point) { + return bbox.ll.x <= point.x && point.x <= bbox.ur.x && bbox.ll.y <= point.y && point.y <= bbox.ur.y; + }; + /* Returns either null, or a bbox (aka an ordered pair of points) + * If there is only one point of overlap, a bbox with identical points + * will be returned */ + + + var getBboxOverlap = function getBboxOverlap(b1, b2) { + // check if the bboxes overlap at all + 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 + + var lowerX = b1.ll.x < b2.ll.x ? b2.ll.x : b1.ll.x; + var upperX = b1.ur.x < b2.ur.x ? b1.ur.x : b2.ur.x; // find the middle two Y values + + var lowerY = b1.ll.y < b2.ll.y ? b2.ll.y : b1.ll.y; + var upperY = b1.ur.y < b2.ur.y ? b1.ur.y : b2.ur.y; // put those middle values together to get the overlap + + return { + ll: { + x: lowerX, + y: lowerY + }, + ur: { + x: upperX, + y: upperY + } + }; + }; + /* Javascript doesn't do integer math. Everything is + * floating point with percision Number.EPSILON. + * + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON + */ + + + var epsilon = Number.EPSILON; // IE Polyfill + + if (epsilon === undefined) epsilon = Math.pow(2, -52); + var EPSILON_SQ = epsilon * epsilon; + /* FLP comparator */ + + var cmp = function cmp(a, b) { + // check if they're both 0 + if (-epsilon < a && a < epsilon) { + if (-epsilon < b && b < epsilon) { + return 0; + } + } // check if they're flp equal + + + var ab = a - b; + + if (ab * ab < EPSILON_SQ * a * b) { + return 0; + } // normal comparison + + + return a < b ? -1 : 1; + }; + /** + * This class rounds incoming values sufficiently so that + * floating points problems are, for the most part, avoided. + * + * Incoming points are have their x & y values tested against + * all previously seen x & y values. If either is 'too close' + * to a previously seen value, it's value is 'snapped' to the + * previously seen value. + * + * All points should be rounded by this class before being + * stored in any data structures in the rest of this algorithm. + */ + + + var PtRounder = /*#__PURE__*/function () { + function PtRounder() { + _classCallCheck(this, PtRounder); + + this.reset(); + } + + _createClass(PtRounder, [{ + key: "reset", + value: function reset() { + this.xRounder = new CoordRounder(); + this.yRounder = new CoordRounder(); + } + }, { + key: "round", + value: function round(x, y) { + return { + x: this.xRounder.round(x), + y: this.yRounder.round(y) + }; + } + }]); + + return PtRounder; + }(); + + var CoordRounder = /*#__PURE__*/function () { + function CoordRounder() { + _classCallCheck(this, CoordRounder); + + this.tree = new Tree(); // preseed with 0 so we don't end up with values < Number.EPSILON + + this.round(0); + } // Note: this can rounds input values backwards or forwards. + // You might ask, why not restrict this to just rounding + // forwards? Wouldn't that allow left endpoints to always + // remain left endpoints during splitting (never change to + // right). No - it wouldn't, because we snap intersections + // to endpoints (to establish independence from the segment + // angle for t-intersections). + + + _createClass(CoordRounder, [{ + key: "round", + value: function round(coord) { + var node = this.tree.add(coord); + var prevNode = this.tree.prev(node); + + if (prevNode !== null && cmp(node.key, prevNode.key) === 0) { + this.tree.remove(coord); + return prevNode.key; + } + + var nextNode = this.tree.next(node); + + if (nextNode !== null && cmp(node.key, nextNode.key) === 0) { + this.tree.remove(coord); + return nextNode.key; + } + + return coord; + } + }]); + + return CoordRounder; + }(); // singleton available by import + + + var rounder = new PtRounder(); + /* Cross Product of two vectors with first point at origin */ + + var crossProduct = function crossProduct(a, b) { + return a.x * b.y - a.y * b.x; + }; + /* Dot Product of two vectors with first point at origin */ + + + var dotProduct = function dotProduct(a, b) { + return a.x * b.x + a.y * b.y; + }; + /* Comparator for two vectors with same starting point */ + + + var compareVectorAngles = function compareVectorAngles(basePt, endPt1, endPt2) { + var v1 = { + x: endPt1.x - basePt.x, + y: endPt1.y - basePt.y + }; + var v2 = { + x: endPt2.x - basePt.x, + y: endPt2.y - basePt.y + }; + var kross = crossProduct(v1, v2); + return cmp(kross, 0); + }; + + var length = function length(v) { + return Math.sqrt(dotProduct(v, v)); + }; + /* Get the sine of the angle from pShared -> pAngle to pShaed -> pBase */ + + + var sineOfAngle = function sineOfAngle(pShared, pBase, pAngle) { + var vBase = { + x: pBase.x - pShared.x, + y: pBase.y - pShared.y + }; + var vAngle = { + x: pAngle.x - pShared.x, + y: pAngle.y - pShared.y + }; + return crossProduct(vAngle, vBase) / length(vAngle) / length(vBase); + }; + /* Get the cosine of the angle from pShared -> pAngle to pShaed -> pBase */ + + + var cosineOfAngle = function cosineOfAngle(pShared, pBase, pAngle) { + var vBase = { + x: pBase.x - pShared.x, + y: pBase.y - pShared.y + }; + var vAngle = { + x: pAngle.x - pShared.x, + y: pAngle.y - pShared.y + }; + return dotProduct(vAngle, vBase) / length(vAngle) / length(vBase); + }; + /* Get the x coordinate where the given line (defined by a point and vector) + * crosses the horizontal line with the given y coordiante. + * In the case of parrallel lines (including overlapping ones) returns null. */ + + + var horizontalIntersection = function horizontalIntersection(pt, v, y) { + if (v.y === 0) return null; + return { + x: pt.x + v.x / v.y * (y - pt.y), + y: y + }; + }; + /* Get the y coordinate where the given line (defined by a point and vector) + * crosses the vertical line with the given x coordiante. + * In the case of parrallel lines (including overlapping ones) returns null. */ + + + var verticalIntersection = function verticalIntersection(pt, v, x) { + if (v.x === 0) return null; + return { + x: x, + y: pt.y + v.y / v.x * (x - pt.x) + }; + }; + /* Get the intersection of two lines, each defined by a base point and a vector. + * In the case of parrallel lines (including overlapping ones) returns null. */ + + + var intersection = function intersection(pt1, v1, pt2, v2) { + // take some shortcuts for vertical and horizontal lines + // this also ensures we don't calculate an intersection and then discover + // it's actually outside the bounding box of the line + if (v1.x === 0) return verticalIntersection(pt2, v2, pt1.x); + if (v2.x === 0) return verticalIntersection(pt1, v1, pt2.x); + if (v1.y === 0) return horizontalIntersection(pt2, v2, pt1.y); + if (v2.y === 0) return horizontalIntersection(pt1, v1, pt2.y); // General case for non-overlapping segments. + // This algorithm is based on Schneider and Eberly. + // http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf - pg 244 + + var kross = crossProduct(v1, v2); + if (kross == 0) return null; + var ve = { + x: pt2.x - pt1.x, + y: pt2.y - pt1.y + }; + var d1 = crossProduct(ve, v1) / kross; + var d2 = crossProduct(ve, v2) / kross; // take the average of the two calculations to minimize rounding error + + var x1 = pt1.x + d2 * v1.x, + x2 = pt2.x + d1 * v2.x; + var y1 = pt1.y + d2 * v1.y, + y2 = pt2.y + d1 * v2.y; + var x = (x1 + x2) / 2; + var y = (y1 + y2) / 2; + return { + x: x, + y: y + }; + }; + + var SweepEvent = /*#__PURE__*/function () { + _createClass(SweepEvent, null, [{ + key: "compare", + // for ordering sweep events in the sweep event queue + value: function compare(a, b) { + // favor event with a point that the sweep line hits first + var ptCmp = SweepEvent.comparePoints(a.point, b.point); + if (ptCmp !== 0) return ptCmp; // the points are the same, so link them if needed + + if (a.point !== b.point) a.link(b); // favor right events over left + + if (a.isLeft !== b.isLeft) return a.isLeft ? 1 : -1; // we have two matching left or right endpoints + // ordering of this case is the same as for their segments + + return Segment.compare(a.segment, b.segment); + } // for ordering points in sweep line order + + }, { + key: "comparePoints", + value: function comparePoints(aPt, bPt) { + if (aPt.x < bPt.x) return -1; + if (aPt.x > bPt.x) return 1; + if (aPt.y < bPt.y) return -1; + if (aPt.y > bPt.y) return 1; + return 0; + } // Warning: 'point' input will be modified and re-used (for performance) + + }]); + + function SweepEvent(point, isLeft) { + _classCallCheck(this, SweepEvent); + + if (point.events === undefined) point.events = [this];else point.events.push(this); + this.point = point; + this.isLeft = isLeft; // this.segment, this.otherSE set by factory + } + + _createClass(SweepEvent, [{ + key: "link", + value: function link(other) { + if (other.point === this.point) { + throw new Error('Tried to link already linked events'); + } + + var otherEvents = other.point.events; + + for (var i = 0, iMax = otherEvents.length; i < iMax; i++) { + var evt = otherEvents[i]; + this.point.events.push(evt); + evt.point = this.point; + } + + this.checkForConsuming(); + } + /* Do a pass over our linked events and check to see if any pair + * of segments match, and should be consumed. */ + + }, { + key: "checkForConsuming", + value: function checkForConsuming() { + // FIXME: The loops in this method run O(n^2) => no good. + // Maintain little ordered sweep event trees? + // Can we maintaining an ordering that avoids the need + // for the re-sorting with getLeftmostComparator in geom-out? + // Compare each pair of events to see if other events also match + var numEvents = this.point.events.length; + + for (var i = 0; i < numEvents; i++) { + var evt1 = this.point.events[i]; + if (evt1.segment.consumedBy !== undefined) continue; + + for (var j = i + 1; j < numEvents; j++) { + var evt2 = this.point.events[j]; + if (evt2.consumedBy !== undefined) continue; + if (evt1.otherSE.point.events !== evt2.otherSE.point.events) continue; + evt1.segment.consume(evt2.segment); + } + } + } + }, { + key: "getAvailableLinkedEvents", + value: function getAvailableLinkedEvents() { + // point.events is always of length 2 or greater + var events = []; + + for (var i = 0, iMax = this.point.events.length; i < iMax; i++) { + var evt = this.point.events[i]; + + if (evt !== this && !evt.segment.ringOut && evt.segment.isInResult()) { + events.push(evt); + } + } + + return events; + } + /** + * Returns a comparator function for sorting linked events that will + * favor the event that will give us the smallest left-side angle. + * All ring construction starts as low as possible heading to the right, + * so by always turning left as sharp as possible we'll get polygons + * without uncessary loops & holes. + * + * The comparator function has a compute cache such that it avoids + * re-computing already-computed values. + */ + + }, { + key: "getLeftmostComparator", + value: function getLeftmostComparator(baseEvent) { + var _this = this; + + var cache = new Map(); + + var fillCache = function fillCache(linkedEvent) { + var nextEvent = linkedEvent.otherSE; + cache.set(linkedEvent, { + sine: sineOfAngle(_this.point, baseEvent.point, nextEvent.point), + cosine: cosineOfAngle(_this.point, baseEvent.point, nextEvent.point) + }); + }; + + return function (a, b) { + if (!cache.has(a)) fillCache(a); + if (!cache.has(b)) fillCache(b); + + var _cache$get = cache.get(a), + asine = _cache$get.sine, + acosine = _cache$get.cosine; + + var _cache$get2 = cache.get(b), + bsine = _cache$get2.sine, + bcosine = _cache$get2.cosine; // both on or above x-axis + + + if (asine >= 0 && bsine >= 0) { + if (acosine < bcosine) return 1; + if (acosine > bcosine) return -1; + return 0; + } // both below x-axis + + + if (asine < 0 && bsine < 0) { + if (acosine < bcosine) return -1; + if (acosine > bcosine) return 1; + return 0; + } // one above x-axis, one below + + + if (bsine < asine) return -1; + if (bsine > asine) return 1; + return 0; + }; + } + }]); + + return SweepEvent; + }(); // segments and sweep events when all else is identical + + + var segmentId = 0; + + var Segment = /*#__PURE__*/function () { + _createClass(Segment, null, [{ + key: "compare", + + /* This compare() function is for ordering segments in the sweep + * line tree, and does so according to the following criteria: + * + * Consider the vertical line that lies an infinestimal step to the + * right of the right-more of the two left endpoints of the input + * segments. Imagine slowly moving a point up from negative infinity + * in the increasing y direction. Which of the two segments will that + * point intersect first? That segment comes 'before' the other one. + * + * If neither segment would be intersected by such a line, (if one + * or more of the segments are vertical) then the line to be considered + * is directly on the right-more of the two left inputs. + */ + value: function compare(a, b) { + var alx = a.leftSE.point.x; + var blx = b.leftSE.point.x; + var arx = a.rightSE.point.x; + var brx = b.rightSE.point.x; // check if they're even in the same vertical plane + + if (brx < alx) return 1; + if (arx < blx) return -1; + var aly = a.leftSE.point.y; + var bly = b.leftSE.point.y; + var ary = a.rightSE.point.y; + var bry = b.rightSE.point.y; // is left endpoint of segment B the right-more? + + if (alx < blx) { + // are the two segments in the same horizontal plane? + if (bly < aly && bly < ary) return 1; + if (bly > aly && bly > ary) return -1; // is the B left endpoint colinear to segment A? + + var aCmpBLeft = a.comparePoint(b.leftSE.point); + if (aCmpBLeft < 0) return 1; + if (aCmpBLeft > 0) return -1; // is the A right endpoint colinear to segment B ? + + var bCmpARight = b.comparePoint(a.rightSE.point); + if (bCmpARight !== 0) return bCmpARight; // colinear segments, consider the one with left-more + // left endpoint to be first (arbitrary?) + + return -1; + } // is left endpoint of segment A the right-more? + + + if (alx > blx) { + if (aly < bly && aly < bry) return -1; + if (aly > bly && aly > bry) return 1; // is the A left endpoint colinear to segment B? + + var bCmpALeft = b.comparePoint(a.leftSE.point); + if (bCmpALeft !== 0) return bCmpALeft; // is the B right endpoint colinear to segment A? + + var aCmpBRight = a.comparePoint(b.rightSE.point); + if (aCmpBRight < 0) return 1; + if (aCmpBRight > 0) return -1; // colinear segments, consider the one with left-more + // left endpoint to be first (arbitrary?) + + return 1; + } // if we get here, the two left endpoints are in the same + // vertical plane, ie alx === blx + // consider the lower left-endpoint to come first + + + if (aly < bly) return -1; + if (aly > bly) return 1; // left endpoints are identical + // check for colinearity by using the left-more right endpoint + // is the A right endpoint more left-more? + + if (arx < brx) { + var _bCmpARight = b.comparePoint(a.rightSE.point); + + if (_bCmpARight !== 0) return _bCmpARight; + } // is the B right endpoint more left-more? + + + if (arx > brx) { + var _aCmpBRight = a.comparePoint(b.rightSE.point); + + if (_aCmpBRight < 0) return 1; + if (_aCmpBRight > 0) return -1; + } + + if (arx !== brx) { + // are these two [almost] vertical segments with opposite orientation? + // if so, the one with the lower right endpoint comes first + var ay = ary - aly; + var ax = arx - alx; + var by = bry - bly; + var bx = brx - blx; + if (ay > ax && by < bx) return 1; + if (ay < ax && by > bx) return -1; + } // we have colinear segments with matching orientation + // consider the one with more left-more right endpoint to be first + + + if (arx > brx) return 1; + if (arx < brx) return -1; // if we get here, two two right endpoints are in the same + // vertical plane, ie arx === brx + // consider the lower right-endpoint to come first + + if (ary < bry) return -1; + if (ary > bry) return 1; // right endpoints identical as well, so the segments are idential + // fall back on creation order as consistent tie-breaker + + if (a.id < b.id) return -1; + if (a.id > b.id) return 1; // identical segment, ie a === b + + return 0; + } + /* Warning: a reference to ringWindings input will be stored, + * and possibly will be later modified */ + + }]); + + function Segment(leftSE, rightSE, rings, windings) { + _classCallCheck(this, Segment); + + this.id = ++segmentId; + this.leftSE = leftSE; + leftSE.segment = this; + leftSE.otherSE = rightSE; + this.rightSE = rightSE; + rightSE.segment = this; + rightSE.otherSE = leftSE; + this.rings = rings; + this.windings = windings; // left unset for performance, set later in algorithm + // this.ringOut, this.consumedBy, this.prev + } + + _createClass(Segment, [{ + key: "replaceRightSE", + + /* When a segment is split, the rightSE is replaced with a new sweep event */ + value: function replaceRightSE(newRightSE) { + this.rightSE = newRightSE; + this.rightSE.segment = this; + this.rightSE.otherSE = this.leftSE; + this.leftSE.otherSE = this.rightSE; + } + }, { + key: "bbox", + value: function bbox() { + var y1 = this.leftSE.point.y; + var y2 = this.rightSE.point.y; + return { + ll: { + x: this.leftSE.point.x, + y: y1 < y2 ? y1 : y2 + }, + ur: { + x: this.rightSE.point.x, + y: y1 > y2 ? y1 : y2 + } + }; + } + /* A vector from the left point to the right */ + + }, { + key: "vector", + value: function vector() { + return { + x: this.rightSE.point.x - this.leftSE.point.x, + y: this.rightSE.point.y - this.leftSE.point.y + }; + } + }, { + key: "isAnEndpoint", + value: function isAnEndpoint(pt) { + 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; + } + /* Compare this segment with a point. + * + * A point P is considered to be colinear to a segment if there + * exists a distance D such that if we travel along the segment + * from one * endpoint towards the other a distance D, we find + * ourselves at point P. + * + * Return value indicates: + * + * 1: point lies above the segment (to the left of vertical) + * 0: point is colinear to segment + * -1: point lies below the segment (to the right of vertical) + */ + + }, { + key: "comparePoint", + value: function comparePoint(point) { + if (this.isAnEndpoint(point)) return 0; + var lPt = this.leftSE.point; + var rPt = this.rightSE.point; + var v = this.vector(); // Exactly vertical segments. + + if (lPt.x === rPt.x) { + if (point.x === lPt.x) return 0; + return point.x < lPt.x ? 1 : -1; + } // Nearly vertical segments with an intersection. + // Check to see where a point on the line with matching Y coordinate is. + + + var yDist = (point.y - lPt.y) / v.y; + var xFromYDist = lPt.x + yDist * v.x; + if (point.x === xFromYDist) return 0; // General case. + // Check to see where a point on the line with matching X coordinate is. + + var xDist = (point.x - lPt.x) / v.x; + var yFromXDist = lPt.y + xDist * v.y; + if (point.y === yFromXDist) return 0; + return point.y < yFromXDist ? -1 : 1; + } + /** + * Given another segment, returns the first non-trivial intersection + * between the two segments (in terms of sweep line ordering), if it exists. + * + * A 'non-trivial' intersection is one that will cause one or both of the + * segments to be split(). As such, 'trivial' vs. 'non-trivial' intersection: + * + * * endpoint of segA with endpoint of segB --> trivial + * * endpoint of segA with point along segB --> non-trivial + * * endpoint of segB with point along segA --> non-trivial + * * point along segA with point along segB --> non-trivial + * + * If no non-trivial intersection exists, return null + * Else, return null. + */ + + }, { + key: "getIntersection", + value: function getIntersection(other) { + // If bboxes don't overlap, there can't be any intersections + var tBbox = this.bbox(); + var oBbox = other.bbox(); + var bboxOverlap = getBboxOverlap(tBbox, oBbox); + if (bboxOverlap === null) return null; // We first check to see if the endpoints can be considered intersections. + // This will 'snap' intersections to endpoints if possible, and will + // handle cases of colinearity. + + var tlp = this.leftSE.point; + var trp = this.rightSE.point; + var olp = other.leftSE.point; + var orp = other.rightSE.point; // does each endpoint touch the other segment? + // note that we restrict the 'touching' definition to only allow segments + // to touch endpoints that lie forward from where we are in the sweep line pass + + var touchesOtherLSE = isInBbox(tBbox, olp) && this.comparePoint(olp) === 0; + var touchesThisLSE = isInBbox(oBbox, tlp) && other.comparePoint(tlp) === 0; + var touchesOtherRSE = isInBbox(tBbox, orp) && this.comparePoint(orp) === 0; + var touchesThisRSE = isInBbox(oBbox, trp) && other.comparePoint(trp) === 0; // do left endpoints match? + + if (touchesThisLSE && touchesOtherLSE) { + // these two cases are for colinear segments with matching left + // endpoints, and one segment being longer than the other + if (touchesThisRSE && !touchesOtherRSE) return trp; + if (!touchesThisRSE && touchesOtherRSE) return orp; // either the two segments match exactly (two trival intersections) + // or just on their left endpoint (one trivial intersection + + return null; + } // does this left endpoint matches (other doesn't) + + + if (touchesThisLSE) { + // check for segments that just intersect on opposing endpoints + if (touchesOtherRSE) { + if (tlp.x === orp.x && tlp.y === orp.y) return null; + } // t-intersection on left endpoint + + + return tlp; + } // does other left endpoint matches (this doesn't) + + + if (touchesOtherLSE) { + // check for segments that just intersect on opposing endpoints + if (touchesThisRSE) { + if (trp.x === olp.x && trp.y === olp.y) return null; + } // t-intersection on left endpoint + + + return olp; + } // trivial intersection on right endpoints + + + if (touchesThisRSE && touchesOtherRSE) return null; // t-intersections on just one right endpoint + + if (touchesThisRSE) return trp; + if (touchesOtherRSE) return orp; // None of our endpoints intersect. Look for a general intersection between + // infinite lines laid over the segments + + var pt = intersection(tlp, this.vector(), olp, other.vector()); // are the segments parrallel? Note that if they were colinear with overlap, + // they would have an endpoint intersection and that case was already handled above + + if (pt === null) return null; // is the intersection found between the lines not on the segments? + + if (!isInBbox(bboxOverlap, pt)) return null; // round the the computed point if needed + + return rounder.round(pt.x, pt.y); + } + /** + * Split the given segment into multiple segments on the given points. + * * Each existing segment will retain its leftSE and a new rightSE will be + * generated for it. + * * A new segment will be generated which will adopt the original segment's + * rightSE, and a new leftSE will be generated for it. + * * If there are more than two points given to split on, new segments + * in the middle will be generated with new leftSE and rightSE's. + * * An array of the newly generated SweepEvents will be returned. + * + * Warning: input array of points is modified + */ + + }, { + key: "split", + value: function split(point) { + var newEvents = []; + var alreadyLinked = point.events !== undefined; + var newLeftSE = new SweepEvent(point, true); + var newRightSE = new SweepEvent(point, false); + var oldRightSE = this.rightSE; + this.replaceRightSE(newRightSE); + newEvents.push(newRightSE); + newEvents.push(newLeftSE); + var newSeg = new Segment(newLeftSE, oldRightSE, this.rings.slice(), this.windings.slice()); // when splitting a nearly vertical downward-facing segment, + // sometimes one of the resulting new segments is vertical, in which + // case its left and right events may need to be swapped + + if (SweepEvent.comparePoints(newSeg.leftSE.point, newSeg.rightSE.point) > 0) { + newSeg.swapEvents(); + } + + if (SweepEvent.comparePoints(this.leftSE.point, this.rightSE.point) > 0) { + this.swapEvents(); + } // in the point we just used to create new sweep events with was already + // linked to other events, we need to check if either of the affected + // segments should be consumed + + + if (alreadyLinked) { + newLeftSE.checkForConsuming(); + newRightSE.checkForConsuming(); + } + + return newEvents; + } + /* Swap which event is left and right */ + + }, { + key: "swapEvents", + value: function swapEvents() { + var tmpEvt = this.rightSE; + this.rightSE = this.leftSE; + this.leftSE = tmpEvt; + this.leftSE.isLeft = true; + this.rightSE.isLeft = false; + + for (var i = 0, iMax = this.windings.length; i < iMax; i++) { + this.windings[i] *= -1; + } + } + /* Consume another segment. We take their rings under our wing + * and mark them as consumed. Use for perfectly overlapping segments */ + + }, { + key: "consume", + value: function consume(other) { + var consumer = this; + var consumee = other; + + while (consumer.consumedBy) { + consumer = consumer.consumedBy; + } + + while (consumee.consumedBy) { + consumee = consumee.consumedBy; + } + + var cmp = Segment.compare(consumer, consumee); + if (cmp === 0) return; // already consumed + // the winner of the consumption is the earlier segment + // according to sweep line ordering + + if (cmp > 0) { + var tmp = consumer; + consumer = consumee; + consumee = tmp; + } // make sure a segment doesn't consume it's prev + + + if (consumer.prev === consumee) { + var _tmp = consumer; + consumer = consumee; + consumee = _tmp; + } + + for (var i = 0, iMax = consumee.rings.length; i < iMax; i++) { + var ring = consumee.rings[i]; + var winding = consumee.windings[i]; + var index = consumer.rings.indexOf(ring); + + if (index === -1) { + consumer.rings.push(ring); + consumer.windings.push(winding); + } else consumer.windings[index] += winding; + } + + consumee.rings = null; + consumee.windings = null; + consumee.consumedBy = consumer; // mark sweep events consumed as to maintain ordering in sweep event queue + + consumee.leftSE.consumedBy = consumer.leftSE; + consumee.rightSE.consumedBy = consumer.rightSE; + } + /* The first segment previous segment chain that is in the result */ + + }, { + key: "prevInResult", + value: function prevInResult() { + if (this._prevInResult !== undefined) return this._prevInResult; + if (!this.prev) this._prevInResult = null;else if (this.prev.isInResult()) this._prevInResult = this.prev;else this._prevInResult = this.prev.prevInResult(); + return this._prevInResult; + } + }, { + key: "beforeState", + value: function beforeState() { + if (this._beforeState !== undefined) return this._beforeState; + if (!this.prev) this._beforeState = { + rings: [], + windings: [], + multiPolys: [] + };else { + var seg = this.prev.consumedBy || this.prev; + this._beforeState = seg.afterState(); + } + return this._beforeState; + } + }, { + key: "afterState", + value: function afterState() { + if (this._afterState !== undefined) return this._afterState; + var beforeState = this.beforeState(); + this._afterState = { + rings: beforeState.rings.slice(0), + windings: beforeState.windings.slice(0), + multiPolys: [] + }; + var ringsAfter = this._afterState.rings; + var windingsAfter = this._afterState.windings; + var mpsAfter = this._afterState.multiPolys; // calculate ringsAfter, windingsAfter + + for (var i = 0, iMax = this.rings.length; i < iMax; i++) { + var ring = this.rings[i]; + var winding = this.windings[i]; + var index = ringsAfter.indexOf(ring); + + if (index === -1) { + ringsAfter.push(ring); + windingsAfter.push(winding); + } else windingsAfter[index] += winding; + } // calcualte polysAfter + + + var polysAfter = []; + var polysExclude = []; + + for (var _i = 0, _iMax = ringsAfter.length; _i < _iMax; _i++) { + if (windingsAfter[_i] === 0) continue; // non-zero rule + + var _ring = ringsAfter[_i]; + var poly = _ring.poly; + if (polysExclude.indexOf(poly) !== -1) continue; + if (_ring.isExterior) polysAfter.push(poly);else { + if (polysExclude.indexOf(poly) === -1) polysExclude.push(poly); + + var _index = polysAfter.indexOf(_ring.poly); + + if (_index !== -1) polysAfter.splice(_index, 1); + } + } // calculate multiPolysAfter + + + for (var _i2 = 0, _iMax2 = polysAfter.length; _i2 < _iMax2; _i2++) { + var mp = polysAfter[_i2].multiPoly; + if (mpsAfter.indexOf(mp) === -1) mpsAfter.push(mp); + } + + return this._afterState; + } + /* Is this segment part of the final result? */ + + }, { + key: "isInResult", + value: function isInResult() { + // if we've been consumed, we're not in the result + if (this.consumedBy) return false; + if (this._isInResult !== undefined) return this._isInResult; + var mpsBefore = this.beforeState().multiPolys; + var mpsAfter = this.afterState().multiPolys; + + switch (operation.type) { + case 'union': + { + // UNION - included iff: + // * On one side of us there is 0 poly interiors AND + // * On the other side there is 1 or more. + var noBefores = mpsBefore.length === 0; + var noAfters = mpsAfter.length === 0; + this._isInResult = noBefores !== noAfters; + break; + } + + case 'intersection': + { + // INTERSECTION - included iff: + // * on one side of us all multipolys are rep. with poly interiors AND + // * on the other side of us, not all multipolys are repsented + // with poly interiors + var least; + var most; + + if (mpsBefore.length < mpsAfter.length) { + least = mpsBefore.length; + most = mpsAfter.length; + } else { + least = mpsAfter.length; + most = mpsBefore.length; + } + + this._isInResult = most === operation.numMultiPolys && least < most; + break; + } + + case 'xor': + { + // XOR - included iff: + // * the difference between the number of multipolys represented + // with poly interiors on our two sides is an odd number + var diff = Math.abs(mpsBefore.length - mpsAfter.length); + this._isInResult = diff % 2 === 1; + break; + } + + case 'difference': + { + // DIFFERENCE included iff: + // * on exactly one side, we have just the subject + var isJustSubject = function isJustSubject(mps) { + return mps.length === 1 && mps[0].isSubject; + }; + + this._isInResult = isJustSubject(mpsBefore) !== isJustSubject(mpsAfter); + break; + } + + default: + throw new Error("Unrecognized operation type found ".concat(operation.type)); + } + + return this._isInResult; + } + }], [{ + key: "fromRing", + value: function fromRing(pt1, pt2, ring) { + var leftPt, rightPt, winding; // ordering the two points according to sweep line ordering + + var cmpPts = SweepEvent.comparePoints(pt1, pt2); + + if (cmpPts < 0) { + leftPt = pt1; + rightPt = pt2; + winding = 1; + } else if (cmpPts > 0) { + leftPt = pt2; + rightPt = pt1; + winding = -1; + } else throw new Error("Tried to create degenerate segment at [".concat(pt1.x, ", ").concat(pt1.y, "]")); + + var leftSE = new SweepEvent(leftPt, true); + var rightSE = new SweepEvent(rightPt, false); + return new Segment(leftSE, rightSE, [ring], [winding]); + } + }]); + + return Segment; + }(); + + var RingIn = /*#__PURE__*/function () { + function RingIn(geomRing, poly, isExterior) { + _classCallCheck(this, RingIn); + + if (!Array.isArray(geomRing) || geomRing.length === 0) { + throw new Error('Input geometry is not a valid Polygon or MultiPolygon'); + } + + this.poly = poly; + this.isExterior = isExterior; + this.segments = []; + + if (typeof geomRing[0][0] !== 'number' || typeof geomRing[0][1] !== 'number') { + throw new Error('Input geometry is not a valid Polygon or MultiPolygon'); + } + + var firstPoint = rounder.round(geomRing[0][0], geomRing[0][1]); + this.bbox = { + ll: { + x: firstPoint.x, + y: firstPoint.y + }, + ur: { + x: firstPoint.x, + y: firstPoint.y + } + }; + var prevPoint = firstPoint; + + for (var i = 1, iMax = geomRing.length; i < iMax; i++) { + if (typeof geomRing[i][0] !== 'number' || typeof geomRing[i][1] !== 'number') { + throw new Error('Input geometry is not a valid Polygon or MultiPolygon'); + } + + var point = rounder.round(geomRing[i][0], geomRing[i][1]); // skip repeated points + + if (point.x === prevPoint.x && point.y === prevPoint.y) continue; + this.segments.push(Segment.fromRing(prevPoint, point, this)); + if (point.x < this.bbox.ll.x) this.bbox.ll.x = point.x; + if (point.y < this.bbox.ll.y) this.bbox.ll.y = point.y; + if (point.x > this.bbox.ur.x) this.bbox.ur.x = point.x; + if (point.y > this.bbox.ur.y) this.bbox.ur.y = point.y; + prevPoint = point; + } // add segment from last to first if last is not the same as first + + + if (firstPoint.x !== prevPoint.x || firstPoint.y !== prevPoint.y) { + this.segments.push(Segment.fromRing(prevPoint, firstPoint, this)); + } + } + + _createClass(RingIn, [{ + key: "getSweepEvents", + value: function getSweepEvents() { + var sweepEvents = []; + + for (var i = 0, iMax = this.segments.length; i < iMax; i++) { + var segment = this.segments[i]; + sweepEvents.push(segment.leftSE); + sweepEvents.push(segment.rightSE); + } + + return sweepEvents; + } + }]); + + return RingIn; + }(); + + var PolyIn = /*#__PURE__*/function () { + function PolyIn(geomPoly, multiPoly) { + _classCallCheck(this, PolyIn); + + if (!Array.isArray(geomPoly)) { + throw new Error('Input geometry is not a valid Polygon or MultiPolygon'); + } + + this.exteriorRing = new RingIn(geomPoly[0], this, true); // copy by value + + this.bbox = { + ll: { + x: this.exteriorRing.bbox.ll.x, + y: this.exteriorRing.bbox.ll.y + }, + ur: { + x: this.exteriorRing.bbox.ur.x, + y: this.exteriorRing.bbox.ur.y + } + }; + this.interiorRings = []; + + for (var i = 1, iMax = geomPoly.length; i < iMax; i++) { + var ring = new RingIn(geomPoly[i], this, false); + if (ring.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = ring.bbox.ll.x; + if (ring.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = ring.bbox.ll.y; + if (ring.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = ring.bbox.ur.x; + if (ring.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = ring.bbox.ur.y; + this.interiorRings.push(ring); + } + + this.multiPoly = multiPoly; + } + + _createClass(PolyIn, [{ + key: "getSweepEvents", + value: function getSweepEvents() { + var sweepEvents = this.exteriorRing.getSweepEvents(); + + for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) { + var ringSweepEvents = this.interiorRings[i].getSweepEvents(); + + for (var j = 0, jMax = ringSweepEvents.length; j < jMax; j++) { + sweepEvents.push(ringSweepEvents[j]); + } + } + + return sweepEvents; + } + }]); + + return PolyIn; + }(); + + var MultiPolyIn = /*#__PURE__*/function () { + function MultiPolyIn(geom, isSubject) { + _classCallCheck(this, MultiPolyIn); + + if (!Array.isArray(geom)) { + throw new Error('Input geometry is not a valid Polygon or MultiPolygon'); + } + + try { + // if the input looks like a polygon, convert it to a multipolygon + if (typeof geom[0][0][0] === 'number') geom = [geom]; + } catch (ex) {// The input is either malformed or has empty arrays. + // In either case, it will be handled later on. + } + + this.polys = []; + this.bbox = { + ll: { + x: Number.POSITIVE_INFINITY, + y: Number.POSITIVE_INFINITY + }, + ur: { + x: Number.NEGATIVE_INFINITY, + y: Number.NEGATIVE_INFINITY + } + }; + + for (var i = 0, iMax = geom.length; i < iMax; i++) { + var poly = new PolyIn(geom[i], this); + if (poly.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = poly.bbox.ll.x; + if (poly.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = poly.bbox.ll.y; + if (poly.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = poly.bbox.ur.x; + if (poly.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = poly.bbox.ur.y; + this.polys.push(poly); + } + + this.isSubject = isSubject; + } + + _createClass(MultiPolyIn, [{ + key: "getSweepEvents", + value: function getSweepEvents() { + var sweepEvents = []; + + for (var i = 0, iMax = this.polys.length; i < iMax; i++) { + var polySweepEvents = this.polys[i].getSweepEvents(); + + for (var j = 0, jMax = polySweepEvents.length; j < jMax; j++) { + sweepEvents.push(polySweepEvents[j]); + } + } + + return sweepEvents; + } + }]); + + return MultiPolyIn; + }(); + + var RingOut = /*#__PURE__*/function () { + _createClass(RingOut, null, [{ + key: "factory", + + /* Given the segments from the sweep line pass, compute & return a series + * of closed rings from all the segments marked to be part of the result */ + value: function factory(allSegments) { + var ringsOut = []; + + for (var i = 0, iMax = allSegments.length; i < iMax; i++) { + var segment = allSegments[i]; + if (!segment.isInResult() || segment.ringOut) continue; + var prevEvent = null; + var event = segment.leftSE; + var nextEvent = segment.rightSE; + var events = [event]; + var startingPoint = event.point; + var intersectionLEs = []; + /* Walk the chain of linked events to form a closed ring */ + + while (true) { + prevEvent = event; + event = nextEvent; + events.push(event); + /* Is the ring complete? */ + + if (event.point === startingPoint) break; + + while (true) { + var availableLEs = event.getAvailableLinkedEvents(); + /* Did we hit a dead end? This shouldn't happen. Indicates some earlier + * part of the algorithm malfunctioned... please file a bug report. */ + + if (availableLEs.length === 0) { + var firstPt = events[0].point; + var lastPt = events[events.length - 1].point; + 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, "].")); + } + /* Only one way to go, so cotinue on the path */ + + + if (availableLEs.length === 1) { + nextEvent = availableLEs[0].otherSE; + break; + } + /* We must have an intersection. Check for a completed loop */ + + + var indexLE = null; + + for (var j = 0, jMax = intersectionLEs.length; j < jMax; j++) { + if (intersectionLEs[j].point === event.point) { + indexLE = j; + break; + } + } + /* Found a completed loop. Cut that off and make a ring */ + + + if (indexLE !== null) { + var intersectionLE = intersectionLEs.splice(indexLE)[0]; + var ringEvents = events.splice(intersectionLE.index); + ringEvents.unshift(ringEvents[0].otherSE); + ringsOut.push(new RingOut(ringEvents.reverse())); + continue; + } + /* register the intersection */ + + + intersectionLEs.push({ + index: events.length, + point: event.point + }); + /* Choose the left-most option to continue the walk */ + + var comparator = event.getLeftmostComparator(prevEvent); + nextEvent = availableLEs.sort(comparator)[0].otherSE; + break; + } + } + + ringsOut.push(new RingOut(events)); + } + + return ringsOut; + } + }]); + + function RingOut(events) { + _classCallCheck(this, RingOut); + + this.events = events; + + for (var i = 0, iMax = events.length; i < iMax; i++) { + events[i].segment.ringOut = this; + } + + this.poly = null; + } + + _createClass(RingOut, [{ + key: "getGeom", + value: function getGeom() { + // Remove superfluous points (ie extra points along a straight line), + var prevPt = this.events[0].point; + var points = [prevPt]; + + for (var i = 1, iMax = this.events.length - 1; i < iMax; i++) { + var _pt = this.events[i].point; + var _nextPt = this.events[i + 1].point; + if (compareVectorAngles(_pt, prevPt, _nextPt) === 0) continue; + points.push(_pt); + prevPt = _pt; + } // ring was all (within rounding error of angle calc) colinear points + + + if (points.length === 1) return null; // check if the starting point is necessary + + var pt = points[0]; + var nextPt = points[1]; + if (compareVectorAngles(pt, prevPt, nextPt) === 0) points.shift(); + points.push(points[0]); + var step = this.isExteriorRing() ? 1 : -1; + var iStart = this.isExteriorRing() ? 0 : points.length - 1; + var iEnd = this.isExteriorRing() ? points.length : -1; + var orderedPoints = []; + + for (var _i = iStart; _i != iEnd; _i += step) { + orderedPoints.push([points[_i].x, points[_i].y]); + } + + return orderedPoints; + } + }, { + key: "isExteriorRing", + value: function isExteriorRing() { + if (this._isExteriorRing === undefined) { + var enclosing = this.enclosingRing(); + this._isExteriorRing = enclosing ? !enclosing.isExteriorRing() : true; + } + + return this._isExteriorRing; + } + }, { + key: "enclosingRing", + value: function enclosingRing() { + if (this._enclosingRing === undefined) { + this._enclosingRing = this._calcEnclosingRing(); + } + + return this._enclosingRing; + } + /* Returns the ring that encloses this one, if any */ + + }, { + key: "_calcEnclosingRing", + value: function _calcEnclosingRing() { + // start with the ealier sweep line event so that the prevSeg + // chain doesn't lead us inside of a loop of ours + var leftMostEvt = this.events[0]; + + for (var i = 1, iMax = this.events.length; i < iMax; i++) { + var evt = this.events[i]; + if (SweepEvent.compare(leftMostEvt, evt) > 0) leftMostEvt = evt; + } + + var prevSeg = leftMostEvt.segment.prevInResult(); + var prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null; + + while (true) { + // no segment found, thus no ring can enclose us + if (!prevSeg) return null; // no segments below prev segment found, thus the ring of the prev + // segment must loop back around and enclose us + + if (!prevPrevSeg) return prevSeg.ringOut; // if the two segments are of different rings, the ring of the prev + // segment must either loop around us or the ring of the prev prev + // seg, which would make us and the ring of the prev peers + + if (prevPrevSeg.ringOut !== prevSeg.ringOut) { + if (prevPrevSeg.ringOut.enclosingRing() !== prevSeg.ringOut) { + return prevSeg.ringOut; + } else return prevSeg.ringOut.enclosingRing(); + } // two segments are from the same ring, so this was a penisula + // of that ring. iterate downward, keep searching + + + prevSeg = prevPrevSeg.prevInResult(); + prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null; + } + } + }]); + + return RingOut; + }(); + + var PolyOut = /*#__PURE__*/function () { + function PolyOut(exteriorRing) { + _classCallCheck(this, PolyOut); + + this.exteriorRing = exteriorRing; + exteriorRing.poly = this; + this.interiorRings = []; + } + + _createClass(PolyOut, [{ + key: "addInterior", + value: function addInterior(ring) { + this.interiorRings.push(ring); + ring.poly = this; + } + }, { + key: "getGeom", + value: function getGeom() { + var geom = [this.exteriorRing.getGeom()]; // exterior ring was all (within rounding error of angle calc) colinear points + + if (geom[0] === null) return null; + + for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) { + var ringGeom = this.interiorRings[i].getGeom(); // interior ring was all (within rounding error of angle calc) colinear points + + if (ringGeom === null) continue; + geom.push(ringGeom); + } + + return geom; + } + }]); + + return PolyOut; + }(); + + var MultiPolyOut = /*#__PURE__*/function () { + function MultiPolyOut(rings) { + _classCallCheck(this, MultiPolyOut); + + this.rings = rings; + this.polys = this._composePolys(rings); + } + + _createClass(MultiPolyOut, [{ + key: "getGeom", + value: function getGeom() { + var geom = []; + + for (var i = 0, iMax = this.polys.length; i < iMax; i++) { + var polyGeom = this.polys[i].getGeom(); // exterior ring was all (within rounding error of angle calc) colinear points + + if (polyGeom === null) continue; + geom.push(polyGeom); + } + + return geom; + } + }, { + key: "_composePolys", + value: function _composePolys(rings) { + var polys = []; + + for (var i = 0, iMax = rings.length; i < iMax; i++) { + var ring = rings[i]; + if (ring.poly) continue; + if (ring.isExteriorRing()) polys.push(new PolyOut(ring));else { + var enclosingRing = ring.enclosingRing(); + if (!enclosingRing.poly) polys.push(new PolyOut(enclosingRing)); + enclosingRing.poly.addInterior(ring); + } + } + + return polys; + } + }]); + + return MultiPolyOut; + }(); + /** + * NOTE: We must be careful not to change any segments while + * they are in the SplayTree. AFAIK, there's no way to tell + * the tree to rebalance itself - thus before splitting + * a segment that's in the tree, we remove it from the tree, + * do the split, then re-insert it. (Even though splitting a + * segment *shouldn't* change its correct position in the + * sweep line tree, the reality is because of rounding errors, + * it sometimes does.) + */ + + + var SweepLine = /*#__PURE__*/function () { + function SweepLine(queue) { + var comparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Segment.compare; + + _classCallCheck(this, SweepLine); + + this.queue = queue; + this.tree = new Tree(comparator); + this.segments = []; + } + + _createClass(SweepLine, [{ + key: "process", + value: function process(event) { + var segment = event.segment; + var newEvents = []; // if we've already been consumed by another segment, + // clean up our body parts and get out + + if (event.consumedBy) { + if (event.isLeft) this.queue.remove(event.otherSE);else this.tree.remove(segment); + return newEvents; + } + + var node = event.isLeft ? this.tree.insert(segment) : this.tree.find(segment); + 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.'); + var prevNode = node; + var nextNode = node; + var prevSeg = undefined; + var nextSeg = undefined; // skip consumed segments still in tree + + while (prevSeg === undefined) { + prevNode = this.tree.prev(prevNode); + if (prevNode === null) prevSeg = null;else if (prevNode.key.consumedBy === undefined) prevSeg = prevNode.key; + } // skip consumed segments still in tree + + + while (nextSeg === undefined) { + nextNode = this.tree.next(nextNode); + if (nextNode === null) nextSeg = null;else if (nextNode.key.consumedBy === undefined) nextSeg = nextNode.key; + } + + if (event.isLeft) { + // Check for intersections against the previous segment in the sweep line + var prevMySplitter = null; + + if (prevSeg) { + var prevInter = prevSeg.getIntersection(segment); + + if (prevInter !== null) { + if (!segment.isAnEndpoint(prevInter)) prevMySplitter = prevInter; + + if (!prevSeg.isAnEndpoint(prevInter)) { + var newEventsFromSplit = this._splitSafely(prevSeg, prevInter); + + for (var i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) { + newEvents.push(newEventsFromSplit[i]); + } + } + } + } // Check for intersections against the next segment in the sweep line + + + var nextMySplitter = null; + + if (nextSeg) { + var nextInter = nextSeg.getIntersection(segment); + + if (nextInter !== null) { + if (!segment.isAnEndpoint(nextInter)) nextMySplitter = nextInter; + + if (!nextSeg.isAnEndpoint(nextInter)) { + var _newEventsFromSplit = this._splitSafely(nextSeg, nextInter); + + for (var _i = 0, _iMax = _newEventsFromSplit.length; _i < _iMax; _i++) { + newEvents.push(_newEventsFromSplit[_i]); + } + } + } + } // For simplicity, even if we find more than one intersection we only + // spilt on the 'earliest' (sweep-line style) of the intersections. + // The other intersection will be handled in a future process(). + + + if (prevMySplitter !== null || nextMySplitter !== null) { + var mySplitter = null; + if (prevMySplitter === null) mySplitter = nextMySplitter;else if (nextMySplitter === null) mySplitter = prevMySplitter;else { + var cmpSplitters = SweepEvent.comparePoints(prevMySplitter, nextMySplitter); + mySplitter = cmpSplitters <= 0 ? prevMySplitter : nextMySplitter; + } // Rounding errors can cause changes in ordering, + // so remove afected segments and right sweep events before splitting + + this.queue.remove(segment.rightSE); + newEvents.push(segment.rightSE); + + var _newEventsFromSplit2 = segment.split(mySplitter); + + for (var _i2 = 0, _iMax2 = _newEventsFromSplit2.length; _i2 < _iMax2; _i2++) { + newEvents.push(_newEventsFromSplit2[_i2]); + } + } + + if (newEvents.length > 0) { + // We found some intersections, so re-do the current event to + // make sure sweep line ordering is totally consistent for later + // use with the segment 'prev' pointers + this.tree.remove(segment); + newEvents.push(event); + } else { + // done with left event + this.segments.push(segment); + segment.prev = prevSeg; + } + } else { + // event.isRight + // since we're about to be removed from the sweep line, check for + // intersections between our previous and next segments + if (prevSeg && nextSeg) { + var inter = prevSeg.getIntersection(nextSeg); + + if (inter !== null) { + if (!prevSeg.isAnEndpoint(inter)) { + var _newEventsFromSplit3 = this._splitSafely(prevSeg, inter); + + for (var _i3 = 0, _iMax3 = _newEventsFromSplit3.length; _i3 < _iMax3; _i3++) { + newEvents.push(_newEventsFromSplit3[_i3]); + } + } + + if (!nextSeg.isAnEndpoint(inter)) { + var _newEventsFromSplit4 = this._splitSafely(nextSeg, inter); + + for (var _i4 = 0, _iMax4 = _newEventsFromSplit4.length; _i4 < _iMax4; _i4++) { + newEvents.push(_newEventsFromSplit4[_i4]); + } + } + } + } + + this.tree.remove(segment); + } + + return newEvents; + } + /* Safely split a segment that is currently in the datastructures + * IE - a segment other than the one that is currently being processed. */ + + }, { + key: "_splitSafely", + value: function _splitSafely(seg, pt) { + // Rounding errors can cause changes in ordering, + // so remove afected segments and right sweep events before splitting + // removeNode() doesn't work, so have re-find the seg + // https://github.com/w8r/splay-tree/pull/5 + this.tree.remove(seg); + var rightSE = seg.rightSE; + this.queue.remove(rightSE); + var newEvents = seg.split(pt); + newEvents.push(rightSE); // splitting can trigger consumption + + if (seg.consumedBy === undefined) this.tree.insert(seg); + return newEvents; + } + }]); + + return SweepLine; + }(); + + var POLYGON_CLIPPING_MAX_QUEUE_SIZE = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_QUEUE_SIZE || 1000000; + var POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS || 1000000; + + var Operation = /*#__PURE__*/function () { + function Operation() { + _classCallCheck(this, Operation); + } + + _createClass(Operation, [{ + key: "run", + value: function run(type, geom, moreGeoms) { + operation.type = type; + rounder.reset(); + /* Convert inputs to MultiPoly objects */ + + var multipolys = [new MultiPolyIn(geom, true)]; + + for (var i = 0, iMax = moreGeoms.length; i < iMax; i++) { + multipolys.push(new MultiPolyIn(moreGeoms[i], false)); + } + + operation.numMultiPolys = multipolys.length; + /* BBox optimization for difference operation + * If the bbox of a multipolygon that's part of the clipping doesn't + * intersect the bbox of the subject at all, we can just drop that + * multiploygon. */ + + if (operation.type === 'difference') { + // in place removal + var subject = multipolys[0]; + var _i = 1; + + while (_i < multipolys.length) { + if (getBboxOverlap(multipolys[_i].bbox, subject.bbox) !== null) _i++;else multipolys.splice(_i, 1); + } + } + /* BBox optimization for intersection operation + * If we can find any pair of multipolygons whose bbox does not overlap, + * then the result will be empty. */ + + + if (operation.type === 'intersection') { + // TODO: this is O(n^2) in number of polygons. By sorting the bboxes, + // it could be optimized to O(n * ln(n)) + for (var _i2 = 0, _iMax = multipolys.length; _i2 < _iMax; _i2++) { + var mpA = multipolys[_i2]; + + for (var j = _i2 + 1, jMax = multipolys.length; j < jMax; j++) { + if (getBboxOverlap(mpA.bbox, multipolys[j].bbox) === null) return []; + } + } + } + /* Put segment endpoints in a priority queue */ + + + var queue = new Tree(SweepEvent.compare); + + for (var _i3 = 0, _iMax2 = multipolys.length; _i3 < _iMax2; _i3++) { + var sweepEvents = multipolys[_i3].getSweepEvents(); + + for (var _j = 0, _jMax = sweepEvents.length; _j < _jMax; _j++) { + queue.insert(sweepEvents[_j]); + + if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) { + // prevents an infinite loop, an otherwise common manifestation of bugs + throw new Error('Infinite loop when putting segment endpoints in a priority queue ' + '(queue size too big). Please file a bug report.'); + } + } + } + /* Pass the sweep line over those endpoints */ + + + var sweepLine = new SweepLine(queue); + var prevQueueSize = queue.size; + var node = queue.pop(); + + while (node) { + var evt = node.key; + + if (queue.size === prevQueueSize) { + // prevents an infinite loop, an otherwise common manifestation of bugs + var seg = evt.segment; + 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.'); + } + + if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) { + // prevents an infinite loop, an otherwise common manifestation of bugs + throw new Error('Infinite loop when passing sweep line over endpoints ' + '(queue size too big). Please file a bug report.'); + } + + if (sweepLine.segments.length > POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS) { + // prevents an infinite loop, an otherwise common manifestation of bugs + throw new Error('Infinite loop when passing sweep line over endpoints ' + '(too many sweep line segments). Please file a bug report.'); + } + + var newEvents = sweepLine.process(evt); + + for (var _i4 = 0, _iMax3 = newEvents.length; _i4 < _iMax3; _i4++) { + var _evt = newEvents[_i4]; + if (_evt.consumedBy === undefined) queue.insert(_evt); + } + + prevQueueSize = queue.size; + node = queue.pop(); + } // free some memory we don't need anymore + + + rounder.reset(); + /* Collect and compile segments we're keeping into a multipolygon */ + + var ringsOut = RingOut.factory(sweepLine.segments); + var result = new MultiPolyOut(ringsOut); + return result.getGeom(); + } + }]); + + return Operation; + }(); // singleton available by import + + + var operation = new Operation(); + + var union = function union(geom) { + for (var _len = arguments.length, moreGeoms = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + moreGeoms[_key - 1] = arguments[_key]; + } + + return operation.run('union', geom, moreGeoms); + }; + + var intersection$1 = function intersection(geom) { + for (var _len2 = arguments.length, moreGeoms = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + moreGeoms[_key2 - 1] = arguments[_key2]; + } + + return operation.run('intersection', geom, moreGeoms); + }; + + var xor = function xor(geom) { + for (var _len3 = arguments.length, moreGeoms = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { + moreGeoms[_key3 - 1] = arguments[_key3]; + } + + return operation.run('xor', geom, moreGeoms); + }; + + var difference = function difference(subjectGeom) { + for (var _len4 = arguments.length, clippingGeoms = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { + clippingGeoms[_key4 - 1] = arguments[_key4]; + } + + return operation.run('difference', subjectGeom, clippingGeoms); + }; + + var index = { + union: union, + intersection: intersection$1, + xor: xor, + difference: difference + }; + + var geojsonPrecision = {exports: {}}; + + (function () { + function parse(t, coordinatePrecision, extrasPrecision) { + function point(p) { + return p.map(function (e, index) { + if (index < 2) { + return 1 * e.toFixed(coordinatePrecision); + } else { + return 1 * e.toFixed(extrasPrecision); + } + }); + } + + function multi(l) { + return l.map(point); + } + + function poly(p) { + return p.map(multi); + } + + function multiPoly(m) { + return m.map(poly); + } + + function geometry(obj) { + if (!obj) { + return {}; + } + + switch (obj.type) { + case "Point": + obj.coordinates = point(obj.coordinates); + return obj; + + case "LineString": + case "MultiPoint": + obj.coordinates = multi(obj.coordinates); + return obj; + + case "Polygon": + case "MultiLineString": + obj.coordinates = poly(obj.coordinates); + return obj; + + case "MultiPolygon": + obj.coordinates = multiPoly(obj.coordinates); + return obj; + + case "GeometryCollection": + obj.geometries = obj.geometries.map(geometry); + return obj; + + default: + return {}; + } + } + + function feature(obj) { + obj.geometry = geometry(obj.geometry); + return obj; + } + + function featureCollection(f) { + f.features = f.features.map(feature); + return f; + } + + function geometryCollection(g) { + g.geometries = g.geometries.map(geometry); + return g; + } + + if (!t) { + return t; + } + + switch (t.type) { + case "Feature": + return feature(t); + + case "GeometryCollection": + return geometryCollection(t); + + case "FeatureCollection": + return featureCollection(t); + + case "Point": + case "LineString": + case "Polygon": + case "MultiPoint": + case "MultiPolygon": + case "MultiLineString": + return geometry(t); + + default: + return t; + } + } + + geojsonPrecision.exports = parse; + geojsonPrecision.exports.parse = parse; + })(); + + var precision = geojsonPrecision.exports; + + var $$d = _export; + var fails$5 = fails$N; + var toObject = toObject$i; + var toPrimitive = toPrimitive$7; + + var FORCED$3 = fails$5(function () { + return new Date(NaN).toJSON() !== null + || Date.prototype.toJSON.call({ toISOString: function () { return 1; } }) !== 1; + }); + + // `Date.prototype.toJSON` method + // https://tc39.es/ecma262/#sec-date.prototype.tojson + $$d({ target: 'Date', proto: true, forced: FORCED$3 }, { + // eslint-disable-next-line no-unused-vars -- required for `.length` + toJSON: function toJSON(key) { + var O = toObject(this); + var pv = toPrimitive(O); + return typeof pv == 'number' && !isFinite(pv) ? null : O.toISOString(); + } + }); + + var $$c = _export; + + // `URL.prototype.toJSON` method + // https://url.spec.whatwg.org/#dom-url-tojson + $$c({ target: 'URL', proto: true, enumerable: true }, { + toJSON: function toJSON() { + return URL.prototype.toString.call(this); + } + }); + + function isObject$3(obj) { + return _typeof(obj) === 'object' && obj !== null; + } + + function forEach(obj, cb) { + if (Array.isArray(obj)) { + obj.forEach(cb); + } else if (isObject$3(obj)) { + Object.keys(obj).forEach(function (key) { + var val = obj[key]; + cb(val, key); + }); + } + } + + function getTreeDepth(obj) { + var depth = 0; + + if (Array.isArray(obj) || isObject$3(obj)) { + forEach(obj, function (val) { + if (Array.isArray(val) || isObject$3(val)) { + var tmpDepth = getTreeDepth(val); + + if (tmpDepth > depth) { + depth = tmpDepth; + } + } + }); + return depth + 1; + } + + return depth; + } + + function stringify(obj, options) { + options = options || {}; + var indent = JSON.stringify([1], null, get(options, 'indent', 2)).slice(2, -3); + var addMargin = get(options, 'margins', false); + var addArrayMargin = get(options, 'arrayMargins', false); + var addObjectMargin = get(options, 'objectMargins', false); + var maxLength = indent === '' ? Infinity : get(options, 'maxLength', 80); + var maxNesting = get(options, 'maxNesting', Infinity); + return function _stringify(obj, currentIndent, reserved) { + if (obj && typeof obj.toJSON === 'function') { + obj = obj.toJSON(); + } + + var string = JSON.stringify(obj); + + if (string === undefined) { + return string; + } + + var length = maxLength - currentIndent.length - reserved; + var treeDepth = getTreeDepth(obj); + + if (treeDepth <= maxNesting && string.length <= length) { + var prettified = prettify(string, { + addMargin: addMargin, + addArrayMargin: addArrayMargin, + addObjectMargin: addObjectMargin + }); + + if (prettified.length <= length) { + return prettified; + } + } + + if (isObject$3(obj)) { + var nextIndent = currentIndent + indent; + var items = []; + var delimiters; + + var comma = function comma(array, index) { + return index === array.length - 1 ? 0 : 1; + }; + + if (Array.isArray(obj)) { + for (var index = 0; index < obj.length; index++) { + items.push(_stringify(obj[index], nextIndent, comma(obj, index)) || 'null'); + } + + delimiters = '[]'; + } else { + Object.keys(obj).forEach(function (key, index, array) { + var keyPart = JSON.stringify(key) + ': '; + + var value = _stringify(obj[key], nextIndent, keyPart.length + comma(array, index)); + + if (value !== undefined) { + items.push(keyPart + value); + } + }); + delimiters = '{}'; + } + + if (items.length > 0) { + return [delimiters[0], indent + items.join(',\n' + nextIndent), delimiters[1]].join('\n' + currentIndent); + } + } + + return string; + }(obj, '', 0); + } // Note: This regex matches even invalid JSON strings, but since we’re + // working on the output of `JSON.stringify` we know that only valid strings + // are present (unless the user supplied a weird `options.indent` but in + // that case we don’t care since the output would be invalid anyway). + + + var stringOrChar = /("(?:[^\\"]|\\.)*")|[:,\][}{]/g; + + function prettify(string, options) { + options = options || {}; + var tokens = { + '{': '{', + '}': '}', + '[': '[', + ']': ']', + ',': ', ', + ':': ': ' + }; + + if (options.addMargin || options.addObjectMargin) { + tokens['{'] = '{ '; + tokens['}'] = ' }'; + } + + if (options.addMargin || options.addArrayMargin) { + tokens['['] = '[ '; + tokens[']'] = ' ]'; + } + + return string.replace(stringOrChar, function (match, string) { + return string ? match : tokens[match]; + }); + } + + function get(options, name, defaultValue) { + return name in options ? options[name] : defaultValue; + } + + var jsonStringifyPrettyCompact = stringify; + + var _default = /*#__PURE__*/function () { + // constructor + // + // `fc` Optional FeatureCollection of known features + // + // Optionally pass a GeoJSON FeatureCollection of known features which we can refer to later. + // Each feature must have a filename-like `id`, for example: `something.geojson` + // + // { + // "type": "FeatureCollection" + // "features": [ + // { + // "type": "Feature", + // "id": "philly_metro.geojson", + // "properties": { … }, + // "geometry": { … } + // } + // ] + // } + function _default(fc) { + var _this = this; + + _classCallCheck$1(this, _default); + + // The _cache retains resolved features, so if you ask for the same thing multiple times + // we don't repeat the expensive resolving/clipping operations. + // + // Each feature has a stable identifier that is used as the cache key. + // The identifiers look like: + // - for point locations, the stringified point: e.g. '[8.67039,49.41882]' + // - for geojson locations, the geojson id: e.g. 'de-hamburg.geojson' + // - for countrycoder locations, feature.id property: e.g. 'Q2' (countrycoder uses Wikidata identifiers) + // - for aggregated locationSets, +[include]-[exclude]: e.g '+[Q2]-[Q18,Q27611]' + this._cache = {}; // When strict mode = true, throw on invalid locations or locationSets. + // When strict mode = false, return `null` for invalid locations or locationSets. + + this._strict = true; // process input FeatureCollection + + if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) { + fc.features.forEach(function (feature) { + feature.properties = feature.properties || {}; + var props = feature.properties; // Get `id` from either `id` or `properties` + + var id = feature.id || props.id; + if (!id || !/^\S+\.geojson$/i.test(id)) return; // Ensure `id` exists and is lowercase + + id = id.toLowerCase(); + feature.id = id; + props.id = id; // Ensure `area` property exists + + if (!props.area) { + var area = geojsonArea.geometry(feature.geometry) / 1e6; // m² to km² + + props.area = Number(area.toFixed(2)); + } + + _this._cache[id] = feature; + }); + } // Replace CountryCoder world geometry to be a polygon covering the world. + + + var world = _cloneDeep(feature$1('Q2')); + + world.geometry = { + type: 'Polygon', + coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]] + }; + world.id = 'Q2'; + world.properties.id = 'Q2'; + world.properties.area = geojsonArea.geometry(world.geometry) / 1e6; // m² to km² + + this._cache.Q2 = world; + } // validateLocation + // `location` The location to validate + // + // Pass a `location` value to validate + // + // Returns a result like: + // { + // type: 'point', 'geojson', or 'countrycoder' + // location: the queried location + // id: the stable identifier for the feature + // } + // or `null` if the location is invalid + // + + + _createClass$1(_default, [{ + key: "validateLocation", + value: function validateLocation(location) { + if (Array.isArray(location) && (location.length === 2 || location.length === 3)) { + // [lon, lat] or [lon, lat, radius] point? + var lon = location[0]; + var lat = location[1]; + var radius = location[2]; + + if (Number.isFinite(lon) && lon >= -180 && lon <= 180 && Number.isFinite(lat) && lat >= -90 && lat <= 90 && (location.length === 2 || Number.isFinite(radius) && radius > 0)) { + var id = '[' + location.toString() + ']'; + return { + type: 'point', + location: location, + id: id + }; + } + } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) { + // a .geojson filename? + var _id = location.toLowerCase(); + + if (this._cache[_id]) { + return { + type: 'geojson', + location: location, + id: _id + }; + } + } else if (typeof location === 'string' || typeof location === 'number') { + // a country-coder value? + var feature = feature$1(location); + + if (feature) { + // Use wikidata QID as the identifier, since that seems to be the one + // property that everything in CountryCoder is guaranteed to have. + var _id2 = feature.properties.wikidata; + return { + type: 'countrycoder', + location: location, + id: _id2 + }; + } + } + + if (this._strict) { + throw new Error("validateLocation: Invalid location: \"".concat(location, "\".")); + } else { + return null; + } + } // resolveLocation + // `location` The location to resolve + // + // Pass a `location` value to resolve + // + // Returns a result like: + // { + // type: 'point', 'geojson', or 'countrycoder' + // location: the queried location + // id: a stable identifier for the feature + // feature: the resolved GeoJSON feature + // } + // or `null` if the location is invalid + // + + }, { + key: "resolveLocation", + value: function resolveLocation(location) { + var valid = this.validateLocation(location); + if (!valid) return null; + var id = valid.id; // Return a result from cache if we can + + if (this._cache[id]) { + return Object.assign(valid, { + feature: this._cache[id] + }); + } // A [lon,lat] coordinate pair? + + + if (valid.type === 'point') { + var lon = location[0]; + var lat = location[1]; + var radius = location[2] || 25; // km + + var EDGES = 10; + var PRECISION = 3; + var area = Math.PI * radius * radius; + var feature = this._cache[id] = precision({ + type: 'Feature', + id: id, + properties: { + id: id, + area: Number(area.toFixed(2)) + }, + geometry: circleToPolygon([lon, lat], radius * 1000, EDGES) // km to m + + }, PRECISION); + return Object.assign(valid, { + feature: feature + }); // A .geojson filename? + } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') { + var _feature = _cloneDeep(feature$1(id)); + + var props = _feature.properties; // -> This block of code is weird and requires some explanation. <- + // CountryCoder includes higher level features which are made up of members. + // These features don't have their own geometry, but CountryCoder provides an + // `aggregateFeature` method to combine these members into a MultiPolygon. + // In the past, Turf/JSTS/martinez could not handle the aggregated features, + // so we'd iteratively union them all together. (this was slow) + // But now mfogel/polygon-clipping handles these MultiPolygons like a boss. + // This approach also has the benefit of removing all the internal boaders and + // simplifying the regional polygons a lot. + + if (Array.isArray(props.members)) { + var aggregate = aggregateFeature(id); + aggregate.geometry.coordinates = _clip([aggregate], 'UNION').geometry.coordinates; + _feature.geometry = aggregate.geometry; + } // Ensure `area` property exists + + + if (!props.area) { + var _area = geojsonArea.geometry(_feature.geometry) / 1e6; // m² to km² + + + props.area = Number(_area.toFixed(2)); + } // Ensure `id` property exists + + + _feature.id = id; + props.id = id; + this._cache[id] = _feature; + return Object.assign(valid, { + feature: _feature + }); + } + + if (this._strict) { + throw new Error("resolveLocation: Couldn't resolve location \"".concat(location, "\".")); + } else { + return null; + } + } // validateLocationSet + // `locationSet` the locationSet to validate + // + // Pass a locationSet Object to validate like: + // { + // include: [ Array of locations ], + // exclude: [ Array of locations ] + // } + // + // Returns a result like: + // { + // type: 'locationset' + // locationSet: the queried locationSet + // id: the stable identifier for the feature + // } + // or `null` if the locationSet is invalid + // + + }, { + key: "validateLocationSet", + value: function validateLocationSet(locationSet) { + locationSet = locationSet || {}; + var validator = this.validateLocation.bind(this); + var include = (locationSet.include || []).map(validator).filter(Boolean); + var exclude = (locationSet.exclude || []).map(validator).filter(Boolean); + + if (!include.length) { + if (this._strict) { + throw new Error("validateLocationSet: LocationSet includes nothing."); + } else { + // non-strict mode, replace an empty locationSet with one that includes "the world" + locationSet.include = ['Q2']; + include = [{ + type: 'countrycoder', + location: 'Q2', + id: 'Q2' + }]; + } + } // Generate stable identifier + + + include.sort(_sortLocations); + var id = '+[' + include.map(function (d) { + return d.id; + }).join(',') + ']'; + + if (exclude.length) { + exclude.sort(_sortLocations); + id += '-[' + exclude.map(function (d) { + return d.id; + }).join(',') + ']'; + } + + return { + type: 'locationset', + locationSet: locationSet, + id: id + }; + } // resolveLocationSet + // `locationSet` the locationSet to resolve + // + // Pass a locationSet Object to validate like: + // { + // include: [ Array of locations ], + // exclude: [ Array of locations ] + // } + // + // Returns a result like: + // { + // type: 'locationset' + // locationSet: the queried locationSet + // id: the stable identifier for the feature + // feature: the resolved GeoJSON feature + // } + // or `null` if the locationSet is invalid + // + + }, { + key: "resolveLocationSet", + value: function resolveLocationSet(locationSet) { + locationSet = locationSet || {}; + var valid = this.validateLocationSet(locationSet); + if (!valid) return null; + var id = valid.id; // Return a result from cache if we can + + if (this._cache[id]) { + return Object.assign(valid, { + feature: this._cache[id] + }); + } + + var resolver = this.resolveLocation.bind(this); + var includes = (locationSet.include || []).map(resolver).filter(Boolean); + var excludes = (locationSet.exclude || []).map(resolver).filter(Boolean); // Return quickly if it's a single included location.. + + if (includes.length === 1 && excludes.length === 0) { + return Object.assign(valid, { + feature: includes[0].feature + }); + } // Calculate unions + + + var includeGeoJSON = _clip(includes.map(function (d) { + return d.feature; + }), 'UNION'); + + var excludeGeoJSON = _clip(excludes.map(function (d) { + return d.feature; + }), 'UNION'); // Calculate difference, update `area` and return result + + + var resultGeoJSON = excludeGeoJSON ? _clip([includeGeoJSON, excludeGeoJSON], 'DIFFERENCE') : includeGeoJSON; + var area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6; // m² to km² + + resultGeoJSON.id = id; + resultGeoJSON.properties = { + id: id, + area: Number(area.toFixed(2)) + }; + this._cache[id] = resultGeoJSON; + return Object.assign(valid, { + feature: resultGeoJSON + }); + } // strict + // + + }, { + key: "strict", + value: function strict(val) { + if (val === undefined) { + // get + return this._strict; + } else { + // set + this._strict = val; + return this; + } + } // cache + // convenience method to access the internal cache + + }, { + key: "cache", + value: function cache() { + return this._cache; + } // stringify + // convenience method to prettyStringify the given object + + }, { + key: "stringify", + value: function stringify(obj, options) { + return jsonStringifyPrettyCompact(obj, options); + } + }]); + + return _default; + }(); // Wrap the mfogel/polygon-clipping library and return a GeoJSON feature. + + function _clip(features, which) { + if (!Array.isArray(features) || !features.length) return null; + var fn = { + UNION: index.union, + DIFFERENCE: index.difference + }[which]; + var args = features.map(function (feature) { + return feature.geometry.coordinates; + }); + var coords = fn.apply(null, args); + return { + type: 'Feature', + properties: {}, + geometry: { + type: whichType(coords), + coordinates: coords + } + }; // is this a Polygon or a MultiPolygon? + + function whichType(coords) { + var a = Array.isArray(coords); + var b = a && Array.isArray(coords[0]); + var c = b && Array.isArray(coords[0][0]); + var d = c && Array.isArray(coords[0][0][0]); + return d ? 'MultiPolygon' : 'Polygon'; + } + } + + function _cloneDeep(obj) { + return JSON.parse(JSON.stringify(obj)); + } // Sorting the location lists is ok because they end up unioned together. + // This sorting makes it possible to generate a deterministic id. + + + function _sortLocations(a, b) { + var rank = { + countrycoder: 1, + geojson: 2, + point: 3 + }; + var aRank = rank[a.type]; + var bRank = rank[b.type]; + return aRank > bRank ? 1 : aRank < bRank ? -1 : a.id.localeCompare(b.id); + } + + var $$b = _export; + + // `Number.MAX_SAFE_INTEGER` constant + // https://tc39.es/ecma262/#sec-number.max_safe_integer + $$b({ target: 'Number', stat: true }, { + MAX_SAFE_INTEGER: 0x1FFFFFFFFFFFFF + }); + + var aesJs = {exports: {}}; + + (function (module, exports) { + (function (root) { + + function checkInt(value) { + return parseInt(value) === value; + } + + function checkInts(arrayish) { + if (!checkInt(arrayish.length)) { + return false; + } + + for (var i = 0; i < arrayish.length; i++) { + if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) { + return false; + } + } + + return true; + } + + function coerceArray(arg, copy) { + // ArrayBuffer view + if (arg.buffer && arg.name === 'Uint8Array') { + if (copy) { + if (arg.slice) { + arg = arg.slice(); + } else { + arg = Array.prototype.slice.call(arg); + } + } + + return arg; + } // It's an array; check it is a valid representation of a byte + + + if (Array.isArray(arg)) { + if (!checkInts(arg)) { + throw new Error('Array contains invalid value: ' + arg); + } + + return new Uint8Array(arg); + } // Something else, but behaves like an array (maybe a Buffer? Arguments?) + + + if (checkInt(arg.length) && checkInts(arg)) { + return new Uint8Array(arg); + } + + throw new Error('unsupported array-like object'); + } + + function createArray(length) { + return new Uint8Array(length); + } + + function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) { + if (sourceStart != null || sourceEnd != null) { + if (sourceArray.slice) { + sourceArray = sourceArray.slice(sourceStart, sourceEnd); + } else { + sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd); + } + } + + targetArray.set(sourceArray, targetStart); + } + + var convertUtf8 = function () { + function toBytes(text) { + var result = [], + i = 0; + text = encodeURI(text); + + while (i < text.length) { + var c = text.charCodeAt(i++); // if it is a % sign, encode the following 2 bytes as a hex value + + if (c === 37) { + result.push(parseInt(text.substr(i, 2), 16)); + i += 2; // otherwise, just the actual byte + } else { + result.push(c); + } + } + + return coerceArray(result); + } + + function fromBytes(bytes) { + var result = [], + i = 0; + + while (i < bytes.length) { + var c = bytes[i]; + + if (c < 128) { + result.push(String.fromCharCode(c)); + i++; + } else if (c > 191 && c < 224) { + result.push(String.fromCharCode((c & 0x1f) << 6 | bytes[i + 1] & 0x3f)); + i += 2; + } else { + result.push(String.fromCharCode((c & 0x0f) << 12 | (bytes[i + 1] & 0x3f) << 6 | bytes[i + 2] & 0x3f)); + i += 3; + } + } + + return result.join(''); + } + + return { + toBytes: toBytes, + fromBytes: fromBytes + }; + }(); + + var convertHex = function () { + function toBytes(text) { + var result = []; + + for (var i = 0; i < text.length; i += 2) { + result.push(parseInt(text.substr(i, 2), 16)); + } + + return result; + } // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html + + + var Hex = '0123456789abcdef'; + + function fromBytes(bytes) { + var result = []; + + for (var i = 0; i < bytes.length; i++) { + var v = bytes[i]; + result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]); + } + + return result.join(''); + } + + return { + toBytes: toBytes, + fromBytes: fromBytes + }; + }(); // Number of rounds by keysize + + + var numberOfRounds = { + 16: 10, + 24: 12, + 32: 14 + }; // Round constant words + + var rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91]; // S-box and Inverse S-box (S is for Substitution) + + var S = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16]; + var Si = [0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d]; // Transformations for encryption + + var T1 = [0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a]; + var T2 = [0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616]; + var T3 = [0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16]; + var T4 = [0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c]; // Transformations for decryption + + var T5 = [0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742]; + var T6 = [0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857]; + var T7 = [0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8]; + var T8 = [0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0]; // Transformations for decryption key expansion + + var U1 = [0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]; + var U2 = [0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697]; + var U3 = [0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46]; + var U4 = [0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d]; + + function convertToInt32(bytes) { + var result = []; + + for (var i = 0; i < bytes.length; i += 4) { + result.push(bytes[i] << 24 | bytes[i + 1] << 16 | bytes[i + 2] << 8 | bytes[i + 3]); + } + + return result; + } + + var AES = function AES(key) { + if (!(this instanceof AES)) { + throw Error('AES must be instanitated with `new`'); + } + + Object.defineProperty(this, 'key', { + value: coerceArray(key, true) + }); + + this._prepare(); + }; + + AES.prototype._prepare = function () { + var rounds = numberOfRounds[this.key.length]; + + if (rounds == null) { + throw new Error('invalid key size (must be 16, 24 or 32 bytes)'); + } // encryption round keys + + + this._Ke = []; // decryption round keys + + this._Kd = []; + + for (var i = 0; i <= rounds; i++) { + this._Ke.push([0, 0, 0, 0]); + + this._Kd.push([0, 0, 0, 0]); + } + + var roundKeyCount = (rounds + 1) * 4; + var KC = this.key.length / 4; // convert the key into ints + + var tk = convertToInt32(this.key); // copy values into round key arrays + + var index; + + for (var i = 0; i < KC; i++) { + index = i >> 2; + this._Ke[index][i % 4] = tk[i]; + this._Kd[rounds - index][i % 4] = tk[i]; + } // key expansion (fips-197 section 5.2) + + + var rconpointer = 0; + var t = KC, + tt; + + while (t < roundKeyCount) { + tt = tk[KC - 1]; + tk[0] ^= S[tt >> 16 & 0xFF] << 24 ^ S[tt >> 8 & 0xFF] << 16 ^ S[tt & 0xFF] << 8 ^ S[tt >> 24 & 0xFF] ^ rcon[rconpointer] << 24; + rconpointer += 1; // key expansion (for non-256 bit) + + if (KC != 8) { + for (var i = 1; i < KC; i++) { + tk[i] ^= tk[i - 1]; + } // key expansion for 256-bit keys is "slightly different" (fips-197) + + } else { + for (var i = 1; i < KC / 2; i++) { + tk[i] ^= tk[i - 1]; + } + + tt = tk[KC / 2 - 1]; + tk[KC / 2] ^= S[tt & 0xFF] ^ S[tt >> 8 & 0xFF] << 8 ^ S[tt >> 16 & 0xFF] << 16 ^ S[tt >> 24 & 0xFF] << 24; + + for (var i = KC / 2 + 1; i < KC; i++) { + tk[i] ^= tk[i - 1]; + } + } // copy values into round key arrays + + + var i = 0, + r, + c; + + while (i < KC && t < roundKeyCount) { + r = t >> 2; + c = t % 4; + this._Ke[r][c] = tk[i]; + this._Kd[rounds - r][c] = tk[i++]; + t++; + } + } // inverse-cipher-ify the decryption round key (fips-197 section 5.3) + + + for (var r = 1; r < rounds; r++) { + for (var c = 0; c < 4; c++) { + tt = this._Kd[r][c]; + this._Kd[r][c] = U1[tt >> 24 & 0xFF] ^ U2[tt >> 16 & 0xFF] ^ U3[tt >> 8 & 0xFF] ^ U4[tt & 0xFF]; + } + } + }; + + AES.prototype.encrypt = function (plaintext) { + if (plaintext.length != 16) { + throw new Error('invalid plaintext size (must be 16 bytes)'); + } + + var rounds = this._Ke.length - 1; + var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key) + + var t = convertToInt32(plaintext); + + for (var i = 0; i < 4; i++) { + t[i] ^= this._Ke[0][i]; + } // apply round transforms + + + for (var r = 1; r < rounds; r++) { + for (var i = 0; i < 4; i++) { + a[i] = T1[t[i] >> 24 & 0xff] ^ T2[t[(i + 1) % 4] >> 16 & 0xff] ^ T3[t[(i + 2) % 4] >> 8 & 0xff] ^ T4[t[(i + 3) % 4] & 0xff] ^ this._Ke[r][i]; + } + + t = a.slice(); + } // the last round is special + + + var result = createArray(16), + tt; + + for (var i = 0; i < 4; i++) { + tt = this._Ke[rounds][i]; + result[4 * i] = (S[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff; + result[4 * i + 1] = (S[t[(i + 1) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff; + result[4 * i + 2] = (S[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff; + result[4 * i + 3] = (S[t[(i + 3) % 4] & 0xff] ^ tt) & 0xff; + } + + return result; + }; + + AES.prototype.decrypt = function (ciphertext) { + if (ciphertext.length != 16) { + throw new Error('invalid ciphertext size (must be 16 bytes)'); + } + + var rounds = this._Kd.length - 1; + var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key) + + var t = convertToInt32(ciphertext); + + for (var i = 0; i < 4; i++) { + t[i] ^= this._Kd[0][i]; + } // apply round transforms + + + for (var r = 1; r < rounds; r++) { + for (var i = 0; i < 4; i++) { + a[i] = T5[t[i] >> 24 & 0xff] ^ T6[t[(i + 3) % 4] >> 16 & 0xff] ^ T7[t[(i + 2) % 4] >> 8 & 0xff] ^ T8[t[(i + 1) % 4] & 0xff] ^ this._Kd[r][i]; + } + + t = a.slice(); + } // the last round is special + + + var result = createArray(16), + tt; + + for (var i = 0; i < 4; i++) { + tt = this._Kd[rounds][i]; + result[4 * i] = (Si[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff; + result[4 * i + 1] = (Si[t[(i + 3) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff; + result[4 * i + 2] = (Si[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff; + result[4 * i + 3] = (Si[t[(i + 1) % 4] & 0xff] ^ tt) & 0xff; + } + + return result; + }; + /** + * Mode Of Operation - Electonic Codebook (ECB) + */ + + + var ModeOfOperationECB = function ModeOfOperationECB(key) { + if (!(this instanceof ModeOfOperationECB)) { + throw Error('AES must be instanitated with `new`'); + } + + this.description = "Electronic Code Block"; + this.name = "ecb"; + this._aes = new AES(key); + }; + + ModeOfOperationECB.prototype.encrypt = function (plaintext) { + plaintext = coerceArray(plaintext); + + if (plaintext.length % 16 !== 0) { + throw new Error('invalid plaintext size (must be multiple of 16 bytes)'); + } + + var ciphertext = createArray(plaintext.length); + var block = createArray(16); + + for (var i = 0; i < plaintext.length; i += 16) { + copyArray(plaintext, block, 0, i, i + 16); + block = this._aes.encrypt(block); + copyArray(block, ciphertext, i); + } + + return ciphertext; + }; + + ModeOfOperationECB.prototype.decrypt = function (ciphertext) { + ciphertext = coerceArray(ciphertext); + + if (ciphertext.length % 16 !== 0) { + throw new Error('invalid ciphertext size (must be multiple of 16 bytes)'); + } + + var plaintext = createArray(ciphertext.length); + var block = createArray(16); + + for (var i = 0; i < ciphertext.length; i += 16) { + copyArray(ciphertext, block, 0, i, i + 16); + block = this._aes.decrypt(block); + copyArray(block, plaintext, i); + } + + return plaintext; + }; + /** + * Mode Of Operation - Cipher Block Chaining (CBC) + */ + + + var ModeOfOperationCBC = function ModeOfOperationCBC(key, iv) { + if (!(this instanceof ModeOfOperationCBC)) { + throw Error('AES must be instanitated with `new`'); + } + + this.description = "Cipher Block Chaining"; + this.name = "cbc"; + + if (!iv) { + iv = createArray(16); + } else if (iv.length != 16) { + throw new Error('invalid initialation vector size (must be 16 bytes)'); + } + + this._lastCipherblock = coerceArray(iv, true); + this._aes = new AES(key); + }; + + ModeOfOperationCBC.prototype.encrypt = function (plaintext) { + plaintext = coerceArray(plaintext); + + if (plaintext.length % 16 !== 0) { + throw new Error('invalid plaintext size (must be multiple of 16 bytes)'); + } + + var ciphertext = createArray(plaintext.length); + var block = createArray(16); + + for (var i = 0; i < plaintext.length; i += 16) { + copyArray(plaintext, block, 0, i, i + 16); + + for (var j = 0; j < 16; j++) { + block[j] ^= this._lastCipherblock[j]; + } + + this._lastCipherblock = this._aes.encrypt(block); + copyArray(this._lastCipherblock, ciphertext, i); + } + + return ciphertext; + }; + + ModeOfOperationCBC.prototype.decrypt = function (ciphertext) { + ciphertext = coerceArray(ciphertext); + + if (ciphertext.length % 16 !== 0) { + throw new Error('invalid ciphertext size (must be multiple of 16 bytes)'); + } + + var plaintext = createArray(ciphertext.length); + var block = createArray(16); + + for (var i = 0; i < ciphertext.length; i += 16) { + copyArray(ciphertext, block, 0, i, i + 16); + block = this._aes.decrypt(block); + + for (var j = 0; j < 16; j++) { + plaintext[i + j] = block[j] ^ this._lastCipherblock[j]; + } + + copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16); + } + + return plaintext; + }; + /** + * Mode Of Operation - Cipher Feedback (CFB) + */ + + + var ModeOfOperationCFB = function ModeOfOperationCFB(key, iv, segmentSize) { + if (!(this instanceof ModeOfOperationCFB)) { + throw Error('AES must be instanitated with `new`'); + } + + this.description = "Cipher Feedback"; + this.name = "cfb"; + + if (!iv) { + iv = createArray(16); + } else if (iv.length != 16) { + throw new Error('invalid initialation vector size (must be 16 size)'); + } + + if (!segmentSize) { + segmentSize = 1; + } + + this.segmentSize = segmentSize; + this._shiftRegister = coerceArray(iv, true); + this._aes = new AES(key); + }; + + ModeOfOperationCFB.prototype.encrypt = function (plaintext) { + if (plaintext.length % this.segmentSize != 0) { + throw new Error('invalid plaintext size (must be segmentSize bytes)'); + } + + var encrypted = coerceArray(plaintext, true); + var xorSegment; + + for (var i = 0; i < encrypted.length; i += this.segmentSize) { + xorSegment = this._aes.encrypt(this._shiftRegister); + + for (var j = 0; j < this.segmentSize; j++) { + encrypted[i + j] ^= xorSegment[j]; + } // Shift the register + + + copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize); + copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize); + } + + return encrypted; + }; + + ModeOfOperationCFB.prototype.decrypt = function (ciphertext) { + if (ciphertext.length % this.segmentSize != 0) { + throw new Error('invalid ciphertext size (must be segmentSize bytes)'); + } + + var plaintext = coerceArray(ciphertext, true); + var xorSegment; + + for (var i = 0; i < plaintext.length; i += this.segmentSize) { + xorSegment = this._aes.encrypt(this._shiftRegister); + + for (var j = 0; j < this.segmentSize; j++) { + plaintext[i + j] ^= xorSegment[j]; + } // Shift the register + + + copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize); + copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize); + } + + return plaintext; + }; + /** + * Mode Of Operation - Output Feedback (OFB) + */ + + + var ModeOfOperationOFB = function ModeOfOperationOFB(key, iv) { + if (!(this instanceof ModeOfOperationOFB)) { + throw Error('AES must be instanitated with `new`'); + } + + this.description = "Output Feedback"; + this.name = "ofb"; + + if (!iv) { + iv = createArray(16); + } else if (iv.length != 16) { + throw new Error('invalid initialation vector size (must be 16 bytes)'); + } + + this._lastPrecipher = coerceArray(iv, true); + this._lastPrecipherIndex = 16; + this._aes = new AES(key); + }; + + ModeOfOperationOFB.prototype.encrypt = function (plaintext) { + var encrypted = coerceArray(plaintext, true); + + for (var i = 0; i < encrypted.length; i++) { + if (this._lastPrecipherIndex === 16) { + this._lastPrecipher = this._aes.encrypt(this._lastPrecipher); + this._lastPrecipherIndex = 0; + } + + encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++]; + } + + return encrypted; + }; // Decryption is symetric + + + ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt; + /** + * Counter object for CTR common mode of operation + */ + + var Counter = function Counter(initialValue) { + if (!(this instanceof Counter)) { + throw Error('Counter must be instanitated with `new`'); + } // We allow 0, but anything false-ish uses the default 1 + + + if (initialValue !== 0 && !initialValue) { + initialValue = 1; + } + + if (typeof initialValue === 'number') { + this._counter = createArray(16); + this.setValue(initialValue); + } else { + this.setBytes(initialValue); + } + }; + + Counter.prototype.setValue = function (value) { + if (typeof value !== 'number' || parseInt(value) != value) { + throw new Error('invalid counter value (must be an integer)'); + } // We cannot safely handle numbers beyond the safe range for integers + + + if (value > Number.MAX_SAFE_INTEGER) { + throw new Error('integer value out of safe range'); + } + + for (var index = 15; index >= 0; --index) { + this._counter[index] = value % 256; + value = parseInt(value / 256); + } + }; + + Counter.prototype.setBytes = function (bytes) { + bytes = coerceArray(bytes, true); + + if (bytes.length != 16) { + throw new Error('invalid counter bytes size (must be 16 bytes)'); + } + + this._counter = bytes; + }; + + Counter.prototype.increment = function () { + for (var i = 15; i >= 0; i--) { + if (this._counter[i] === 255) { + this._counter[i] = 0; + } else { + this._counter[i]++; + break; + } + } + }; + /** + * Mode Of Operation - Counter (CTR) + */ + + + var ModeOfOperationCTR = function ModeOfOperationCTR(key, counter) { + if (!(this instanceof ModeOfOperationCTR)) { + throw Error('AES must be instanitated with `new`'); + } + + this.description = "Counter"; + this.name = "ctr"; + + if (!(counter instanceof Counter)) { + counter = new Counter(counter); + } + + this._counter = counter; + this._remainingCounter = null; + this._remainingCounterIndex = 16; + this._aes = new AES(key); + }; + + ModeOfOperationCTR.prototype.encrypt = function (plaintext) { + var encrypted = coerceArray(plaintext, true); + + for (var i = 0; i < encrypted.length; i++) { + if (this._remainingCounterIndex === 16) { + this._remainingCounter = this._aes.encrypt(this._counter._counter); + this._remainingCounterIndex = 0; + + this._counter.increment(); + } + + encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++]; + } + + return encrypted; + }; // Decryption is symetric + + + ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt; /////////////////////// + // Padding + // See:https://tools.ietf.org/html/rfc2315 + + function pkcs7pad(data) { + data = coerceArray(data, true); + var padder = 16 - data.length % 16; + var result = createArray(data.length + padder); + copyArray(data, result); + + for (var i = data.length; i < result.length; i++) { + result[i] = padder; + } + + return result; + } + + function pkcs7strip(data) { + data = coerceArray(data, true); + + if (data.length < 16) { + throw new Error('PKCS#7 invalid length'); + } + + var padder = data[data.length - 1]; + + if (padder > 16) { + throw new Error('PKCS#7 padding byte out of range'); + } + + var length = data.length - padder; + + for (var i = 0; i < padder; i++) { + if (data[length + i] !== padder) { + throw new Error('PKCS#7 invalid padding byte'); + } + } + + var result = createArray(length); + copyArray(data, result, 0, 0, length); + return result; + } /////////////////////// + // Exporting + // The block cipher + + + var aesjs = { + AES: AES, + Counter: Counter, + ModeOfOperation: { + ecb: ModeOfOperationECB, + cbc: ModeOfOperationCBC, + cfb: ModeOfOperationCFB, + ofb: ModeOfOperationOFB, + ctr: ModeOfOperationCTR + }, + utils: { + hex: convertHex, + utf8: convertUtf8 + }, + padding: { + pkcs7: { + pad: pkcs7pad, + strip: pkcs7strip + } + }, + _arrayTest: { + coerceArray: coerceArray, + createArray: createArray, + copyArray: copyArray + } + }; // node.js + + { + module.exports = aesjs; // RequireJS/AMD + // http://www.requirejs.org/docs/api.html + // https://github.com/amdjs/amdjs-api/wiki/AMD + } + })(); + })(aesJs); + + var aesjs = aesJs.exports; + + // We can use keys that are 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes). + // To generate a random key: window.crypto.getRandomValues(new Uint8Array(16)); + // This default signing key is built into iD and can be used to mask/unmask sensitive values. + + var DEFAULT_128 = [250, 157, 60, 79, 142, 134, 229, 129, 138, 126, 210, 129, 29, 71, 160, 208]; + function utilAesEncrypt(text, key) { + key = key || DEFAULT_128; + var textBytes = aesjs.utils.utf8.toBytes(text); + var aesCtr = new aesjs.ModeOfOperation.ctr(key); + var encryptedBytes = aesCtr.encrypt(textBytes); + var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes); + return encryptedHex; + } + function utilAesDecrypt(encryptedHex, key) { + key = key || DEFAULT_128; + var encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex); + var aesCtr = new aesjs.ModeOfOperation.ctr(key); + var decryptedBytes = aesCtr.decrypt(encryptedBytes); + var text = aesjs.utils.utf8.fromBytes(decryptedBytes); + return text; + } + + function utilCleanTags(tags) { + var out = {}; + + for (var k in tags) { + if (!k) continue; + var v = tags[k]; + + if (v !== undefined) { + out[k] = cleanValue(k, v); + } + } + + return out; + + function cleanValue(k, v) { + function keepSpaces(k) { + return /_hours|_times|:conditional$/.test(k); + } + + function skip(k) { + return /^(description|note|fixme)$/.test(k); + } + + if (skip(k)) return v; + var cleaned = v.split(';').map(function (s) { + return s.trim(); + }).join(keepSpaces(k) ? '; ' : ';'); // The code below is not intended to validate websites and emails. + // It is only intended to prevent obvious copy-paste errors. (#2323) + // clean website- and email-like tags + + if (k.indexOf('website') !== -1 || k.indexOf('email') !== -1 || cleaned.indexOf('http') === 0) { + cleaned = cleaned.replace(/[\u200B-\u200F\uFEFF]/g, ''); // strip LRM and other zero width chars + } + + return cleaned; + } + } + + var _detected; + + function utilDetect(refresh) { + if (_detected && !refresh) return _detected; + _detected = {}; + var ua = navigator.userAgent; + var m = null; + /* Browser */ + + m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge + + if (m !== null) { + _detected.browser = m[1]; + _detected.version = m[2]; + } + + if (!_detected.browser) { + m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i); // IE11 + + if (m !== null) { + _detected.browser = 'msie'; + _detected.version = m[1]; + } + } + + if (!_detected.browser) { + m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i); // Opera 15+ + + if (m !== null) { + _detected.browser = 'Opera'; + _detected.version = m[2]; + } + } + + if (!_detected.browser) { + m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i); + + if (m !== null) { + _detected.browser = m[1]; + _detected.version = m[2]; + m = ua.match(/version\/([\.\d]+)/i); + if (m !== null) _detected.version = m[1]; + } + } + + if (!_detected.browser) { + _detected.browser = navigator.appName; + _detected.version = navigator.appVersion; + } // keep major.minor version only.. + + + _detected.version = _detected.version.split(/\W/).slice(0, 2).join('.'); // detect other browser capabilities + // Legacy Opera has incomplete svg style support. See #715 + + _detected.opera = _detected.browser.toLowerCase() === 'opera' && parseFloat(_detected.version) < 15; + + if (_detected.browser.toLowerCase() === 'msie') { + _detected.ie = true; + _detected.browser = 'Internet Explorer'; + _detected.support = parseFloat(_detected.version) >= 11; + } else { + _detected.ie = false; + _detected.support = true; + } + + _detected.filedrop = window.FileReader && 'ondrop' in window; + _detected.download = !(_detected.ie || _detected.browser.toLowerCase() === 'edge'); + _detected.cssfilters = !(_detected.ie || _detected.browser.toLowerCase() === 'edge'); + /* Platform */ + + if (/Win/.test(ua)) { + _detected.os = 'win'; + _detected.platform = 'Windows'; + } else if (/Mac/.test(ua)) { + _detected.os = 'mac'; + _detected.platform = 'Macintosh'; + } else if (/X11/.test(ua) || /Linux/.test(ua)) { + _detected.os = 'linux'; + _detected.platform = 'Linux'; + } else { + _detected.os = 'win'; + _detected.platform = 'Unknown'; + } + + _detected.isMobileWebKit = (/\b(iPad|iPhone|iPod)\b/.test(ua) || // HACK: iPadOS 13+ requests desktop sites by default by using a Mac user agent, + // so assume any "mac" with multitouch is actually iOS + navigator.platform === 'MacIntel' && 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 1) && /WebKit/.test(ua) && !/Edge/.test(ua) && !window.MSStream; + /* Locale */ + // An array of locales requested by the browser in priority order. + + _detected.browserLocales = Array.from(new Set( // remove duplicates + [navigator.language].concat(navigator.languages || []).concat([// old property for backwards compatibility + navigator.userLanguage]) // remove any undefined values + .filter(Boolean))); + /* Host */ + + var loc = window.top.location; + var origin = loc.origin; + + if (!origin) { + // for unpatched IE11 + origin = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port : ''); + } + + _detected.host = origin + loc.pathname; + return _detected; + } + + // Like selection.property('value', ...), but avoids no-op value sets, + // which can result in layout/repaint thrashing in some situations. + function utilGetSetValue(selection, value) { + function d3_selection_value(value) { + function valueNull() { + delete this.value; + } + + function valueConstant() { + if (this.value !== value) { + this.value = value; + } + } + + function valueFunction() { + var x = value.apply(this, arguments); + + if (x === null || x === undefined) { + delete this.value; + } else if (this.value !== x) { + this.value = x; + } + } + + return value === null || value === undefined ? valueNull : typeof value === 'function' ? valueFunction : valueConstant; + } + + if (arguments.length === 1) { + return selection.property('value'); + } + + return selection.each(d3_selection_value(value)); + } + + function utilKeybinding(namespace) { + var _keybindings = {}; + + function testBindings(d3_event, isCapturing) { + var didMatch = false; + var bindings = Object.keys(_keybindings).map(function (id) { + return _keybindings[id]; + }); + var i, binding; // Most key shortcuts will accept either lower or uppercase ('h' or 'H'), + // so we don't strictly match on the shift key, but we prioritize + // shifted keybindings first, and fallback to unshifted only if no match. + // (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z') + // priority match shifted keybindings first + + for (i = 0; i < bindings.length; i++) { + binding = bindings[i]; + if (!binding.event.modifiers.shiftKey) continue; // no shift + + if (!!binding.capture !== isCapturing) continue; + + if (matches(d3_event, binding, true)) { + binding.callback(d3_event); + didMatch = true; // match a max of one binding per event + + break; + } + } + + if (didMatch) return; // then unshifted keybindings + + for (i = 0; i < bindings.length; i++) { + binding = bindings[i]; + if (binding.event.modifiers.shiftKey) continue; // shift + + if (!!binding.capture !== isCapturing) continue; + + if (matches(d3_event, binding, false)) { + binding.callback(d3_event); + break; + } + } + + function matches(d3_event, binding, testShift) { + var event = d3_event; + var isMatch = false; + var tryKeyCode = true; // Prefer a match on `KeyboardEvent.key` + + if (event.key !== undefined) { + tryKeyCode = event.key.charCodeAt(0) > 255; // outside ISO-Latin-1 + + isMatch = true; + + if (binding.event.key === undefined) { + isMatch = false; + } else if (Array.isArray(binding.event.key)) { + if (binding.event.key.map(function (s) { + return s.toLowerCase(); + }).indexOf(event.key.toLowerCase()) === -1) { + isMatch = false; + } + } else { + if (event.key.toLowerCase() !== binding.event.key.toLowerCase()) { + isMatch = false; + } + } + } // Fallback match on `KeyboardEvent.keyCode`, can happen if: + // - browser doesn't support `KeyboardEvent.key` + // - `KeyboardEvent.key` is outside ISO-Latin-1 range (cyrillic?) + + + if (!isMatch && tryKeyCode) { + isMatch = event.keyCode === binding.event.keyCode; + } + + if (!isMatch) return false; // test modifier keys + + if (!(event.ctrlKey && event.altKey)) { + // if both are set, assume AltGr and skip it - #4096 + if (event.ctrlKey !== binding.event.modifiers.ctrlKey) return false; + if (event.altKey !== binding.event.modifiers.altKey) return false; + } + + if (event.metaKey !== binding.event.modifiers.metaKey) return false; + if (testShift && event.shiftKey !== binding.event.modifiers.shiftKey) return false; + return true; + } + } + + function capture(d3_event) { + testBindings(d3_event, true); + } + + function bubble(d3_event) { + var tagName = select(d3_event.target).node().tagName; + + if (tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') { + return; + } + + testBindings(d3_event, false); + } + + function keybinding(selection) { + selection = selection || select(document); + selection.on('keydown.capture.' + namespace, capture, true); + selection.on('keydown.bubble.' + namespace, bubble, false); + return keybinding; + } // was: keybinding.off() + + + keybinding.unbind = function (selection) { + _keybindings = []; + selection = selection || select(document); + selection.on('keydown.capture.' + namespace, null); + selection.on('keydown.bubble.' + namespace, null); + return keybinding; + }; + + keybinding.clear = function () { + _keybindings = {}; + return keybinding; + }; // Remove one or more keycode bindings. + + + keybinding.off = function (codes, capture) { + var arr = utilArrayUniq([].concat(codes)); + + for (var i = 0; i < arr.length; i++) { + var id = arr[i] + (capture ? '-capture' : '-bubble'); + delete _keybindings[id]; + } + + return keybinding; + }; // Add one or more keycode bindings. + + + keybinding.on = function (codes, callback, capture) { + if (typeof callback !== 'function') { + return keybinding.off(codes, capture); + } + + var arr = utilArrayUniq([].concat(codes)); + + for (var i = 0; i < arr.length; i++) { + var id = arr[i] + (capture ? '-capture' : '-bubble'); + var binding = { + id: id, + capture: capture, + callback: callback, + event: { + key: undefined, + // preferred + keyCode: 0, + // fallback + modifiers: { + shiftKey: false, + ctrlKey: false, + altKey: false, + metaKey: false + } + } + }; + + if (_keybindings[id]) { + console.warn('warning: duplicate keybinding for "' + id + '"'); // eslint-disable-line no-console + } + + _keybindings[id] = binding; + var matches = arr[i].toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g); + + for (var j = 0; j < matches.length; j++) { + // Normalise matching errors + if (matches[j] === '++') matches[j] = '+'; + + if (matches[j] in utilKeybinding.modifierCodes) { + var prop = utilKeybinding.modifierProperties[utilKeybinding.modifierCodes[matches[j]]]; + binding.event.modifiers[prop] = true; + } else { + binding.event.key = utilKeybinding.keys[matches[j]] || matches[j]; + + if (matches[j] in utilKeybinding.keyCodes) { + binding.event.keyCode = utilKeybinding.keyCodes[matches[j]]; + } + } + } + } + + return keybinding; + }; + + return keybinding; + } + /* + * See https://github.com/keithamus/jwerty + */ + + utilKeybinding.modifierCodes = { + // Shift key, ⇧ + '⇧': 16, + shift: 16, + // CTRL key, on Mac: ⌃ + '⌃': 17, + ctrl: 17, + // ALT key, on Mac: ⌥ (Alt) + '⌥': 18, + alt: 18, + option: 18, + // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super) + '⌘': 91, + meta: 91, + cmd: 91, + 'super': 91, + win: 91 + }; + utilKeybinding.modifierProperties = { + 16: 'shiftKey', + 17: 'ctrlKey', + 18: 'altKey', + 91: 'metaKey' + }; + utilKeybinding.plusKeys = ['plus', 'ffplus', '=', 'ffequals', '≠', '±']; + utilKeybinding.minusKeys = ['_', '-', 'ffminus', 'dash', '–', '—']; + utilKeybinding.keys = { + // Backspace key, on Mac: ⌫ (Backspace) + '⌫': 'Backspace', + backspace: 'Backspace', + // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥ + '⇥': 'Tab', + '⇆': 'Tab', + tab: 'Tab', + // Return key, ↩ + '↩': 'Enter', + '↵': 'Enter', + '⏎': 'Enter', + 'return': 'Enter', + enter: 'Enter', + '⌅': 'Enter', + // Pause/Break key + 'pause': 'Pause', + 'pause-break': 'Pause', + // Caps Lock key, ⇪ + '⇪': 'CapsLock', + caps: 'CapsLock', + 'caps-lock': 'CapsLock', + // Escape key, on Mac: ⎋, on Windows: Esc + '⎋': ['Escape', 'Esc'], + escape: ['Escape', 'Esc'], + esc: ['Escape', 'Esc'], + // Space key + space: [' ', 'Spacebar'], + // Page-Up key, or pgup, on Mac: ↖ + '↖': 'PageUp', + pgup: 'PageUp', + 'page-up': 'PageUp', + // Page-Down key, or pgdown, on Mac: ↘ + '↘': 'PageDown', + pgdown: 'PageDown', + 'page-down': 'PageDown', + // END key, on Mac: ⇟ + '⇟': 'End', + end: 'End', + // HOME key, on Mac: ⇞ + '⇞': 'Home', + home: 'Home', + // Insert key, or ins + ins: 'Insert', + insert: 'Insert', + // Delete key, on Mac: ⌦ (Delete) + '⌦': ['Delete', 'Del'], + del: ['Delete', 'Del'], + 'delete': ['Delete', 'Del'], + // Left Arrow Key, or ← + '←': ['ArrowLeft', 'Left'], + left: ['ArrowLeft', 'Left'], + 'arrow-left': ['ArrowLeft', 'Left'], + // Up Arrow Key, or ↑ + '↑': ['ArrowUp', 'Up'], + up: ['ArrowUp', 'Up'], + 'arrow-up': ['ArrowUp', 'Up'], + // Right Arrow Key, or → + '→': ['ArrowRight', 'Right'], + right: ['ArrowRight', 'Right'], + 'arrow-right': ['ArrowRight', 'Right'], + // Up Arrow Key, or ↓ + '↓': ['ArrowDown', 'Down'], + down: ['ArrowDown', 'Down'], + 'arrow-down': ['ArrowDown', 'Down'], + // odities, stuff for backward compatibility (browsers and code): + // Num-Multiply, or * + '*': ['*', 'Multiply'], + star: ['*', 'Multiply'], + asterisk: ['*', 'Multiply'], + multiply: ['*', 'Multiply'], + // Num-Plus or + + '+': ['+', 'Add'], + 'plus': ['+', 'Add'], + // Num-Subtract, or - + '-': ['-', 'Subtract'], + subtract: ['-', 'Subtract'], + 'dash': ['-', 'Subtract'], + // Semicolon + semicolon: ';', + // = or equals + equals: '=', + // Comma, or , + comma: ',', + // Period, or ., or full-stop + period: '.', + 'full-stop': '.', + // Slash, or /, or forward-slash + slash: '/', + 'forward-slash': '/', + // Tick, or `, or back-quote + tick: '`', + 'back-quote': '`', + // Open bracket, or [ + 'open-bracket': '[', + // Back slash, or \ + 'back-slash': '\\', + // Close backet, or ] + 'close-bracket': ']', + // Apostrophe, or Quote, or ' + quote: '\'', + apostrophe: '\'', + // NUMPAD 0-9 + 'num-0': '0', + 'num-1': '1', + 'num-2': '2', + 'num-3': '3', + 'num-4': '4', + 'num-5': '5', + 'num-6': '6', + 'num-7': '7', + 'num-8': '8', + 'num-9': '9', + // F1-F25 + f1: 'F1', + f2: 'F2', + f3: 'F3', + f4: 'F4', + f5: 'F5', + f6: 'F6', + f7: 'F7', + f8: 'F8', + f9: 'F9', + f10: 'F10', + f11: 'F11', + f12: 'F12', + f13: 'F13', + f14: 'F14', + f15: 'F15', + f16: 'F16', + f17: 'F17', + f18: 'F18', + f19: 'F19', + f20: 'F20', + f21: 'F21', + f22: 'F22', + f23: 'F23', + f24: 'F24', + f25: 'F25' + }; + utilKeybinding.keyCodes = { + // Backspace key, on Mac: ⌫ (Backspace) + '⌫': 8, + backspace: 8, + // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥ + '⇥': 9, + '⇆': 9, + tab: 9, + // Return key, ↩ + '↩': 13, + '↵': 13, + '⏎': 13, + 'return': 13, + enter: 13, + '⌅': 13, + // Pause/Break key + 'pause': 19, + 'pause-break': 19, + // Caps Lock key, ⇪ + '⇪': 20, + caps: 20, + 'caps-lock': 20, + // Escape key, on Mac: ⎋, on Windows: Esc + '⎋': 27, + escape: 27, + esc: 27, + // Space key + space: 32, + // Page-Up key, or pgup, on Mac: ↖ + '↖': 33, + pgup: 33, + 'page-up': 33, + // Page-Down key, or pgdown, on Mac: ↘ + '↘': 34, + pgdown: 34, + 'page-down': 34, + // END key, on Mac: ⇟ + '⇟': 35, + end: 35, + // HOME key, on Mac: ⇞ + '⇞': 36, + home: 36, + // Insert key, or ins + ins: 45, + insert: 45, + // Delete key, on Mac: ⌦ (Delete) + '⌦': 46, + del: 46, + 'delete': 46, + // Left Arrow Key, or ← + '←': 37, + left: 37, + 'arrow-left': 37, + // Up Arrow Key, or ↑ + '↑': 38, + up: 38, + 'arrow-up': 38, + // Right Arrow Key, or → + '→': 39, + right: 39, + 'arrow-right': 39, + // Up Arrow Key, or ↓ + '↓': 40, + down: 40, + 'arrow-down': 40, + // odities, printing characters that come out wrong: + // Firefox Equals + 'ffequals': 61, + // Num-Multiply, or * + '*': 106, + star: 106, + asterisk: 106, + multiply: 106, + // Num-Plus or + + '+': 107, + 'plus': 107, + // Num-Subtract, or - + '-': 109, + subtract: 109, + // Vertical Bar / Pipe + '|': 124, + // Firefox Plus + 'ffplus': 171, + // Firefox Minus + 'ffminus': 173, + // Semicolon + ';': 186, + semicolon: 186, + // = or equals + '=': 187, + 'equals': 187, + // Comma, or , + ',': 188, + comma: 188, + // Dash / Underscore key + 'dash': 189, + // Period, or ., or full-stop + '.': 190, + period: 190, + 'full-stop': 190, + // Slash, or /, or forward-slash + '/': 191, + slash: 191, + 'forward-slash': 191, + // Tick, or `, or back-quote + '`': 192, + tick: 192, + 'back-quote': 192, + // Open bracket, or [ + '[': 219, + 'open-bracket': 219, + // Back slash, or \ + '\\': 220, + 'back-slash': 220, + // Close backet, or ] + ']': 221, + 'close-bracket': 221, + // Apostrophe, or Quote, or ' + '\'': 222, + quote: 222, + apostrophe: 222 + }; // NUMPAD 0-9 + + var i = 95, + n = 0; + + while (++i < 106) { + utilKeybinding.keyCodes['num-' + n] = i; + ++n; + } // 0-9 + + + i = 47; + n = 0; + + while (++i < 58) { + utilKeybinding.keyCodes[n] = i; + ++n; + } // F1-F25 + + + i = 111; + n = 1; + + while (++i < 136) { + utilKeybinding.keyCodes['f' + n] = i; + ++n; + } // a-z + + + i = 64; + + while (++i < 91) { + utilKeybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i; + } + + function utilObjectOmit(obj, omitKeys) { + return Object.keys(obj).reduce(function (result, key) { + if (omitKeys.indexOf(key) === -1) { + result[key] = obj[key]; // keep + } + + return result; + }, {}); + } + + // Copies a variable number of methods from source to target. + function utilRebind(target, source) { + var i = 1, + n = arguments.length, + method; + + while (++i < n) { + target[method = arguments[i]] = d3_rebind(target, source, source[method]); + } + + return target; + } // Method is assumed to be a standard D3 getter-setter: + // If passed with no arguments, gets the value. + // If passed with arguments, sets the value and returns the target. + + function d3_rebind(target, source, method) { + return function () { + var value = method.apply(source, arguments); + return value === source ? target : value; + }; + } + + // A per-domain session mutex backed by a cookie and dead man's + // switch. If the session crashes, the mutex will auto-release + // after 5 seconds. + // This accepts a string and returns an object that complies with utilSessionMutexType + function utilSessionMutex(name) { + var mutex = {}; + var intervalID; + + function renew() { + var expires = new Date(); + expires.setSeconds(expires.getSeconds() + 5); + document.cookie = name + '=1; expires=' + expires.toUTCString() + '; sameSite=strict'; + } + + mutex.lock = function () { + if (intervalID) return true; + var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1'); + if (cookie) return false; + renew(); + intervalID = window.setInterval(renew, 4000); + return true; + }; + + mutex.unlock = function () { + if (!intervalID) return; + document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; sameSite=strict'; + clearInterval(intervalID); + intervalID = null; + }; + + mutex.locked = function () { + return !!intervalID; + }; + + return mutex; + } + + function utilTiler() { + var _size = [256, 256]; + var _scale = 256; + var _tileSize = 256; + var _zoomExtent = [0, 20]; + var _translate = [_size[0] / 2, _size[1] / 2]; + var _margin = 0; + var _skipNullIsland = false; + + function clamp(num, min, max) { + return Math.max(min, Math.min(num, max)); + } + + function nearNullIsland(tile) { + var x = tile[0]; + var y = tile[1]; + var z = tile[2]; + + if (z >= 7) { + var center = Math.pow(2, z - 1); + var width = Math.pow(2, z - 6); + var min = center - width / 2; + var max = center + width / 2 - 1; + return x >= min && x <= max && y >= min && y <= max; + } + + return false; + } + + function tiler() { + var z = geoScaleToZoom(_scale / (2 * Math.PI), _tileSize); + var z0 = clamp(Math.round(z), _zoomExtent[0], _zoomExtent[1]); + var tileMin = 0; + var tileMax = Math.pow(2, z0) - 1; + var log2ts = Math.log(_tileSize) * Math.LOG2E; + var k = Math.pow(2, z - z0 + log2ts); + var origin = [(_translate[0] - _scale / 2) / k, (_translate[1] - _scale / 2) / k]; + var cols = range$1(clamp(Math.floor(-origin[0]) - _margin, tileMin, tileMax + 1), clamp(Math.ceil(_size[0] / k - origin[0]) + _margin, tileMin, tileMax + 1)); + var rows = range$1(clamp(Math.floor(-origin[1]) - _margin, tileMin, tileMax + 1), clamp(Math.ceil(_size[1] / k - origin[1]) + _margin, tileMin, tileMax + 1)); + var tiles = []; + + for (var i = 0; i < rows.length; i++) { + var y = rows[i]; + + for (var j = 0; j < cols.length; j++) { + var x = cols[j]; + + if (i >= _margin && i <= rows.length - _margin && j >= _margin && j <= cols.length - _margin) { + tiles.unshift([x, y, z0]); // tiles in view at beginning + } else { + tiles.push([x, y, z0]); // tiles in margin at the end + } + } + } + + tiles.translate = origin; + tiles.scale = k; + return tiles; + } + /** + * getTiles() returns an array of tiles that cover the map view + */ + + + tiler.getTiles = function (projection) { + var origin = [projection.scale() * Math.PI - projection.translate()[0], projection.scale() * Math.PI - projection.translate()[1]]; + this.size(projection.clipExtent()[1]).scale(projection.scale() * 2 * Math.PI).translate(projection.translate()); + var tiles = tiler(); + var ts = tiles.scale; + return tiles.map(function (tile) { + if (_skipNullIsland && nearNullIsland(tile)) { + return false; + } + + var x = tile[0] * ts - origin[0]; + var y = tile[1] * ts - origin[1]; + return { + id: tile.toString(), + xyz: tile, + extent: geoExtent(projection.invert([x, y + ts]), projection.invert([x + ts, y])) + }; + }).filter(Boolean); + }; + /** + * getGeoJSON() returns a FeatureCollection for debugging tiles + */ + + + tiler.getGeoJSON = function (projection) { + var features = tiler.getTiles(projection).map(function (tile) { + return { + type: 'Feature', + properties: { + id: tile.id, + name: tile.id + }, + geometry: { + type: 'Polygon', + coordinates: [tile.extent.polygon()] + } + }; + }); + return { + type: 'FeatureCollection', + features: features + }; + }; + + tiler.tileSize = function (val) { + if (!arguments.length) return _tileSize; + _tileSize = val; + return tiler; + }; + + tiler.zoomExtent = function (val) { + if (!arguments.length) return _zoomExtent; + _zoomExtent = val; + return tiler; + }; + + tiler.size = function (val) { + if (!arguments.length) return _size; + _size = val; + return tiler; + }; + + tiler.scale = function (val) { + if (!arguments.length) return _scale; + _scale = val; + return tiler; + }; + + tiler.translate = function (val) { + if (!arguments.length) return _translate; + _translate = val; + return tiler; + }; // number to extend the rows/columns beyond those covering the viewport + + + tiler.margin = function (val) { + if (!arguments.length) return _margin; + _margin = +val; + return tiler; + }; + + tiler.skipNullIsland = function (val) { + if (!arguments.length) return _skipNullIsland; + _skipNullIsland = val; + return tiler; + }; + + return tiler; + } + + function utilTriggerEvent(target, type) { + target.each(function () { + var evt = document.createEvent('HTMLEvents'); + evt.initEvent(type, true, true); + this.dispatchEvent(evt); + }); + } + + var _mainLocations = coreLocations(); // singleton + // `coreLocations` maintains an internal index of all the boundaries/geofences used by iD. + // It's used by presets, community index, background imagery, to know where in the world these things are valid. + // These geofences should be defined by `locationSet` objects: + // + // let locationSet = { + // include: [ Array of locations ], + // exclude: [ Array of locations ] + // }; + // + // For more info see the location-conflation and country-coder projects, see: + // https://github.com/ideditor/location-conflation + // https://github.com/ideditor/country-coder + // + + function coreLocations() { + var _this = {}; + var _resolvedFeatures = {}; // cache of *resolved* locationSet features + + var _loco = new _default(); // instance of a location-conflation resolver + + + var _wp; // instance of a which-polygon index + // pre-resolve the worldwide locationSet + + + var world = { + locationSet: { + include: ['Q2'] + } + }; + resolveLocationSet(world); + rebuildIndex(); + var _queue = []; + + var _deferred = new Set(); + + var _inProcess; // Returns a Promise to process the queue + + + function processQueue() { + if (!_queue.length) return Promise.resolve(); // console.log(`queue length ${_queue.length}`); + + var chunk = _queue.pop(); + + return new Promise(function (resolvePromise) { + var handle = window.requestIdleCallback(function () { + _deferred["delete"](handle); // const t0 = performance.now(); + + + chunk.forEach(resolveLocationSet); // const t1 = performance.now(); + // console.log('chunk processed in ' + (t1 - t0) + ' ms'); + + resolvePromise(); + }); + + _deferred.add(handle); + }).then(function () { + return processQueue(); + }); + } // Pass an Object with a `locationSet` property, + // Performs the locationSet resolution, caches the result, and sets a `locationSetID` property on the object. + + + function resolveLocationSet(obj) { + if (obj.locationSetID) return; // work was done already + + try { + var locationSet = obj.locationSet; + + if (!locationSet) { + throw new Error('object missing locationSet property'); + } + + if (!locationSet.include) { + // missing `include`, default to worldwide include + locationSet.include = ['Q2']; // https://github.com/openstreetmap/iD/pull/8305#discussion_r662344647 + } + + var resolved = _loco.resolveLocationSet(locationSet); + + var locationSetID = resolved.id; + obj.locationSetID = locationSetID; + + if (!resolved.feature.geometry.coordinates.length || !resolved.feature.properties.area) { + throw new Error("locationSet ".concat(locationSetID, " resolves to an empty feature.")); + } + + if (!_resolvedFeatures[locationSetID]) { + // First time seeing this locationSet feature + var feature = JSON.parse(JSON.stringify(resolved.feature)); // deep clone + + feature.id = locationSetID; // Important: always use the locationSet `id` (`+[Q30]`), not the feature `id` (`Q30`) + + feature.properties.id = locationSetID; + _resolvedFeatures[locationSetID] = feature; // insert into cache + } + } catch (err) { + obj.locationSet = { + include: ['Q2'] + }; // default worldwide + + obj.locationSetID = '+[Q2]'; + } + } // Rebuilds the whichPolygon index with whatever features have been resolved. + + + function rebuildIndex() { + _wp = whichPolygon_1({ + features: Object.values(_resolvedFeatures) + }); + } // + // `mergeCustomGeoJSON` + // Accepts an FeatureCollection-like object containing custom locations + // Each feature must have a filename-like `id`, for example: `something.geojson` + // + // { + // "type": "FeatureCollection" + // "features": [ + // { + // "type": "Feature", + // "id": "philly_metro.geojson", + // "properties": { … }, + // "geometry": { … } + // } + // ] + // } + // + + + _this.mergeCustomGeoJSON = function (fc) { + if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) { + fc.features.forEach(function (feature) { + feature.properties = feature.properties || {}; + var props = feature.properties; // Get `id` from either `id` or `properties` + + var id = feature.id || props.id; + if (!id || !/^\S+\.geojson$/i.test(id)) return; // Ensure `id` exists and is lowercase + + id = id.toLowerCase(); + feature.id = id; + props.id = id; // Ensure `area` property exists + + if (!props.area) { + var area = geojsonArea.geometry(feature.geometry) / 1e6; // m² to km² + + props.area = Number(area.toFixed(2)); + } + + _loco._cache[id] = feature; + }); + } + }; // + // `mergeLocationSets` + // Accepts an Array of Objects containing `locationSet` properties. + // The locationSets will be resolved and indexed in the background. + // [ + // { id: 'preset1', locationSet: {…} }, + // { id: 'preset2', locationSet: {…} }, + // { id: 'preset3', locationSet: {…} }, + // … + // ] + // After resolving and indexing, the Objects will be decorated with a + // `locationSetID` property. + // [ + // { id: 'preset1', locationSet: {…}, locationSetID: '+[Q2]' }, + // { id: 'preset2', locationSet: {…}, locationSetID: '+[Q30]' }, + // { id: 'preset3', locationSet: {…}, locationSetID: '+[Q2]' }, + // … + // ] + // + // Returns a Promise fulfilled when the resolving/indexing has been completed + // This will take some seconds but happen in the background during browser idle time. + // + + + _this.mergeLocationSets = function (objects) { + if (!Array.isArray(objects)) return Promise.reject('nothing to do'); // Resolve all locationSets -> geojson, processing data in chunks + // + // Because this will happen during idle callbacks, we want to choose a chunk size + // that won't make the browser stutter too badly. LocationSets that are a simple + // country coder include will resolve instantly, but ones that involve complex + // include/exclude operations will take some milliseconds longer. + // + // Some discussion and performance results on these tickets: + // https://github.com/ideditor/location-conflation/issues/26 + // https://github.com/osmlab/name-suggestion-index/issues/4784#issuecomment-742003434 + + _queue = _queue.concat(utilArrayChunk(objects, 200)); + + if (!_inProcess) { + _inProcess = processQueue().then(function () { + rebuildIndex(); + _inProcess = null; + return objects; + }); + } + + return _inProcess; + }; // + // `locationSetID` + // Returns a locationSetID for a given locationSet (fallback to `+[Q2]`, world) + // (The locationset doesn't necessarily need to be resolved to compute its `id`) + // + // Arguments + // `locationSet`: A locationSet, e.g. `{ include: ['us'] }` + // Returns + // The locationSetID, e.g. `+[Q30]` + // + + + _this.locationSetID = function (locationSet) { + var locationSetID; + + try { + locationSetID = _loco.validateLocationSet(locationSet).id; + } catch (err) { + locationSetID = '+[Q2]'; // the world + } + + return locationSetID; + }; // + // `feature` + // Returns the resolved GeoJSON feature for a given locationSetID (fallback to 'world') + // + // Arguments + // `locationSetID`: id of the form like `+[Q30]` (United States) + // Returns + // A GeoJSON feature: + // { + // type: 'Feature', + // id: '+[Q30]', + // properties: { id: '+[Q30]', area: 21817019.17, … }, + // geometry: { … } + // } + + + _this.feature = function (locationSetID) { + return _resolvedFeatures[locationSetID] || _resolvedFeatures['+[Q2]']; + }; // + // `locationsAt` + // Find all the resolved locationSets valid at the given location. + // Results include the area (in km²) to facilitate sorting. + // + // Arguments + // `loc`: the [lon,lat] location to query, e.g. `[-74.4813, 40.7967]` + // Returns + // Object of locationSetIDs to areas (in km²) + // { + // "+[Q2]": 511207893.3958111, + // "+[Q30]": 21817019.17, + // "+[new_jersey.geojson]": 22390.77, + // … + // } + // + + + _this.locationsAt = function (loc) { + var result = {}; + (_wp(loc, true) || []).forEach(function (prop) { + return result[prop.id] = prop.area; + }); + return result; + }; // + // `query` + // Execute a query directly against which-polygon + // https://github.com/mapbox/which-polygon + // + // Arguments + // `loc`: the [lon,lat] location to query, + // `multi`: `true` to return all results, `false` to return first result + // Returns + // Array of GeoJSON *properties* for the locationSet features that exist at `loc` + // + + + _this.query = function (loc, multi) { + return _wp(loc, multi); + }; // Direct access to the location-conflation resolver + + + _this.loco = function () { + return _loco; + }; // Direct access to the which-polygon index + + + _this.wp = function () { + return _wp; + }; + + return _this; + } + + var $$a = _export; + var $findIndex = arrayIteration.findIndex; + var addToUnscopables = addToUnscopables$5; + + var FIND_INDEX = 'findIndex'; + var SKIPS_HOLES = true; + + // Shouldn't skip holes + if (FIND_INDEX in []) Array(1)[FIND_INDEX](function () { SKIPS_HOLES = false; }); + + // `Array.prototype.findIndex` method + // https://tc39.es/ecma262/#sec-array.prototype.findindex + $$a({ target: 'Array', proto: true, forced: SKIPS_HOLES }, { + findIndex: function findIndex(callbackfn /* , that = undefined */) { + return $findIndex(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); + } + }); + + // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables + addToUnscopables(FIND_INDEX); + + var isRegExp = isRegexp; + + var notARegexp = function (it) { + if (isRegExp(it)) { + throw TypeError("The method doesn't accept regular expressions"); + } return it; + }; + + var wellKnownSymbol = wellKnownSymbol$s; + + var MATCH = wellKnownSymbol('match'); + + var correctIsRegexpLogic = function (METHOD_NAME) { + var regexp = /./; + try { + '/./'[METHOD_NAME](regexp); + } catch (error1) { + try { + regexp[MATCH] = false; + return '/./'[METHOD_NAME](regexp); + } catch (error2) { /* empty */ } + } return false; + }; + + var $$9 = _export; + var notARegExp$2 = notARegexp; + var requireObjectCoercible$3 = requireObjectCoercible$e; + var correctIsRegExpLogic$2 = correctIsRegexpLogic; + + // `String.prototype.includes` method + // https://tc39.es/ecma262/#sec-string.prototype.includes + $$9({ target: 'String', proto: true, forced: !correctIsRegExpLogic$2('includes') }, { + includes: function includes(searchString /* , position = 0 */) { + return !!~String(requireObjectCoercible$3(this)) + .indexOf(notARegExp$2(searchString), arguments.length > 1 ? arguments[1] : undefined); + } + }); + + var _mainLocalizer = coreLocalizer(); // singleton + + + var _t = _mainLocalizer.t; + // coreLocalizer manages language and locale parameters including translated strings + // + + function coreLocalizer() { + var localizer = {}; + var _dataLanguages = {}; // `_dataLocales` is an object containing all _supported_ locale codes -> language info. + // * `rtl` - right-to-left or left-to-right text direction + // * `pct` - the percent of strings translated; 1 = 100%, full coverage + // + // { + // en: { rtl: false, pct: {…} }, + // de: { rtl: false, pct: {…} }, + // … + // } + + var _dataLocales = {}; // `localeStrings` is an object containing all _loaded_ locale codes -> string data. + // { + // en: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … }, + // de: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … }, + // … + // } + + var _localeStrings = {}; // the current locale + + var _localeCode = 'en-US'; // `_localeCodes` must contain `_localeCode` first, optionally followed by fallbacks + + var _localeCodes = ['en-US', 'en']; + var _languageCode = 'en'; + var _textDirection = 'ltr'; + var _usesMetric = false; + var _languageNames = {}; + var _scriptNames = {}; // getters for the current locale parameters + + localizer.localeCode = function () { + return _localeCode; + }; + + localizer.localeCodes = function () { + return _localeCodes; + }; + + localizer.languageCode = function () { + return _languageCode; + }; + + localizer.textDirection = function () { + return _textDirection; + }; + + localizer.usesMetric = function () { + return _usesMetric; + }; + + localizer.languageNames = function () { + return _languageNames; + }; + + localizer.scriptNames = function () { + return _scriptNames; + }; // The client app may want to manually set the locale, regardless of the + // settings provided by the browser + + + var _preferredLocaleCodes = []; + + localizer.preferredLocaleCodes = function (codes) { + if (!arguments.length) return _preferredLocaleCodes; + + if (typeof codes === 'string') { + // be generous and accept delimited strings as input + _preferredLocaleCodes = codes.split(/,|;| /gi).filter(Boolean); + } else { + _preferredLocaleCodes = codes; + } + + return localizer; + }; + + var _loadPromise; + + localizer.ensureLoaded = function () { + if (_loadPromise) return _loadPromise; + var filesToFetch = ['languages', // load the list of languages + 'locales' // load the list of supported locales + ]; + var localeDirs = { + general: 'locales', + tagging: 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/translations' + }; + var fileMap = _mainFileFetcher.fileMap(); + + for (var scopeId in localeDirs) { + var key = "locales_index_".concat(scopeId); + + if (!fileMap[key]) { + fileMap[key] = localeDirs[scopeId] + '/index.min.json'; + } + + filesToFetch.push(key); + } + + return _loadPromise = Promise.all(filesToFetch.map(function (key) { + return _mainFileFetcher.get(key); + })).then(function (results) { + _dataLanguages = results[0]; + _dataLocales = results[1]; + var indexes = results.slice(2); + + var requestedLocales = (_preferredLocaleCodes || []).concat(utilDetect().browserLocales) // List of locales preferred by the browser in priority order. + .concat(['en']); // fallback to English since it's the only guaranteed complete language + + + _localeCodes = localesToUseFrom(requestedLocales); + _localeCode = _localeCodes[0]; // Run iD in the highest-priority locale; the rest are fallbacks + + var loadStringsPromises = []; + indexes.forEach(function (index, i) { + // Will always return the index for `en` if nothing else + var fullCoverageIndex = _localeCodes.findIndex(function (locale) { + return index[locale] && index[locale].pct === 1; + }); // We only need to load locales up until we find one with full coverage + + + _localeCodes.slice(0, fullCoverageIndex + 1).forEach(function (code) { + var scopeId = Object.keys(localeDirs)[i]; + var directory = Object.values(localeDirs)[i]; + if (index[code]) loadStringsPromises.push(localizer.loadLocale(code, scopeId, directory)); + }); + }); + return Promise.all(loadStringsPromises); + }).then(function () { + updateForCurrentLocale(); + })["catch"](function (err) { + return console.error(err); + }); // eslint-disable-line + }; // Returns the locales from `requestedLocales` supported by iD that we should use + + + function localesToUseFrom(requestedLocales) { + var supportedLocales = _dataLocales; + var toUse = []; + + for (var i in requestedLocales) { + var locale = requestedLocales[i]; + if (supportedLocales[locale]) toUse.push(locale); + + if (locale.includes('-')) { + // Full locale ('es-ES'), add fallback to the base ('es') + var langPart = locale.split('-')[0]; + if (supportedLocales[langPart]) toUse.push(langPart); + } + } // remove duplicates + + + return utilArrayUniq(toUse); + } + + function updateForCurrentLocale() { + if (!_localeCode) return; + _languageCode = _localeCode.split('-')[0]; + var currentData = _dataLocales[_localeCode] || _dataLocales[_languageCode]; + var hash = utilStringQs(window.location.hash); + + if (hash.rtl === 'true') { + _textDirection = 'rtl'; + } else if (hash.rtl === 'false') { + _textDirection = 'ltr'; + } else { + _textDirection = currentData && currentData.rtl ? 'rtl' : 'ltr'; + } + + var locale = _localeCode; + if (locale.toLowerCase() === 'en-us') locale = 'en'; + _languageNames = _localeStrings.general[locale].languageNames; + _scriptNames = _localeStrings.general[locale].scriptNames; + _usesMetric = _localeCode.slice(-3).toLowerCase() !== '-us'; + } + /* Locales */ + // Returns a Promise to load the strings for the requested locale + + + localizer.loadLocale = function (locale, scopeId, directory) { + // US English is the default + if (locale.toLowerCase() === 'en-us') locale = 'en'; + + if (_localeStrings[scopeId] && _localeStrings[scopeId][locale]) { + // already loaded + return Promise.resolve(locale); + } + + var fileMap = _mainFileFetcher.fileMap(); + var key = "locale_".concat(scopeId, "_").concat(locale); + + if (!fileMap[key]) { + fileMap[key] = "".concat(directory, "/").concat(locale, ".min.json"); + } + + return _mainFileFetcher.get(key).then(function (d) { + if (!_localeStrings[scopeId]) _localeStrings[scopeId] = {}; + _localeStrings[scopeId][locale] = d[locale]; + return locale; + }); + }; + + localizer.pluralRule = function (number) { + return pluralRule(number, _localeCode); + }; // Returns the plural rule for the given `number` with the given `localeCode`. + // One of: `zero`, `one`, `two`, `few`, `many`, `other` + + + function pluralRule(number, localeCode) { + // modern browsers have this functionality built-in + var rules = 'Intl' in window && Intl.PluralRules && new Intl.PluralRules(localeCode); + + if (rules) { + return rules.select(number); + } // fallback to basic one/other, as in English + + + if (number === 1) return 'one'; + return 'other'; + } + /** + * Try to find that string in `locale` or the current `_localeCode` matching + * the given `stringId`. If no string can be found in the requested locale, + * we'll recurse down all the `_localeCodes` until one is found. + * + * @param {string} stringId string identifier + * @param {object?} replacements token replacements and default string + * @param {string?} locale locale to use (defaults to currentLocale) + * @return {string?} localized string + */ + + + localizer.tInfo = function (origStringId, replacements, locale) { + var stringId = origStringId.trim(); + var scopeId = 'general'; + + if (stringId[0] === '_') { + var split = stringId.split('.'); + scopeId = split[0].slice(1); + stringId = split.slice(1).join('.'); + } + + locale = locale || _localeCode; + var path = stringId.split('.').map(function (s) { + return s.replace(//g, '.'); + }).reverse(); + var stringsKey = locale; // US English is the default + + if (stringsKey.toLowerCase() === 'en-us') stringsKey = 'en'; + var result = _localeStrings && _localeStrings[scopeId] && _localeStrings[scopeId][stringsKey]; + + while (result !== undefined && path.length) { + result = result[path.pop()]; + } + + if (result !== undefined) { + if (replacements) { + if (_typeof(result) === 'object' && Object.keys(result).length) { + // If plural forms are provided, dig one level deeper based on the + // first numeric token replacement provided. + var number = Object.values(replacements).find(function (value) { + return typeof value === 'number'; + }); + + if (number !== undefined) { + var rule = pluralRule(number, locale); + + if (result[rule]) { + result = result[rule]; + } else { + // We're pretty sure this should be a plural but no string + // could be found for the given rule. Just pick the first + // string and hope it makes sense. + result = Object.values(result)[0]; + } + } + } + + if (typeof result === 'string') { + for (var key in replacements) { + var value = replacements[key]; + + if (typeof value === 'number') { + if (value.toLocaleString) { + // format numbers for the locale + value = value.toLocaleString(locale, { + style: 'decimal', + useGrouping: true, + minimumFractionDigits: 0 + }); + } else { + value = value.toString(); + } + } + + var token = "{".concat(key, "}"); + var regex = new RegExp(token, 'g'); + result = result.replace(regex, value); + } + } + } + + if (typeof result === 'string') { + // found a localized string! + return { + text: result, + locale: locale + }; + } + } // no localized string found... + // attempt to fallback to a lower-priority language + + + var index = _localeCodes.indexOf(locale); + + if (index >= 0 && index < _localeCodes.length - 1) { + // eventually this will be 'en' or another locale with 100% coverage + var fallback = _localeCodes[index + 1]; + return localizer.tInfo(origStringId, replacements, fallback); + } + + if (replacements && 'default' in replacements) { + // Fallback to a default value if one is specified in `replacements` + return { + text: replacements["default"], + locale: null + }; + } + + var missing = "Missing ".concat(locale, " translation: ").concat(origStringId); + if (typeof console !== 'undefined') console.error(missing); // eslint-disable-line + + return { + text: missing, + locale: 'en' + }; + }; + + localizer.hasTextForStringId = function (stringId) { + return !!localizer.tInfo(stringId, { + "default": 'nothing found' + }).locale; + }; // Returns only the localized text, discarding the locale info + + + localizer.t = function (stringId, replacements, locale) { + return localizer.tInfo(stringId, replacements, locale).text; + }; // Returns the localized text wrapped in an HTML element encoding the locale info + + + localizer.t.html = function (stringId, replacements, locale) { + var info = localizer.tInfo(stringId, replacements, locale); // text may be empty or undefined if `replacements.default` is + + return info.text ? localizer.htmlForLocalizedText(info.text, info.locale) : ''; + }; + + localizer.htmlForLocalizedText = function (text, localeCode) { + return "").concat(text, ""); + }; + + localizer.languageName = function (code, options) { + if (_languageNames[code]) { + // name in locale language + // e.g. "German" + return _languageNames[code]; + } // sometimes we only want the local name + + + if (options && options.localOnly) return null; + var langInfo = _dataLanguages[code]; + + if (langInfo) { + if (langInfo.nativeName) { + // name in native language + // e.g. "Deutsch (de)" + return localizer.t('translate.language_and_code', { + language: langInfo.nativeName, + code: code + }); + } else if (langInfo.base && langInfo.script) { + var base = langInfo.base; // the code of the language this is based on + + if (_languageNames[base]) { + // base language name in locale language + var scriptCode = langInfo.script; + var script = _scriptNames[scriptCode] || scriptCode; // e.g. "Serbian (Cyrillic)" + + return localizer.t('translate.language_and_code', { + language: _languageNames[base], + code: script + }); + } else if (_dataLanguages[base] && _dataLanguages[base].nativeName) { + // e.g. "српски (sr-Cyrl)" + return localizer.t('translate.language_and_code', { + language: _dataLanguages[base].nativeName, + code: code + }); + } + } + } + + return code; // if not found, use the code + }; + + return localizer; + } + + // `presetCollection` is a wrapper around an `Array` of presets `collection`, + // and decorated with some extra methods for searching and matching geometry + // + + function presetCollection(collection) { + var MAXRESULTS = 50; + var _this = {}; + var _memo = {}; + _this.collection = collection; + + _this.item = function (id) { + if (_memo[id]) return _memo[id]; + + var found = _this.collection.find(function (d) { + return d.id === id; + }); + + if (found) _memo[id] = found; + return found; + }; + + _this.index = function (id) { + return _this.collection.findIndex(function (d) { + return d.id === id; + }); + }; + + _this.matchGeometry = function (geometry) { + return presetCollection(_this.collection.filter(function (d) { + return d.matchGeometry(geometry); + })); + }; + + _this.matchAllGeometry = function (geometries) { + return presetCollection(_this.collection.filter(function (d) { + return d && d.matchAllGeometry(geometries); + })); + }; + + _this.matchAnyGeometry = function (geometries) { + return presetCollection(_this.collection.filter(function (d) { + return geometries.some(function (geom) { + return d.matchGeometry(geom); + }); + })); + }; + + _this.fallback = function (geometry) { + var id = geometry; + if (id === 'vertex') id = 'point'; + return _this.item(id); + }; + + _this.search = function (value, geometry, loc) { + if (!value) return _this; // don't remove diacritical characters since we're assuming the user is being intentional + + value = value.toLowerCase().trim(); // match at name beginning or just after a space (e.g. "office" -> match "Law Office") + + function leading(a) { + var index = a.indexOf(value); + return index === 0 || a[index - 1] === ' '; + } // match at name beginning only + + + function leadingStrict(a) { + var index = a.indexOf(value); + return index === 0; + } + + function sortPresets(nameProp) { + return function sortNames(a, b) { + var aCompare = a[nameProp](); + var bCompare = b[nameProp](); // priority if search string matches preset name exactly - #4325 + + if (value === aCompare) return -1; + if (value === bCompare) return 1; // priority for higher matchScore + + var i = b.originalScore - a.originalScore; + if (i !== 0) return i; // priority if search string appears earlier in preset name + + i = aCompare.indexOf(value) - bCompare.indexOf(value); + if (i !== 0) return i; // priority for shorter preset names + + return aCompare.length - bCompare.length; + }; + } + + var pool = _this.collection; + + if (Array.isArray(loc)) { + var validLocations = _mainLocations.locationsAt(loc); + pool = pool.filter(function (a) { + return !a.locationSetID || validLocations[a.locationSetID]; + }); + } + + var searchable = pool.filter(function (a) { + return a.searchable !== false && a.suggestion !== true; + }); + var suggestions = pool.filter(function (a) { + return a.suggestion === true; + }); // matches value to preset.name + + var leadingNames = searchable.filter(function (a) { + return leading(a.searchName()); + }).sort(sortPresets('searchName')); // matches value to preset suggestion name + + var leadingSuggestions = suggestions.filter(function (a) { + return leadingStrict(a.searchName()); + }).sort(sortPresets('searchName')); + var leadingNamesStripped = searchable.filter(function (a) { + return leading(a.searchNameStripped()); + }).sort(sortPresets('searchNameStripped')); + var leadingSuggestionsStripped = suggestions.filter(function (a) { + return leadingStrict(a.searchNameStripped()); + }).sort(sortPresets('searchNameStripped')); // matches value to preset.terms values + + var leadingTerms = searchable.filter(function (a) { + return (a.terms() || []).some(leading); + }); + var leadingSuggestionTerms = suggestions.filter(function (a) { + return (a.terms() || []).some(leading); + }); // matches value to preset.tags values + + var leadingTagValues = searchable.filter(function (a) { + return Object.values(a.tags || {}).filter(function (val) { + return val !== '*'; + }).some(leading); + }); // finds close matches to value in preset.name + + var similarName = searchable.map(function (a) { + return { + preset: a, + dist: utilEditDistance(value, a.searchName()) + }; + }).filter(function (a) { + return a.dist + Math.min(value.length - a.preset.searchName().length, 0) < 3; + }).sort(function (a, b) { + return a.dist - b.dist; + }).map(function (a) { + return a.preset; + }); // finds close matches to value to preset suggestion name + + var similarSuggestions = suggestions.map(function (a) { + return { + preset: a, + dist: utilEditDistance(value, a.searchName()) + }; + }).filter(function (a) { + return a.dist + Math.min(value.length - a.preset.searchName().length, 0) < 1; + }).sort(function (a, b) { + return a.dist - b.dist; + }).map(function (a) { + return a.preset; + }); // finds close matches to value in preset.terms + + var similarTerms = searchable.filter(function (a) { + return (a.terms() || []).some(function (b) { + return utilEditDistance(value, b) + Math.min(value.length - b.length, 0) < 3; + }); + }); + var results = leadingNames.concat(leadingSuggestions, leadingNamesStripped, leadingSuggestionsStripped, leadingTerms, leadingSuggestionTerms, leadingTagValues, similarName, similarSuggestions, similarTerms).slice(0, MAXRESULTS - 1); + + if (geometry) { + if (typeof geometry === 'string') { + results.push(_this.fallback(geometry)); + } else { + geometry.forEach(function (geom) { + return results.push(_this.fallback(geom)); + }); + } + } + + return presetCollection(utilArrayUniq(results)); + }; + + return _this; + } + + // `presetCategory` builds a `presetCollection` of member presets, + // decorated with some extra methods for searching and matching geometry + // + + function presetCategory(categoryID, category, allPresets) { + var _this = Object.assign({}, category); // shallow copy + + + var _searchName; // cache + + + var _searchNameStripped; // cache + + + _this.id = categoryID; + _this.members = presetCollection((category.members || []).map(function (presetID) { + return allPresets[presetID]; + }).filter(Boolean)); + _this.geometry = _this.members.collection.reduce(function (acc, preset) { + for (var i in preset.geometry) { + var geometry = preset.geometry[i]; + + if (acc.indexOf(geometry) === -1) { + acc.push(geometry); + } + } + + return acc; + }, []); + + _this.matchGeometry = function (geom) { + return _this.geometry.indexOf(geom) >= 0; + }; + + _this.matchAllGeometry = function (geometries) { + return _this.members.collection.some(function (preset) { + return preset.matchAllGeometry(geometries); + }); + }; + + _this.matchScore = function () { + return -1; + }; + + _this.name = function () { + return _t("_tagging.presets.categories.".concat(categoryID, ".name"), { + 'default': categoryID + }); + }; + + _this.nameLabel = function () { + return _t.html("_tagging.presets.categories.".concat(categoryID, ".name"), { + 'default': categoryID + }); + }; + + _this.terms = function () { + return []; + }; + + _this.searchName = function () { + if (!_searchName) { + _searchName = (_this.suggestion ? _this.originalName : _this.name()).toLowerCase(); + } + + return _searchName; + }; + + _this.searchNameStripped = function () { + if (!_searchNameStripped) { + _searchNameStripped = _this.searchName(); // split combined diacritical characters into their parts + + if (_searchNameStripped.normalize) _searchNameStripped = _searchNameStripped.normalize('NFD'); // remove diacritics + + _searchNameStripped = _searchNameStripped.replace(/[\u0300-\u036f]/g, ''); + } + + return _searchNameStripped; + }; + + return _this; + } + + // `presetField` decorates a given `field` Object + // with some extra methods for searching and matching geometry + // + + function presetField(fieldID, field) { + var _this = Object.assign({}, field); // shallow copy + + + _this.id = fieldID; // for use in classes, element ids, css selectors + + _this.safeid = utilSafeClassName(fieldID); + + _this.matchGeometry = function (geom) { + return !_this.geometry || _this.geometry.indexOf(geom) !== -1; + }; + + _this.matchAllGeometry = function (geometries) { + return !_this.geometry || geometries.every(function (geom) { + return _this.geometry.indexOf(geom) !== -1; + }); + }; + + _this.t = function (scope, options) { + return _t("_tagging.presets.fields.".concat(fieldID, ".").concat(scope), options); + }; + + _this.t.html = function (scope, options) { + return _t.html("_tagging.presets.fields.".concat(fieldID, ".").concat(scope), options); + }; + + _this.hasTextForStringId = function (scope) { + return _mainLocalizer.hasTextForStringId("_tagging.presets.fields.".concat(fieldID, ".").concat(scope)); + }; + + _this.title = function () { + return _this.overrideLabel || _this.t('label', { + 'default': fieldID + }); + }; + + _this.label = function () { + return _this.overrideLabel || _this.t.html('label', { + 'default': fieldID + }); + }; + + var _placeholder = _this.placeholder; + + _this.placeholder = function () { + return _this.t('placeholder', { + 'default': _placeholder + }); + }; + + _this.originalTerms = (_this.terms || []).join(); + + _this.terms = function () { + return _this.t('terms', { + 'default': _this.originalTerms + }).toLowerCase().trim().split(/\s*,+\s*/); + }; + + _this.increment = _this.type === 'number' ? _this.increment || 1 : undefined; + return _this; + } + + var $$8 = _export; + var lastIndexOf = arrayLastIndexOf; + + // `Array.prototype.lastIndexOf` method + // https://tc39.es/ecma262/#sec-array.prototype.lastindexof + // eslint-disable-next-line es/no-array-prototype-lastindexof -- required for testing + $$8({ target: 'Array', proto: true, forced: lastIndexOf !== [].lastIndexOf }, { + lastIndexOf: lastIndexOf + }); + + // `presetPreset` decorates a given `preset` Object + // with some extra methods for searching and matching geometry + // + + function presetPreset(presetID, preset, addable, allFields, allPresets) { + allFields = allFields || {}; + allPresets = allPresets || {}; + + var _this = Object.assign({}, preset); // shallow copy + + + var _addable = addable || false; + + var _resolvedFields; // cache + + + var _resolvedMoreFields; // cache + + + var _searchName; // cache + + + var _searchNameStripped; // cache + + + _this.id = presetID; + _this.safeid = utilSafeClassName(presetID); // for use in css classes, selectors, element ids + + _this.originalTerms = (_this.terms || []).join(); + _this.originalName = _this.name || ''; + _this.originalScore = _this.matchScore || 1; + _this.originalReference = _this.reference || {}; + _this.originalFields = _this.fields || []; + _this.originalMoreFields = _this.moreFields || []; + + _this.fields = function () { + return _resolvedFields || (_resolvedFields = resolve('fields')); + }; + + _this.moreFields = function () { + return _resolvedMoreFields || (_resolvedMoreFields = resolve('moreFields')); + }; + + _this.resetFields = function () { + return _resolvedFields = _resolvedMoreFields = null; + }; + + _this.tags = _this.tags || {}; + _this.addTags = _this.addTags || _this.tags; + _this.removeTags = _this.removeTags || _this.addTags; + _this.geometry = _this.geometry || []; + + _this.matchGeometry = function (geom) { + return _this.geometry.indexOf(geom) >= 0; + }; + + _this.matchAllGeometry = function (geoms) { + return geoms.every(_this.matchGeometry); + }; + + _this.matchScore = function (entityTags) { + var tags = _this.tags; + var seen = {}; + var score = 0; // match on tags + + for (var k in tags) { + seen[k] = true; + + if (entityTags[k] === tags[k]) { + score += _this.originalScore; + } else if (tags[k] === '*' && k in entityTags) { + score += _this.originalScore / 2; + } else { + return -1; + } + } // boost score for additional matches in addTags - #6802 + + + var addTags = _this.addTags; + + for (var _k in addTags) { + if (!seen[_k] && entityTags[_k] === addTags[_k]) { + score += _this.originalScore; + } + } + + return score; + }; + + _this.t = function (scope, options) { + var textID = "_tagging.presets.presets.".concat(presetID, ".").concat(scope); + return _t(textID, options); + }; + + _this.t.html = function (scope, options) { + var textID = "_tagging.presets.presets.".concat(presetID, ".").concat(scope); + return _t.html(textID, options); + }; + + _this.name = function () { + return _this.t('name', { + 'default': _this.originalName + }); + }; + + _this.nameLabel = function () { + return _this.t.html('name', { + 'default': _this.originalName + }); + }; + + _this.subtitle = function () { + if (_this.suggestion) { + var path = presetID.split('/'); + path.pop(); // remove brand name + + return _t('_tagging.presets.presets.' + path.join('/') + '.name'); + } + + return null; + }; + + _this.subtitleLabel = function () { + if (_this.suggestion) { + var path = presetID.split('/'); + path.pop(); // remove brand name + + return _t.html('_tagging.presets.presets.' + path.join('/') + '.name'); + } + + return null; + }; + + _this.terms = function () { + return _this.t('terms', { + 'default': _this.originalTerms + }).toLowerCase().trim().split(/\s*,+\s*/); + }; + + _this.searchName = function () { + if (!_searchName) { + _searchName = (_this.suggestion ? _this.originalName : _this.name()).toLowerCase(); + } + + return _searchName; + }; + + _this.searchNameStripped = function () { + if (!_searchNameStripped) { + _searchNameStripped = _this.searchName(); // split combined diacritical characters into their parts + + if (_searchNameStripped.normalize) _searchNameStripped = _searchNameStripped.normalize('NFD'); // remove diacritics + + _searchNameStripped = _searchNameStripped.replace(/[\u0300-\u036f]/g, ''); + } + + return _searchNameStripped; + }; + + _this.isFallback = function () { + var tagCount = Object.keys(_this.tags).length; + return tagCount === 0 || tagCount === 1 && _this.tags.hasOwnProperty('area'); + }; + + _this.addable = function (val) { + if (!arguments.length) return _addable; + _addable = val; + return _this; + }; + + _this.reference = function () { + // Lookup documentation on Wikidata... + var qid = _this.tags.wikidata || _this.tags['flag:wikidata'] || _this.tags['brand:wikidata'] || _this.tags['network:wikidata'] || _this.tags['operator:wikidata']; + + if (qid) { + return { + qid: qid + }; + } // Lookup documentation on OSM Wikibase... + + + var key = _this.originalReference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0]; + var value = _this.originalReference.value || _this.tags[key]; + + if (value === '*') { + return { + key: key + }; + } else { + return { + key: key, + value: value + }; + } + }; + + _this.unsetTags = function (tags, geometry, ignoringKeys, skipFieldDefaults) { + // allow manually keeping some tags + var removeTags = ignoringKeys ? utilObjectOmit(_this.removeTags, ignoringKeys) : _this.removeTags; + tags = utilObjectOmit(tags, Object.keys(removeTags)); + + if (geometry && !skipFieldDefaults) { + _this.fields().forEach(function (field) { + if (field.matchGeometry(geometry) && field.key && field["default"] === tags[field.key]) { + delete tags[field.key]; + } + }); + } + + delete tags.area; + return tags; + }; + + _this.setTags = function (tags, geometry, skipFieldDefaults) { + var addTags = _this.addTags; + tags = Object.assign({}, tags); // shallow copy + + for (var k in addTags) { + if (addTags[k] === '*') { + // if this tag is ancillary, don't override an existing value since any value is okay + if (_this.tags[k] || !tags[k] || tags[k] === 'no') { + tags[k] = 'yes'; + } + } else { + tags[k] = addTags[k]; + } + } // Add area=yes if necessary. + // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of: + // 1. chosen preset could be either an area or a line (`barrier=city_wall`) + // 2. chosen preset doesn't have a key in osmAreaKeys (`railway=station`) + + + if (!addTags.hasOwnProperty('area')) { + delete tags.area; + + if (geometry === 'area') { + var needsAreaTag = true; + + if (_this.geometry.indexOf('line') === -1) { + for (var _k2 in addTags) { + if (_k2 in osmAreaKeys) { + needsAreaTag = false; + break; + } + } + } + + if (needsAreaTag) { + tags.area = 'yes'; + } + } + } + + if (geometry && !skipFieldDefaults) { + _this.fields().forEach(function (field) { + if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field["default"]) { + tags[field.key] = field["default"]; + } + }); + } + + return tags; + }; // For a preset without fields, use the fields of the parent preset. + // Replace {preset} placeholders with the fields of the specified presets. + + + function resolve(which) { + var fieldIDs = which === 'fields' ? _this.originalFields : _this.originalMoreFields; + var resolved = []; + fieldIDs.forEach(function (fieldID) { + var match = fieldID.match(/\{(.*)\}/); + + if (match !== null) { + // a presetID wrapped in braces {} + resolved = resolved.concat(inheritFields(match[1], which)); + } else if (allFields[fieldID]) { + // a normal fieldID + resolved.push(allFields[fieldID]); + } else { + console.log("Cannot resolve \"".concat(fieldID, "\" found in ").concat(_this.id, ".").concat(which)); // eslint-disable-line no-console + } + }); // no fields resolved, so use the parent's if possible + + if (!resolved.length) { + var endIndex = _this.id.lastIndexOf('/'); + + var parentID = endIndex && _this.id.substring(0, endIndex); + + if (parentID) { + resolved = inheritFields(parentID, which); + } + } + + return utilArrayUniq(resolved); // returns an array of fields to inherit from the given presetID, if found + + function inheritFields(presetID, which) { + var parent = allPresets[presetID]; + if (!parent) return []; + + if (which === 'fields') { + return parent.fields().filter(shouldInherit); + } else if (which === 'moreFields') { + return parent.moreFields(); + } else { + return []; + } + } // Skip `fields` for the keys which define the preset. + // These are usually `typeCombo` fields like `shop=*` + + + function shouldInherit(f) { + if (f.key && _this.tags[f.key] !== undefined && // inherit anyway if multiple values are allowed or just a checkbox + f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'manyCombo' && f.type !== 'check') return false; + return true; + } + } + + return _this; + } + + var _mainPresetIndex = presetIndex(); // singleton + // `presetIndex` wraps a `presetCollection` + // with methods for loading new data and returning defaults + // + + function presetIndex() { + var dispatch = dispatch$8('favoritePreset', 'recentsChange'); + var MAXRECENTS = 30; // seed the preset lists with geometry fallbacks + + var POINT = presetPreset('point', { + name: 'Point', + tags: {}, + geometry: ['point', 'vertex'], + matchScore: 0.1 + }); + var LINE = presetPreset('line', { + name: 'Line', + tags: {}, + geometry: ['line'], + matchScore: 0.1 + }); + var AREA = presetPreset('area', { + name: 'Area', + tags: { + area: 'yes' + }, + geometry: ['area'], + matchScore: 0.1 + }); + var RELATION = presetPreset('relation', { + name: 'Relation', + tags: {}, + geometry: ['relation'], + matchScore: 0.1 + }); + + var _this = presetCollection([POINT, LINE, AREA, RELATION]); + + var _presets = { + point: POINT, + line: LINE, + area: AREA, + relation: RELATION + }; + var _defaults = { + point: presetCollection([POINT]), + vertex: presetCollection([POINT]), + line: presetCollection([LINE]), + area: presetCollection([AREA]), + relation: presetCollection([RELATION]) + }; + var _fields = {}; + var _categories = {}; + var _universal = []; + var _addablePresetIDs = null; // Set of preset IDs that the user can add + + var _recents; + + var _favorites; // Index of presets by (geometry, tag key). + + + var _geometryIndex = { + point: {}, + vertex: {}, + line: {}, + area: {}, + relation: {} + }; + + var _loadPromise; + + _this.ensureLoaded = function () { + if (_loadPromise) return _loadPromise; + return _loadPromise = Promise.all([_mainFileFetcher.get('preset_categories'), _mainFileFetcher.get('preset_defaults'), _mainFileFetcher.get('preset_presets'), _mainFileFetcher.get('preset_fields')]).then(function (vals) { + _this.merge({ + categories: vals[0], + defaults: vals[1], + presets: vals[2], + fields: vals[3] + }); + + osmSetAreaKeys(_this.areaKeys()); + osmSetPointTags(_this.pointTags()); + osmSetVertexTags(_this.vertexTags()); + }); + }; // `merge` accepts an object containing new preset data (all properties optional): + // { + // fields: {}, + // presets: {}, + // categories: {}, + // defaults: {}, + // featureCollection: {} + //} + + + _this.merge = function (d) { + var newLocationSets = []; // Merge Fields + + if (d.fields) { + Object.keys(d.fields).forEach(function (fieldID) { + var f = d.fields[fieldID]; + + if (f) { + // add or replace + f = presetField(fieldID, f); + if (f.locationSet) newLocationSets.push(f); + _fields[fieldID] = f; + } else { + // remove + delete _fields[fieldID]; + } + }); + } // Merge Presets + + + if (d.presets) { + Object.keys(d.presets).forEach(function (presetID) { + var p = d.presets[presetID]; + + if (p) { + // add or replace + var isAddable = !_addablePresetIDs || _addablePresetIDs.has(presetID); + + p = presetPreset(presetID, p, isAddable, _fields, _presets); + if (p.locationSet) newLocationSets.push(p); + _presets[presetID] = p; + } else { + // remove (but not if it's a fallback) + var existing = _presets[presetID]; + + if (existing && !existing.isFallback()) { + delete _presets[presetID]; + } + } + }); + } // Merge Categories + + + if (d.categories) { + Object.keys(d.categories).forEach(function (categoryID) { + var c = d.categories[categoryID]; + + if (c) { + // add or replace + c = presetCategory(categoryID, c, _presets); + if (c.locationSet) newLocationSets.push(c); + _categories[categoryID] = c; + } else { + // remove + delete _categories[categoryID]; + } + }); + } // Rebuild _this.collection after changing presets and categories + + + _this.collection = Object.values(_presets).concat(Object.values(_categories)); // Merge Defaults + + if (d.defaults) { + Object.keys(d.defaults).forEach(function (geometry) { + var def = d.defaults[geometry]; + + if (Array.isArray(def)) { + // add or replace + _defaults[geometry] = presetCollection(def.map(function (id) { + return _presets[id] || _categories[id]; + }).filter(Boolean)); + } else { + // remove + delete _defaults[geometry]; + } + }); + } // Rebuild universal fields array + + + _universal = Object.values(_fields).filter(function (field) { + return field.universal; + }); // Reset all the preset fields - they'll need to be resolved again + + Object.values(_presets).forEach(function (preset) { + return preset.resetFields(); + }); // Rebuild geometry index + + _geometryIndex = { + point: {}, + vertex: {}, + line: {}, + area: {}, + relation: {} + }; + + _this.collection.forEach(function (preset) { + (preset.geometry || []).forEach(function (geometry) { + var g = _geometryIndex[geometry]; + + for (var key in preset.tags) { + g[key] = g[key] || {}; + var value = preset.tags[key]; + (g[key][value] = g[key][value] || []).push(preset); + } + }); + }); // Merge Custom Features + + + if (d.featureCollection && Array.isArray(d.featureCollection.features)) { + _mainLocations.mergeCustomGeoJSON(d.featureCollection); + } // Resolve all locationSet features. + + + if (newLocationSets.length) { + _mainLocations.mergeLocationSets(newLocationSets); + } + + return _this; + }; + + _this.match = function (entity, resolver) { + return resolver["transient"](entity, 'presetMatch', function () { + var geometry = entity.geometry(resolver); // Treat entities on addr:interpolation lines as points, not vertices - #3241 + + if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) { + geometry = 'point'; + } + + var entityExtent = entity.extent(resolver); + return _this.matchTags(entity.tags, geometry, entityExtent.center()); + }); + }; + + _this.matchTags = function (tags, geometry, loc) { + var keyIndex = _geometryIndex[geometry]; + var bestScore = -1; + var bestMatch; + var matchCandidates = []; + + for (var k in tags) { + var indexMatches = []; + var valueIndex = keyIndex[k]; + if (!valueIndex) continue; + var keyValueMatches = valueIndex[tags[k]]; + if (keyValueMatches) indexMatches.push.apply(indexMatches, _toConsumableArray(keyValueMatches)); + var keyStarMatches = valueIndex['*']; + if (keyStarMatches) indexMatches.push.apply(indexMatches, _toConsumableArray(keyStarMatches)); + if (indexMatches.length === 0) continue; + + for (var i = 0; i < indexMatches.length; i++) { + var candidate = indexMatches[i]; + var score = candidate.matchScore(tags); + + if (score === -1) { + continue; + } + + matchCandidates.push({ + score: score, + candidate: candidate + }); + + if (score > bestScore) { + bestScore = score; + bestMatch = candidate; + } + } + } + + if (bestMatch && bestMatch.locationSetID && bestMatch.locationSetID !== '+[Q2]' && Array.isArray(loc)) { + var validLocations = _mainLocations.locationsAt(loc); + + if (!validLocations[bestMatch.locationSetID]) { + matchCandidates.sort(function (a, b) { + return a.score < b.score ? 1 : -1; + }); + + for (var _i = 0; _i < matchCandidates.length; _i++) { + var candidateScore = matchCandidates[_i]; + + if (!candidateScore.candidate.locationSetID || validLocations[candidateScore.candidate.locationSetID]) { + bestMatch = candidateScore.candidate; + bestScore = candidateScore.score; + break; + } + } + } + } // If any part of an address is present, allow fallback to "Address" preset - #4353 + + + if (!bestMatch || bestMatch.isFallback()) { + for (var _k in tags) { + if (/^addr:/.test(_k) && keyIndex['addr:*'] && keyIndex['addr:*']['*']) { + bestMatch = keyIndex['addr:*']['*'][0]; + break; + } + } + } + + return bestMatch || _this.fallback(geometry); + }; + + _this.allowsVertex = function (entity, resolver) { + if (entity.type !== 'node') return false; + if (Object.keys(entity.tags).length === 0) return true; + return resolver["transient"](entity, 'vertexMatch', function () { + // address lines allow vertices to act as standalone points + if (entity.isOnAddressLine(resolver)) return true; + var geometries = osmNodeGeometriesForTags(entity.tags); + if (geometries.vertex) return true; + if (geometries.point) return false; // allow vertices for unspecified points + + return true; + }); + }; // Because of the open nature of tagging, iD will never have a complete + // list of tags used in OSM, so we want it to have logic like "assume + // that a closed way with an amenity tag is an area, unless the amenity + // is one of these specific types". This function computes a structure + // that allows testing of such conditions, based on the presets designated + // as as supporting (or not supporting) the area geometry. + // + // The returned object L is a keeplist/discardlist of tags. A closed way + // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])` + // (see `Way#isArea()`). In other words, the keys of L form the keeplist, + // and the subkeys form the discardlist. + + + _this.areaKeys = function () { + // The ignore list is for keys that imply lines. (We always add `area=yes` for exceptions) + var ignore = ['barrier', 'highway', 'footway', 'railway', 'junction', 'type']; + var areaKeys = {}; // ignore name-suggestion-index and deprecated presets + + var presets = _this.collection.filter(function (p) { + return !p.suggestion && !p.replacement; + }); // keeplist + + + presets.forEach(function (p) { + var keys = p.tags && Object.keys(p.tags); + var key = keys && keys.length && keys[0]; // pick the first tag + + if (!key) return; + if (ignore.indexOf(key) !== -1) return; + + if (p.geometry.indexOf('area') !== -1) { + // probably an area.. + areaKeys[key] = areaKeys[key] || {}; + } + }); // discardlist + + presets.forEach(function (p) { + var key; + + for (key in p.addTags) { + // examine all addTags to get a better sense of what can be tagged on lines - #6800 + var value = p.addTags[key]; + + if (key in areaKeys && // probably an area... + p.geometry.indexOf('line') !== -1 && // but sometimes a line + value !== '*') { + areaKeys[key][value] = true; + } + } + }); + return areaKeys; + }; + + _this.pointTags = function () { + return _this.collection.reduce(function (pointTags, d) { + // ignore name-suggestion-index, deprecated, and generic presets + if (d.suggestion || d.replacement || d.searchable === false) return pointTags; // only care about the primary tag + + var keys = d.tags && Object.keys(d.tags); + var key = keys && keys.length && keys[0]; // pick the first tag + + if (!key) return pointTags; // if this can be a point + + if (d.geometry.indexOf('point') !== -1) { + pointTags[key] = pointTags[key] || {}; + pointTags[key][d.tags[key]] = true; + } + + return pointTags; + }, {}); + }; + + _this.vertexTags = function () { + return _this.collection.reduce(function (vertexTags, d) { + // ignore name-suggestion-index, deprecated, and generic presets + if (d.suggestion || d.replacement || d.searchable === false) return vertexTags; // only care about the primary tag + + var keys = d.tags && Object.keys(d.tags); + var key = keys && keys.length && keys[0]; // pick the first tag + + if (!key) return vertexTags; // if this can be a vertex + + if (d.geometry.indexOf('vertex') !== -1) { + vertexTags[key] = vertexTags[key] || {}; + vertexTags[key][d.tags[key]] = true; + } + + return vertexTags; + }, {}); + }; + + _this.field = function (id) { + return _fields[id]; + }; + + _this.universal = function () { + return _universal; + }; + + _this.defaults = function (geometry, n, startWithRecents, loc) { + var recents = []; + + if (startWithRecents) { + recents = _this.recent().matchGeometry(geometry).collection.slice(0, 4); + } + + var defaults; + + if (_addablePresetIDs) { + defaults = Array.from(_addablePresetIDs).map(function (id) { + var preset = _this.item(id); + + if (preset && preset.matchGeometry(geometry)) return preset; + return null; + }).filter(Boolean); + } else { + defaults = _defaults[geometry].collection.concat(_this.fallback(geometry)); + } + + var result = presetCollection(utilArrayUniq(recents.concat(defaults)).slice(0, n - 1)); + + if (Array.isArray(loc)) { + var validLocations = _mainLocations.locationsAt(loc); + result.collection = result.collection.filter(function (a) { + return !a.locationSetID || validLocations[a.locationSetID]; + }); + } + + return result; + }; // pass a Set of addable preset ids + + + _this.addablePresetIDs = function (val) { + if (!arguments.length) return _addablePresetIDs; // accept and convert arrays + + if (Array.isArray(val)) val = new Set(val); + _addablePresetIDs = val; + + if (_addablePresetIDs) { + // reset all presets + _this.collection.forEach(function (p) { + // categories aren't addable + if (p.addable) p.addable(_addablePresetIDs.has(p.id)); + }); + } else { + _this.collection.forEach(function (p) { + if (p.addable) p.addable(true); + }); + } + + return _this; + }; + + _this.recent = function () { + return presetCollection(utilArrayUniq(_this.getRecents().map(function (d) { + return d.preset; + }))); + }; + + function RibbonItem(preset, source) { + var item = {}; + item.preset = preset; + item.source = source; + + item.isFavorite = function () { + return item.source === 'favorite'; + }; + + item.isRecent = function () { + return item.source === 'recent'; + }; + + item.matches = function (preset) { + return item.preset.id === preset.id; + }; + + item.minified = function () { + return { + pID: item.preset.id + }; + }; + + return item; + } + + function ribbonItemForMinified(d, source) { + if (d && d.pID) { + var preset = _this.item(d.pID); + + if (!preset) return null; + return RibbonItem(preset, source); + } + + return null; + } + + _this.getGenericRibbonItems = function () { + return ['point', 'line', 'area'].map(function (id) { + return RibbonItem(_this.item(id), 'generic'); + }); + }; + + _this.getAddable = function () { + if (!_addablePresetIDs) return []; + return _addablePresetIDs.map(function (id) { + var preset = _this.item(id); + + if (preset) return RibbonItem(preset, 'addable'); + return null; + }).filter(Boolean); + }; + + function setRecents(items) { + _recents = items; + var minifiedItems = items.map(function (d) { + return d.minified(); + }); + corePreferences('preset_recents', JSON.stringify(minifiedItems)); + dispatch.call('recentsChange'); + } + + _this.getRecents = function () { + if (!_recents) { + // fetch from local storage + _recents = (JSON.parse(corePreferences('preset_recents')) || []).reduce(function (acc, d) { + var item = ribbonItemForMinified(d, 'recent'); + if (item && item.preset.addable()) acc.push(item); + return acc; + }, []); + } + + return _recents; + }; + + _this.addRecent = function (preset, besidePreset, after) { + var recents = _this.getRecents(); + + var beforeItem = _this.recentMatching(besidePreset); + + var toIndex = recents.indexOf(beforeItem); + if (after) toIndex += 1; + var newItem = RibbonItem(preset, 'recent'); + recents.splice(toIndex, 0, newItem); + setRecents(recents); + }; + + _this.removeRecent = function (preset) { + var item = _this.recentMatching(preset); + + if (item) { + var items = _this.getRecents(); + + items.splice(items.indexOf(item), 1); + setRecents(items); + } + }; + + _this.recentMatching = function (preset) { + var items = _this.getRecents(); + + for (var i in items) { + if (items[i].matches(preset)) { + return items[i]; + } + } + + return null; + }; + + _this.moveItem = function (items, fromIndex, toIndex) { + if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 || fromIndex >= items.length || toIndex >= items.length) return null; + items.splice(toIndex, 0, items.splice(fromIndex, 1)[0]); + return items; + }; + + _this.moveRecent = function (item, beforeItem) { + var recents = _this.getRecents(); + + var fromIndex = recents.indexOf(item); + var toIndex = recents.indexOf(beforeItem); + + var items = _this.moveItem(recents, fromIndex, toIndex); + + if (items) setRecents(items); + }; + + _this.setMostRecent = function (preset) { + if (preset.searchable === false) return; + + var items = _this.getRecents(); + + var item = _this.recentMatching(preset); + + if (item) { + items.splice(items.indexOf(item), 1); + } else { + item = RibbonItem(preset, 'recent'); + } // remove the last recent (first in, first out) + + + while (items.length >= MAXRECENTS) { + items.pop(); + } // prepend array + + + items.unshift(item); + setRecents(items); + }; + + function setFavorites(items) { + _favorites = items; + var minifiedItems = items.map(function (d) { + return d.minified(); + }); + corePreferences('preset_favorites', JSON.stringify(minifiedItems)); // call update + + dispatch.call('favoritePreset'); + } + + _this.addFavorite = function (preset, besidePreset, after) { + var favorites = _this.getFavorites(); + + var beforeItem = _this.favoriteMatching(besidePreset); + + var toIndex = favorites.indexOf(beforeItem); + if (after) toIndex += 1; + var newItem = RibbonItem(preset, 'favorite'); + favorites.splice(toIndex, 0, newItem); + setFavorites(favorites); + }; + + _this.toggleFavorite = function (preset) { + var favs = _this.getFavorites(); + + var favorite = _this.favoriteMatching(preset); + + if (favorite) { + favs.splice(favs.indexOf(favorite), 1); + } else { + // only allow 10 favorites + if (favs.length === 10) { + // remove the last favorite (last in, first out) + favs.pop(); + } // append array + + + favs.push(RibbonItem(preset, 'favorite')); + } + + setFavorites(favs); + }; + + _this.removeFavorite = function (preset) { + var item = _this.favoriteMatching(preset); + + if (item) { + var items = _this.getFavorites(); + + items.splice(items.indexOf(item), 1); + setFavorites(items); + } + }; + + _this.getFavorites = function () { + if (!_favorites) { + // fetch from local storage + var rawFavorites = JSON.parse(corePreferences('preset_favorites')); + + if (!rawFavorites) { + rawFavorites = []; + corePreferences('preset_favorites', JSON.stringify(rawFavorites)); + } + + _favorites = rawFavorites.reduce(function (output, d) { + var item = ribbonItemForMinified(d, 'favorite'); + if (item && item.preset.addable()) output.push(item); + return output; + }, []); + } + + return _favorites; + }; + + _this.favoriteMatching = function (preset) { + var favs = _this.getFavorites(); + + for (var index in favs) { + if (favs[index].matches(preset)) { + return favs[index]; + } + } + + return null; + }; + + return utilRebind(_this, dispatch, 'on'); + } + + function utilTagText(entity) { + var obj = entity && entity.tags || {}; + return Object.keys(obj).map(function (k) { + return k + '=' + obj[k]; + }).join(', '); + } + function utilTotalExtent(array, graph) { + var extent = geoExtent(); + var val, entity; + + for (var i = 0; i < array.length; i++) { + val = array[i]; + entity = typeof val === 'string' ? graph.hasEntity(val) : val; + + if (entity) { + extent._extend(entity.extent(graph)); + } + } + + return extent; + } + function utilTagDiff(oldTags, newTags) { + var tagDiff = []; + var keys = utilArrayUnion(Object.keys(oldTags), Object.keys(newTags)).sort(); + keys.forEach(function (k) { + var oldVal = oldTags[k]; + var newVal = newTags[k]; + + if ((oldVal || oldVal === '') && (newVal === undefined || newVal !== oldVal)) { + tagDiff.push({ + type: '-', + key: k, + oldVal: oldVal, + newVal: newVal, + display: '- ' + k + '=' + oldVal + }); + } + + if ((newVal || newVal === '') && (oldVal === undefined || newVal !== oldVal)) { + tagDiff.push({ + type: '+', + key: k, + oldVal: oldVal, + newVal: newVal, + display: '+ ' + k + '=' + newVal + }); + } + }); + return tagDiff; + } + function utilEntitySelector(ids) { + return ids.length ? '.' + ids.join(',.') : 'nothing'; + } // returns an selector to select entity ids for: + // - entityIDs passed in + // - shallow descendant entityIDs for any of those entities that are relations + + function utilEntityOrMemberSelector(ids, graph) { + var seen = new Set(ids); + ids.forEach(collectShallowDescendants); + return utilEntitySelector(Array.from(seen)); + + function collectShallowDescendants(id) { + var entity = graph.hasEntity(id); + if (!entity || entity.type !== 'relation') return; + entity.members.map(function (member) { + return member.id; + }).forEach(function (id) { + seen.add(id); + }); + } + } // returns an selector to select entity ids for: + // - entityIDs passed in + // - deep descendant entityIDs for any of those entities that are relations + + function utilEntityOrDeepMemberSelector(ids, graph) { + return utilEntitySelector(utilEntityAndDeepMemberIDs(ids, graph)); + } // returns an selector to select entity ids for: + // - entityIDs passed in + // - deep descendant entityIDs for any of those entities that are relations + + function utilEntityAndDeepMemberIDs(ids, graph) { + var seen = new Set(); + ids.forEach(collectDeepDescendants); + return Array.from(seen); + + function collectDeepDescendants(id) { + if (seen.has(id)) return; + seen.add(id); + var entity = graph.hasEntity(id); + if (!entity || entity.type !== 'relation') return; + entity.members.map(function (member) { + return member.id; + }).forEach(collectDeepDescendants); // recurse + } + } // returns an selector to select entity ids for: + // - deep descendant entityIDs for any of those entities that are relations + + function utilDeepMemberSelector(ids, graph, skipMultipolgonMembers) { + var idsSet = new Set(ids); + var seen = new Set(); + var returners = new Set(); + ids.forEach(collectDeepDescendants); + return utilEntitySelector(Array.from(returners)); + + function collectDeepDescendants(id) { + if (seen.has(id)) return; + seen.add(id); + + if (!idsSet.has(id)) { + returners.add(id); + } + + var entity = graph.hasEntity(id); + if (!entity || entity.type !== 'relation') return; + if (skipMultipolgonMembers && entity.isMultipolygon()) return; + entity.members.map(function (member) { + return member.id; + }).forEach(collectDeepDescendants); // recurse + } + } // Adds or removes highlight styling for the specified entities + + function utilHighlightEntities(ids, highlighted, context) { + context.surface().selectAll(utilEntityOrDeepMemberSelector(ids, context.graph())).classed('highlighted', highlighted); + } // returns an Array that is the union of: + // - nodes for any nodeIDs passed in + // - child nodes of any wayIDs passed in + // - descendant member and child nodes of relationIDs passed in + + function utilGetAllNodes(ids, graph) { + var seen = new Set(); + var nodes = new Set(); + ids.forEach(collectNodes); + return Array.from(nodes); + + function collectNodes(id) { + if (seen.has(id)) return; + seen.add(id); + var entity = graph.hasEntity(id); + if (!entity) return; + + if (entity.type === 'node') { + nodes.add(entity); + } else if (entity.type === 'way') { + entity.nodes.forEach(collectNodes); + } else { + entity.members.map(function (member) { + return member.id; + }).forEach(collectNodes); // recurse + } + } + } + function utilDisplayName(entity) { + var localizedNameKey = 'name:' + _mainLocalizer.languageCode().toLowerCase(); + var name = entity.tags[localizedNameKey] || entity.tags.name || ''; + if (name) return name; + var tags = { + direction: entity.tags.direction, + from: entity.tags.from, + network: entity.tags.cycle_network || entity.tags.network, + ref: entity.tags.ref, + to: entity.tags.to, + via: entity.tags.via + }; + var keyComponents = []; + + if (tags.network) { + keyComponents.push('network'); + } + + if (tags.ref) { + keyComponents.push('ref'); + } // Routes may need more disambiguation based on direction or destination + + + if (entity.tags.route) { + if (tags.direction) { + keyComponents.push('direction'); + } else if (tags.from && tags.to) { + keyComponents.push('from'); + keyComponents.push('to'); + + if (tags.via) { + keyComponents.push('via'); + } + } + } + + if (keyComponents.length) { + name = _t('inspector.display_name.' + keyComponents.join('_'), tags); + } + + return name; + } + function utilDisplayNameForPath(entity) { + var name = utilDisplayName(entity); + var isFirefox = utilDetect().browser.toLowerCase().indexOf('firefox') > -1; + var isNewChromium = Number(utilDetect().version.split('.')[0]) >= 96.0; + + if (!isFirefox && !isNewChromium && name && rtlRegex.test(name)) { + name = fixRTLTextForSvg(name); + } + + return name; + } + function utilDisplayType(id) { + return { + n: _t('inspector.node'), + w: _t('inspector.way'), + r: _t('inspector.relation') + }[id.charAt(0)]; + } // `utilDisplayLabel` + // Returns a string suitable for display + // By default returns something like name/ref, fallback to preset type, fallback to OSM type + // "Main Street" or "Tertiary Road" + // If `verbose=true`, include both preset name and feature name. + // "Tertiary Road Main Street" + // + + function utilDisplayLabel(entity, graphOrGeometry, verbose) { + var result; + var displayName = utilDisplayName(entity); + var preset = typeof graphOrGeometry === 'string' ? _mainPresetIndex.matchTags(entity.tags, graphOrGeometry) : _mainPresetIndex.match(entity, graphOrGeometry); + var presetName = preset && (preset.suggestion ? preset.subtitle() : preset.name()); + + if (verbose) { + result = [presetName, displayName].filter(Boolean).join(' '); + } else { + result = displayName || presetName; + } // Fallback to the OSM type (node/way/relation) + + + return result || utilDisplayType(entity.id); + } + function utilEntityRoot(entityType) { + return { + node: 'n', + way: 'w', + relation: 'r' + }[entityType]; + } // Returns a single object containing the tags of all the given entities. + // Example: + // { + // highway: 'service', + // service: 'parking_aisle' + // } + // + + // { + // highway: 'service', + // service: 'driveway', + // width: '3' + // } + // = + // { + // highway: 'service', + // service: [ 'driveway', 'parking_aisle' ], + // width: [ '3', undefined ] + // } + + function utilCombinedTags(entityIDs, graph) { + var tags = {}; + var tagCounts = {}; + var allKeys = new Set(); + var entities = entityIDs.map(function (entityID) { + return graph.hasEntity(entityID); + }).filter(Boolean); // gather the aggregate keys + + entities.forEach(function (entity) { + var keys = Object.keys(entity.tags).filter(Boolean); + keys.forEach(function (key) { + allKeys.add(key); + }); + }); + entities.forEach(function (entity) { + allKeys.forEach(function (key) { + var value = entity.tags[key]; // purposely allow `undefined` + + if (!tags.hasOwnProperty(key)) { + // first value, set as raw + tags[key] = value; + } else { + if (!Array.isArray(tags[key])) { + if (tags[key] !== value) { + // first alternate value, replace single value with array + tags[key] = [tags[key], value]; + } + } else { + // type is array + if (tags[key].indexOf(value) === -1) { + // subsequent alternate value, add to array + tags[key].push(value); + } + } + } + + var tagHash = key + '=' + value; + if (!tagCounts[tagHash]) tagCounts[tagHash] = 0; + tagCounts[tagHash] += 1; + }); + }); + + for (var key in tags) { + if (!Array.isArray(tags[key])) continue; // sort values by frequency then alphabetically + + tags[key] = tags[key].sort(function (val1, val2) { + var key = key; // capture + + var count2 = tagCounts[key + '=' + val2]; + var count1 = tagCounts[key + '=' + val1]; + + if (count2 !== count1) { + return count2 - count1; + } + + if (val2 && val1) { + return val1.localeCompare(val2); + } + + return val1 ? 1 : -1; + }); + } + + return tags; + } + function utilStringQs(str) { + var i = 0; // advance past any leading '?' or '#' characters + + while (i < str.length && (str[i] === '?' || str[i] === '#')) { + i++; + } + + str = str.slice(i); + return str.split('&').reduce(function (obj, pair) { + var parts = pair.split('='); + + if (parts.length === 2) { + obj[parts[0]] = null === parts[1] ? '' : decodeURIComponent(parts[1]); + } + + return obj; + }, {}); + } + function utilQsString(obj, noencode) { + // encode everything except special characters used in certain hash parameters: + // "/" in map states, ":", ",", {" and "}" in background + function softEncode(s) { + return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent); + } + + return Object.keys(obj).sort().map(function (key) { + return encodeURIComponent(key) + '=' + (noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key])); + }).join('&'); + } + function utilPrefixDOMProperty(property) { + var prefixes = ['webkit', 'ms', 'moz', 'o']; + var i = -1; + var n = prefixes.length; + var s = document.body; + if (property in s) return property; + property = property.substr(0, 1).toUpperCase() + property.substr(1); + + while (++i < n) { + if (prefixes[i] + property in s) { + return prefixes[i] + property; + } + } + + return false; + } + function utilPrefixCSSProperty(property) { + var prefixes = ['webkit', 'ms', 'Moz', 'O']; + var i = -1; + var n = prefixes.length; + var s = document.body.style; + + if (property.toLowerCase() in s) { + return property.toLowerCase(); + } + + while (++i < n) { + if (prefixes[i] + property in s) { + return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase(); + } + } + + return false; + } + var transformProperty; + function utilSetTransform(el, x, y, scale) { + var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform'); + var translate = utilDetect().opera ? 'translate(' + x + 'px,' + y + 'px)' : 'translate3d(' + x + 'px,' + y + 'px,0)'; + return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : '')); + } // Calculates Levenshtein distance between two strings + // see: https://en.wikipedia.org/wiki/Levenshtein_distance + // first converts the strings to lowercase and replaces diacritic marks with ascii equivalents. + + function utilEditDistance(a, b) { + a = remove$6(a.toLowerCase()); + b = remove$6(b.toLowerCase()); + if (a.length === 0) return b.length; + if (b.length === 0) return a.length; + var matrix = []; + var i, j; + + for (i = 0; i <= b.length; i++) { + matrix[i] = [i]; + } + + for (j = 0; j <= a.length; j++) { + matrix[0][j] = j; + } + + for (i = 1; i <= b.length; i++) { + for (j = 1; j <= a.length; j++) { + if (b.charAt(i - 1) === a.charAt(j - 1)) { + matrix[i][j] = matrix[i - 1][j - 1]; + } else { + matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution + Math.min(matrix[i][j - 1] + 1, // insertion + matrix[i - 1][j] + 1)); // deletion + } + } + } + + return matrix[b.length][a.length]; + } // a d3.mouse-alike which + // 1. Only works on HTML elements, not SVG + // 2. Does not cause style recalculation + + function utilFastMouse(container) { + var rect = container.getBoundingClientRect(); + var rectLeft = rect.left; + var rectTop = rect.top; + var clientLeft = +container.clientLeft; + var clientTop = +container.clientTop; + return function (e) { + return [e.clientX - rectLeft - clientLeft, e.clientY - rectTop - clientTop]; + }; + } + function utilAsyncMap(inputs, func, callback) { + var remaining = inputs.length; + var results = []; + var errors = []; + inputs.forEach(function (d, i) { + func(d, function done(err, data) { + errors[i] = err; + results[i] = data; + remaining--; + if (!remaining) callback(errors, results); + }); + }); + } // wraps an index to an interval [0..length-1] + + function utilWrap(index, length) { + if (index < 0) { + index += Math.ceil(-index / length) * length; + } + + return index % length; + } + /** + * a replacement for functor + * + * @param {*} value any value + * @returns {Function} a function that returns that value or the value if it's a function + */ + + function utilFunctor(value) { + if (typeof value === 'function') return value; + return function () { + return value; + }; + } + function utilNoAuto(selection) { + var isText = selection.size() && selection.node().tagName.toLowerCase() === 'textarea'; + return selection // assign 'new-password' even for non-password fields to prevent browsers (Chrome) ignoring 'off' + .attr('autocomplete', 'new-password').attr('autocorrect', 'off').attr('autocapitalize', 'off').attr('spellcheck', isText ? 'true' : 'false'); + } // https://stackoverflow.com/questions/194846/is-there-any-kind-of-hash-code-function-in-javascript + // https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/ + + function utilHashcode(str) { + var hash = 0; + + if (str.length === 0) { + return hash; + } + + for (var i = 0; i < str.length; i++) { + var _char = str.charCodeAt(i); + + hash = (hash << 5) - hash + _char; + hash = hash & hash; // Convert to 32bit integer + } + + return hash; + } // Returns version of `str` with all runs of special characters replaced by `_`; + // suitable for HTML ids, classes, selectors, etc. + + function utilSafeClassName(str) { + return str.toLowerCase().replace(/[^a-z0-9]+/g, '_'); + } // Returns string based on `val` that is highly unlikely to collide with an id + // used previously or that's present elsewhere in the document. Useful for preventing + // browser-provided autofills or when embedding iD on pages with unknown elements. + + function utilUniqueDomId(val) { + return 'ideditor-' + utilSafeClassName(val.toString()) + '-' + new Date().getTime().toString(); + } // Returns the length of `str` in unicode characters. This can be less than + // `String.length()` since a single unicode character can be composed of multiple + // JavaScript UTF-16 code units. + + function utilUnicodeCharsCount(str) { + // Native ES2015 implementations of `Array.from` split strings into unicode characters + return Array.from(str).length; + } // Returns a new string representing `str` cut from its start to `limit` length + // in unicode characters. Note that this runs the risk of splitting graphemes. + + function utilUnicodeCharsTruncated(str, limit) { + return Array.from(str).slice(0, limit).join(''); + } + + function osmEntity(attrs) { + // For prototypal inheritance. + if (this instanceof osmEntity) return; // Create the appropriate subtype. + + if (attrs && attrs.type) { + return osmEntity[attrs.type].apply(this, arguments); + } else if (attrs && attrs.id) { + return osmEntity[osmEntity.id.type(attrs.id)].apply(this, arguments); + } // Initialize a generic Entity (used only in tests). + + + return new osmEntity().initialize(arguments); + } + + osmEntity.id = function (type) { + return osmEntity.id.fromOSM(type, osmEntity.id.next[type]--); + }; + + osmEntity.id.next = { + changeset: -1, + node: -1, + way: -1, + relation: -1 + }; + + osmEntity.id.fromOSM = function (type, id) { + return type[0] + id; + }; + + osmEntity.id.toOSM = function (id) { + return id.slice(1); + }; + + osmEntity.id.type = function (id) { + return { + 'c': 'changeset', + 'n': 'node', + 'w': 'way', + 'r': 'relation' + }[id[0]]; + }; // A function suitable for use as the second argument to d3.selection#data(). + + + osmEntity.key = function (entity) { + return entity.id + 'v' + (entity.v || 0); + }; + + var _deprecatedTagValuesByKey; + + osmEntity.deprecatedTagValuesByKey = function (dataDeprecated) { + if (!_deprecatedTagValuesByKey) { + _deprecatedTagValuesByKey = {}; + dataDeprecated.forEach(function (d) { + var oldKeys = Object.keys(d.old); + + if (oldKeys.length === 1) { + var oldKey = oldKeys[0]; + var oldValue = d.old[oldKey]; + + if (oldValue !== '*') { + if (!_deprecatedTagValuesByKey[oldKey]) { + _deprecatedTagValuesByKey[oldKey] = [oldValue]; + } else { + _deprecatedTagValuesByKey[oldKey].push(oldValue); + } + } + } + }); + } + + return _deprecatedTagValuesByKey; + }; + + osmEntity.prototype = { + tags: {}, + initialize: function initialize(sources) { + for (var i = 0; i < sources.length; ++i) { + var source = sources[i]; + + for (var prop in source) { + if (Object.prototype.hasOwnProperty.call(source, prop)) { + if (source[prop] === undefined) { + delete this[prop]; + } else { + this[prop] = source[prop]; + } + } + } + } + + if (!this.id && this.type) { + this.id = osmEntity.id(this.type); + } + + if (!this.hasOwnProperty('visible')) { + this.visible = true; + } + + if (debug) { + Object.freeze(this); + Object.freeze(this.tags); + if (this.loc) Object.freeze(this.loc); + if (this.nodes) Object.freeze(this.nodes); + if (this.members) Object.freeze(this.members); + } + + return this; + }, + copy: function copy(resolver, copies) { + if (copies[this.id]) return copies[this.id]; + var copy = osmEntity(this, { + id: undefined, + user: undefined, + version: undefined + }); + copies[this.id] = copy; + return copy; + }, + osmId: function osmId() { + return osmEntity.id.toOSM(this.id); + }, + isNew: function isNew() { + return this.osmId() < 0; + }, + update: function update(attrs) { + return osmEntity(this, attrs, { + v: 1 + (this.v || 0) + }); + }, + mergeTags: function mergeTags(tags) { + var merged = Object.assign({}, this.tags); // shallow copy + + var changed = false; + + for (var k in tags) { + var t1 = merged[k]; + var t2 = tags[k]; + + if (!t1) { + changed = true; + merged[k] = t2; + } else if (t1 !== t2) { + changed = true; + merged[k] = utilUnicodeCharsTruncated(utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(';'), 255 // avoid exceeding character limit; see also services/osm.js -> maxCharsForTagValue() + ); + } + } + + return changed ? this.update({ + tags: merged + }) : this; + }, + intersects: function intersects(extent, resolver) { + return this.extent(resolver).intersects(extent); + }, + hasNonGeometryTags: function hasNonGeometryTags() { + return Object.keys(this.tags).some(function (k) { + return k !== 'area'; + }); + }, + hasParentRelations: function hasParentRelations(resolver) { + return resolver.parentRelations(this).length > 0; + }, + hasInterestingTags: function hasInterestingTags() { + return Object.keys(this.tags).some(osmIsInterestingTag); + }, + isHighwayIntersection: function isHighwayIntersection() { + return false; + }, + isDegenerate: function isDegenerate() { + return true; + }, + deprecatedTags: function deprecatedTags(dataDeprecated) { + var tags = this.tags; // if there are no tags, none can be deprecated + + if (Object.keys(tags).length === 0) return []; + var deprecated = []; + dataDeprecated.forEach(function (d) { + var oldKeys = Object.keys(d.old); + + if (d.replace) { + var hasExistingValues = Object.keys(d.replace).some(function (replaceKey) { + if (!tags[replaceKey] || d.old[replaceKey]) return false; + var replaceValue = d.replace[replaceKey]; + if (replaceValue === '*') return false; + if (replaceValue === tags[replaceKey]) return false; + return true; + }); // don't flag deprecated tags if the upgrade path would overwrite existing data - #7843 + + if (hasExistingValues) return; + } + + var matchesDeprecatedTags = oldKeys.every(function (oldKey) { + if (!tags[oldKey]) return false; + if (d.old[oldKey] === '*') return true; + if (d.old[oldKey] === tags[oldKey]) return true; + var vals = tags[oldKey].split(';').filter(Boolean); + + if (vals.length === 0) { + return false; + } else if (vals.length > 1) { + return vals.indexOf(d.old[oldKey]) !== -1; + } else { + if (tags[oldKey] === d.old[oldKey]) { + if (d.replace && d.old[oldKey] === d.replace[oldKey]) { + var replaceKeys = Object.keys(d.replace); + return !replaceKeys.every(function (replaceKey) { + return tags[replaceKey] === d.replace[replaceKey]; + }); + } else { + return true; + } + } + } + + return false; + }); + + if (matchesDeprecatedTags) { + deprecated.push(d); + } + }); + return deprecated; + } + }; + + function osmLanes(entity) { + if (entity.type !== 'way') return null; + if (!entity.tags.highway) return null; + var tags = entity.tags; + var isOneWay = entity.isOneWay(); + var laneCount = getLaneCount(tags, isOneWay); + var maxspeed = parseMaxspeed(tags); + var laneDirections = parseLaneDirections(tags, isOneWay, laneCount); + var forward = laneDirections.forward; + var backward = laneDirections.backward; + var bothways = laneDirections.bothways; // parse the piped string 'x|y|z' format + + var turnLanes = {}; + turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']); + turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']); + turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']); + var maxspeedLanes = {}; + maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed); + maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed); + maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed); + var psvLanes = {}; + psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']); + psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']); + psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']); + var busLanes = {}; + busLanes.unspecified = parseMiscLanes(tags['bus:lanes']); + busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']); + busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']); + var taxiLanes = {}; + taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']); + taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']); + taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']); + var hovLanes = {}; + hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']); + hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']); + hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']); + var hgvLanes = {}; + hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']); + hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']); + hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']); + var bicyclewayLanes = {}; + bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']); + bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']); + bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']); + var lanesObj = { + forward: [], + backward: [], + unspecified: [] + }; // map forward/backward/unspecified of each lane type to lanesObj + + mapToLanesObj(lanesObj, turnLanes, 'turnLane'); + mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed'); + mapToLanesObj(lanesObj, psvLanes, 'psv'); + mapToLanesObj(lanesObj, busLanes, 'bus'); + mapToLanesObj(lanesObj, taxiLanes, 'taxi'); + mapToLanesObj(lanesObj, hovLanes, 'hov'); + mapToLanesObj(lanesObj, hgvLanes, 'hgv'); + mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway'); + return { + metadata: { + count: laneCount, + oneway: isOneWay, + forward: forward, + backward: backward, + bothways: bothways, + turnLanes: turnLanes, + maxspeed: maxspeed, + maxspeedLanes: maxspeedLanes, + psvLanes: psvLanes, + busLanes: busLanes, + taxiLanes: taxiLanes, + hovLanes: hovLanes, + hgvLanes: hgvLanes, + bicyclewayLanes: bicyclewayLanes + }, + lanes: lanesObj + }; + } + + function getLaneCount(tags, isOneWay) { + var count; + + if (tags.lanes) { + count = parseInt(tags.lanes, 10); + + if (count > 0) { + return count; + } + } + + switch (tags.highway) { + case 'trunk': + case 'motorway': + count = isOneWay ? 2 : 4; + break; + + default: + count = isOneWay ? 1 : 2; + break; + } + + return count; + } + + function parseMaxspeed(tags) { + var maxspeed = tags.maxspeed; + if (!maxspeed) return; + var maxspeedRegex = /^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/; + if (!maxspeedRegex.test(maxspeed)) return; + return parseInt(maxspeed, 10); + } + + function parseLaneDirections(tags, isOneWay, laneCount) { + var forward = parseInt(tags['lanes:forward'], 10); + var backward = parseInt(tags['lanes:backward'], 10); + var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0; + + if (parseInt(tags.oneway, 10) === -1) { + forward = 0; + bothways = 0; + backward = laneCount; + } else if (isOneWay) { + forward = laneCount; + bothways = 0; + backward = 0; + } else if (isNaN(forward) && isNaN(backward)) { + backward = Math.floor((laneCount - bothways) / 2); + forward = laneCount - bothways - backward; + } else if (isNaN(forward)) { + if (backward > laneCount - bothways) { + backward = laneCount - bothways; + } + + forward = laneCount - bothways - backward; + } else if (isNaN(backward)) { + if (forward > laneCount - bothways) { + forward = laneCount - bothways; + } + + backward = laneCount - bothways - forward; + } + + return { + forward: forward, + backward: backward, + bothways: bothways + }; + } + + function parseTurnLanes(tag) { + if (!tag) return; + var validValues = ['left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none']; + return tag.split('|').map(function (s) { + if (s === '') s = 'none'; + return s.split(';').map(function (d) { + return validValues.indexOf(d) === -1 ? 'unknown' : d; + }); + }); + } + + function parseMaxspeedLanes(tag, maxspeed) { + if (!tag) return; + return tag.split('|').map(function (s) { + if (s === 'none') return s; + var m = parseInt(s, 10); + if (s === '' || m === maxspeed) return null; + return isNaN(m) ? 'unknown' : m; + }); + } + + function parseMiscLanes(tag) { + if (!tag) return; + var validValues = ['yes', 'no', 'designated']; + return tag.split('|').map(function (s) { + if (s === '') s = 'no'; + return validValues.indexOf(s) === -1 ? 'unknown' : s; + }); + } + + function parseBicycleWay(tag) { + if (!tag) return; + var validValues = ['yes', 'no', 'designated', 'lane']; + return tag.split('|').map(function (s) { + if (s === '') s = 'no'; + return validValues.indexOf(s) === -1 ? 'unknown' : s; + }); + } + + function mapToLanesObj(lanesObj, data, key) { + if (data.forward) { + data.forward.forEach(function (l, i) { + if (!lanesObj.forward[i]) lanesObj.forward[i] = {}; + lanesObj.forward[i][key] = l; + }); + } + + if (data.backward) { + data.backward.forEach(function (l, i) { + if (!lanesObj.backward[i]) lanesObj.backward[i] = {}; + lanesObj.backward[i][key] = l; + }); + } + + if (data.unspecified) { + data.unspecified.forEach(function (l, i) { + if (!lanesObj.unspecified[i]) lanesObj.unspecified[i] = {}; + lanesObj.unspecified[i][key] = l; + }); + } + } + + function osmWay() { + if (!(this instanceof osmWay)) { + return new osmWay().initialize(arguments); + } else if (arguments.length) { + this.initialize(arguments); + } + } + osmEntity.way = osmWay; + osmWay.prototype = Object.create(osmEntity.prototype); + Object.assign(osmWay.prototype, { + type: 'way', + nodes: [], + copy: function copy(resolver, copies) { + if (copies[this.id]) return copies[this.id]; + var copy = osmEntity.prototype.copy.call(this, resolver, copies); + var nodes = this.nodes.map(function (id) { + return resolver.entity(id).copy(resolver, copies).id; + }); + copy = copy.update({ + nodes: nodes + }); + copies[this.id] = copy; + return copy; + }, + extent: function extent(resolver) { + return resolver["transient"](this, 'extent', function () { + var extent = geoExtent(); + + for (var i = 0; i < this.nodes.length; i++) { + var node = resolver.hasEntity(this.nodes[i]); + + if (node) { + extent._extend(node.extent()); + } + } + + return extent; + }); + }, + first: function first() { + return this.nodes[0]; + }, + last: function last() { + return this.nodes[this.nodes.length - 1]; + }, + contains: function contains(node) { + return this.nodes.indexOf(node) >= 0; + }, + affix: function affix(node) { + if (this.nodes[0] === node) return 'prefix'; + if (this.nodes[this.nodes.length - 1] === node) return 'suffix'; + }, + layer: function layer() { + // explicit layer tag, clamp between -10, 10.. + if (isFinite(this.tags.layer)) { + return Math.max(-10, Math.min(+this.tags.layer, 10)); + } // implied layer tag.. + + + if (this.tags.covered === 'yes') return -1; + if (this.tags.location === 'overground') return 1; + if (this.tags.location === 'underground') return -1; + if (this.tags.location === 'underwater') return -10; + if (this.tags.power === 'line') return 10; + if (this.tags.power === 'minor_line') return 10; + if (this.tags.aerialway) return 10; + if (this.tags.bridge) return 1; + if (this.tags.cutting) return -1; + if (this.tags.tunnel) return -1; + if (this.tags.waterway) return -1; + if (this.tags.man_made === 'pipeline') return -10; + if (this.tags.boundary) return -10; + return 0; + }, + // the approximate width of the line based on its tags except its `width` tag + impliedLineWidthMeters: function impliedLineWidthMeters() { + var averageWidths = { + highway: { + // width is for single lane + motorway: 5, + motorway_link: 5, + trunk: 4.5, + trunk_link: 4.5, + primary: 4, + secondary: 4, + tertiary: 4, + primary_link: 4, + secondary_link: 4, + tertiary_link: 4, + unclassified: 4, + road: 4, + living_street: 4, + bus_guideway: 4, + pedestrian: 4, + residential: 3.5, + service: 3.5, + track: 3, + cycleway: 2.5, + bridleway: 2, + corridor: 2, + steps: 2, + path: 1.5, + footway: 1.5 + }, + railway: { + // width includes ties and rail bed, not just track gauge + rail: 2.5, + light_rail: 2.5, + tram: 2.5, + subway: 2.5, + monorail: 2.5, + funicular: 2.5, + disused: 2.5, + preserved: 2.5, + miniature: 1.5, + narrow_gauge: 1.5 + }, + waterway: { + river: 50, + canal: 25, + stream: 5, + tidal_channel: 5, + fish_pass: 2.5, + drain: 2.5, + ditch: 1.5 + } + }; + + for (var key in averageWidths) { + if (this.tags[key] && averageWidths[key][this.tags[key]]) { + var width = averageWidths[key][this.tags[key]]; + + if (key === 'highway') { + var laneCount = this.tags.lanes && parseInt(this.tags.lanes, 10); + if (!laneCount) laneCount = this.isOneWay() ? 1 : 2; + return width * laneCount; + } + + return width; + } + } + + return null; + }, + isOneWay: function isOneWay() { + // explicit oneway tag.. + var values = { + 'yes': true, + '1': true, + '-1': true, + 'reversible': true, + 'alternating': true, + 'no': false, + '0': false + }; + + if (values[this.tags.oneway] !== undefined) { + return values[this.tags.oneway]; + } // implied oneway tag.. + + + for (var key in this.tags) { + if (key in osmOneWayTags && this.tags[key] in osmOneWayTags[key]) { + return true; + } + } + + return false; + }, + // Some identifier for tag that implies that this way is "sided", + // i.e. the right side is the 'inside' (e.g. the right side of a + // natural=cliff is lower). + sidednessIdentifier: function sidednessIdentifier() { + for (var key in this.tags) { + var value = this.tags[key]; + + if (key in osmRightSideIsInsideTags && value in osmRightSideIsInsideTags[key]) { + if (osmRightSideIsInsideTags[key][value] === true) { + return key; + } else { + // if the map's value is something other than a + // literal true, we should use it so we can + // special case some keys (e.g. natural=coastline + // is handled differently to other naturals). + return osmRightSideIsInsideTags[key][value]; + } + } + } + + return null; + }, + isSided: function isSided() { + if (this.tags.two_sided === 'yes') { + return false; + } + + return this.sidednessIdentifier() !== null; + }, + lanes: function lanes() { + return osmLanes(this); + }, + isClosed: function isClosed() { + return this.nodes.length > 1 && this.first() === this.last(); + }, + isConvex: function isConvex(resolver) { + if (!this.isClosed() || this.isDegenerate()) return null; + var nodes = utilArrayUniq(resolver.childNodes(this)); + var coords = nodes.map(function (n) { + return n.loc; + }); + var curr = 0; + var prev = 0; + + for (var i = 0; i < coords.length; i++) { + var o = coords[(i + 1) % coords.length]; + var a = coords[i]; + var b = coords[(i + 2) % coords.length]; + var res = geoVecCross(a, b, o); + curr = res > 0 ? 1 : res < 0 ? -1 : 0; + + if (curr === 0) { + continue; + } else if (prev && curr !== prev) { + return false; + } + + prev = curr; + } + + return true; + }, + // returns an object with the tag that implies this is an area, if any + tagSuggestingArea: function tagSuggestingArea() { + return osmTagSuggestingArea(this.tags); + }, + isArea: function isArea() { + if (this.tags.area === 'yes') return true; + if (!this.isClosed() || this.tags.area === 'no') return false; + return this.tagSuggestingArea() !== null; + }, + isDegenerate: function isDegenerate() { + return new Set(this.nodes).size < (this.isArea() ? 3 : 2); + }, + areAdjacent: function areAdjacent(n1, n2) { + for (var i = 0; i < this.nodes.length; i++) { + if (this.nodes[i] === n1) { + if (this.nodes[i - 1] === n2) return true; + if (this.nodes[i + 1] === n2) return true; + } + } + + return false; + }, + geometry: function geometry(graph) { + return graph["transient"](this, 'geometry', function () { + return this.isArea() ? 'area' : 'line'; + }); + }, + // returns an array of objects representing the segments between the nodes in this way + segments: function segments(graph) { + function segmentExtent(graph) { + var n1 = graph.hasEntity(this.nodes[0]); + var n2 = graph.hasEntity(this.nodes[1]); + return n1 && n2 && geoExtent([[Math.min(n1.loc[0], n2.loc[0]), Math.min(n1.loc[1], n2.loc[1])], [Math.max(n1.loc[0], n2.loc[0]), Math.max(n1.loc[1], n2.loc[1])]]); + } + + return graph["transient"](this, 'segments', function () { + var segments = []; + + for (var i = 0; i < this.nodes.length - 1; i++) { + segments.push({ + id: this.id + '-' + i, + wayId: this.id, + index: i, + nodes: [this.nodes[i], this.nodes[i + 1]], + extent: segmentExtent + }); + } + + return segments; + }); + }, + // If this way is not closed, append the beginning node to the end of the nodelist to close it. + close: function close() { + if (this.isClosed() || !this.nodes.length) return this; + var nodes = this.nodes.slice(); + nodes = nodes.filter(noRepeatNodes); + nodes.push(nodes[0]); + return this.update({ + nodes: nodes + }); + }, + // If this way is closed, remove any connector nodes from the end of the nodelist to unclose it. + unclose: function unclose() { + if (!this.isClosed()) return this; + var nodes = this.nodes.slice(); + var connector = this.first(); + var i = nodes.length - 1; // remove trailing connectors.. + + while (i > 0 && nodes.length > 1 && nodes[i] === connector) { + nodes.splice(i, 1); + i = nodes.length - 1; + } + + nodes = nodes.filter(noRepeatNodes); + return this.update({ + nodes: nodes + }); + }, + // Adds a node (id) in front of the node which is currently at position index. + // If index is undefined, the node will be added to the end of the way for linear ways, + // or just before the final connecting node for circular ways. + // Consecutive duplicates are eliminated including existing ones. + // Circularity is always preserved when adding a node. + addNode: function addNode(id, index) { + var nodes = this.nodes.slice(); + var isClosed = this.isClosed(); + var max = isClosed ? nodes.length - 1 : nodes.length; + + if (index === undefined) { + index = max; + } + + if (index < 0 || index > max) { + throw new RangeError('index ' + index + ' out of range 0..' + max); + } // If this is a closed way, remove all connector nodes except the first one + // (there may be duplicates) and adjust index if necessary.. + + + if (isClosed) { + var connector = this.first(); // leading connectors.. + + var i = 1; + + while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) { + nodes.splice(i, 1); + if (index > i) index--; + } // trailing connectors.. + + + i = nodes.length - 1; + + while (i > 0 && nodes.length > 1 && nodes[i] === connector) { + nodes.splice(i, 1); + if (index > i) index--; + i = nodes.length - 1; + } + } + + nodes.splice(index, 0, id); + nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed.. + + if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) { + nodes.push(nodes[0]); + } + + return this.update({ + nodes: nodes + }); + }, + // Replaces the node which is currently at position index with the given node (id). + // Consecutive duplicates are eliminated including existing ones. + // Circularity is preserved when updating a node. + updateNode: function updateNode(id, index) { + var nodes = this.nodes.slice(); + var isClosed = this.isClosed(); + var max = nodes.length - 1; + + if (index === undefined || index < 0 || index > max) { + throw new RangeError('index ' + index + ' out of range 0..' + max); + } // If this is a closed way, remove all connector nodes except the first one + // (there may be duplicates) and adjust index if necessary.. + + + if (isClosed) { + var connector = this.first(); // leading connectors.. + + var i = 1; + + while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) { + nodes.splice(i, 1); + if (index > i) index--; + } // trailing connectors.. + + + i = nodes.length - 1; + + while (i > 0 && nodes.length > 1 && nodes[i] === connector) { + nodes.splice(i, 1); + if (index === i) index = 0; // update leading connector instead + + i = nodes.length - 1; + } + } + + nodes.splice(index, 1, id); + nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed.. + + if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) { + nodes.push(nodes[0]); + } + + return this.update({ + nodes: nodes + }); + }, + // Replaces each occurrence of node id needle with replacement. + // Consecutive duplicates are eliminated including existing ones. + // Circularity is preserved. + replaceNode: function replaceNode(needleID, replacementID) { + var nodes = this.nodes.slice(); + var isClosed = this.isClosed(); + + for (var i = 0; i < nodes.length; i++) { + if (nodes[i] === needleID) { + nodes[i] = replacementID; + } + } + + nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed.. + + if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) { + nodes.push(nodes[0]); + } + + return this.update({ + nodes: nodes + }); + }, + // Removes each occurrence of node id. + // Consecutive duplicates are eliminated including existing ones. + // Circularity is preserved. + removeNode: function removeNode(id) { + var nodes = this.nodes.slice(); + var isClosed = this.isClosed(); + nodes = nodes.filter(function (node) { + return node !== id; + }).filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed.. + + if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) { + nodes.push(nodes[0]); + } + + return this.update({ + nodes: nodes + }); + }, + asJXON: function asJXON(changeset_id) { + var r = { + way: { + '@id': this.osmId(), + '@version': this.version || 0, + nd: this.nodes.map(function (id) { + return { + keyAttributes: { + ref: osmEntity.id.toOSM(id) + } + }; + }, this), + tag: Object.keys(this.tags).map(function (k) { + return { + keyAttributes: { + k: k, + v: this.tags[k] + } + }; + }, this) + } + }; + + if (changeset_id) { + r.way['@changeset'] = changeset_id; + } + + return r; + }, + asGeoJSON: function asGeoJSON(resolver) { + return resolver["transient"](this, 'GeoJSON', function () { + var coordinates = resolver.childNodes(this).map(function (n) { + return n.loc; + }); + + if (this.isArea() && this.isClosed()) { + return { + type: 'Polygon', + coordinates: [coordinates] + }; + } else { + return { + type: 'LineString', + coordinates: coordinates + }; + } + }); + }, + area: function area(resolver) { + return resolver["transient"](this, 'area', function () { + var nodes = resolver.childNodes(this); + var json = { + type: 'Polygon', + coordinates: [nodes.map(function (n) { + return n.loc; + })] + }; + + if (!this.isClosed() && nodes.length) { + json.coordinates[0].push(nodes[0].loc); + } + + var area = d3_geoArea(json); // Heuristic for detecting counterclockwise winding order. Assumes + // that OpenStreetMap polygons are not hemisphere-spanning. + + if (area > 2 * Math.PI) { + json.coordinates[0] = json.coordinates[0].reverse(); + area = d3_geoArea(json); + } + + return isNaN(area) ? 0 : area; + }); + } + }); // Filter function to eliminate consecutive duplicates. + + function noRepeatNodes(node, i, arr) { + return i === 0 || node !== arr[i - 1]; + } + + // + // 1. Relation tagged with `type=multipolygon` and no interesting tags. + // 2. One and only one member with the `outer` role. Must be a way with interesting tags. + // 3. No members without a role. + // + // Old multipolygons are no longer recommended but are still rendered as areas by iD. + + function osmOldMultipolygonOuterMemberOfRelation(entity, graph) { + if (entity.type !== 'relation' || !entity.isMultipolygon() || Object.keys(entity.tags).filter(osmIsInterestingTag).length > 1) { + return false; + } + + var outerMember; + + for (var memberIndex in entity.members) { + var member = entity.members[memberIndex]; + + if (!member.role || member.role === 'outer') { + if (outerMember) return false; + if (member.type !== 'way') return false; + if (!graph.hasEntity(member.id)) return false; + outerMember = graph.entity(member.id); + + if (Object.keys(outerMember.tags).filter(osmIsInterestingTag).length === 0) { + return false; + } + } + } + + return outerMember; + } // For fixing up rendering of multipolygons with tags on the outer member. + // https://github.com/openstreetmap/iD/issues/613 + + function osmIsOldMultipolygonOuterMember(entity, graph) { + if (entity.type !== 'way' || Object.keys(entity.tags).filter(osmIsInterestingTag).length === 0) { + return false; + } + + var parents = graph.parentRelations(entity); + if (parents.length !== 1) return false; + var parent = parents[0]; + + if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) { + return false; + } + + var members = parent.members, + member; + + for (var i = 0; i < members.length; i++) { + member = members[i]; + + if (member.id === entity.id && member.role && member.role !== 'outer') { + // Not outer member + return false; + } + + if (member.id !== entity.id && (!member.role || member.role === 'outer')) { + // Not a simple multipolygon + return false; + } + } + + return parent; + } + function osmOldMultipolygonOuterMember(entity, graph) { + if (entity.type !== 'way') return false; + var parents = graph.parentRelations(entity); + if (parents.length !== 1) return false; + var parent = parents[0]; + + if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) { + return false; + } + + var members = parent.members, + member, + outerMember; + + for (var i = 0; i < members.length; i++) { + member = members[i]; + + if (!member.role || member.role === 'outer') { + if (outerMember) return false; // Not a simple multipolygon + + outerMember = member; + } + } + + if (!outerMember) return false; + var outerEntity = graph.hasEntity(outerMember.id); + + if (!outerEntity || !Object.keys(outerEntity.tags).filter(osmIsInterestingTag).length) { + return false; + } + + return outerEntity; + } // Join `toJoin` array into sequences of connecting ways. + // Segments which share identical start/end nodes will, as much as possible, + // be connected with each other. + // + // The return value is a nested array. Each constituent array contains elements + // of `toJoin` which have been determined to connect. + // + // Each consitituent array also has a `nodes` property whose value is an + // ordered array of member nodes, with appropriate order reversal and + // start/end coordinate de-duplication. + // + // Members of `toJoin` must have, at minimum, `type` and `id` properties. + // Thus either an array of `osmWay`s or a relation member array may be used. + // + // If an member is an `osmWay`, its tags and childnodes may be reversed via + // `actionReverse` in the output. + // + // The returned sequences array also has an `actions` array property, containing + // any reversal actions that should be applied to the graph, should the calling + // code attempt to actually join the given ways. + // + // Incomplete members (those for which `graph.hasEntity(element.id)` returns + // false) and non-way members are ignored. + // + + function osmJoinWays(toJoin, graph) { + function resolve(member) { + return graph.childNodes(graph.entity(member.id)); + } + + function reverse(item) { + var action = actionReverse(item.id, { + reverseOneway: true + }); + sequences.actions.push(action); + return item instanceof osmWay ? action(graph).entity(item.id) : item; + } // make a copy containing only the items to join + + + toJoin = toJoin.filter(function (member) { + return member.type === 'way' && graph.hasEntity(member.id); + }); // Are the things we are joining relation members or `osmWays`? + // If `osmWays`, skip the "prefer a forward path" code below (see #4872) + + var i; + var joinAsMembers = true; + + for (i = 0; i < toJoin.length; i++) { + if (toJoin[i] instanceof osmWay) { + joinAsMembers = false; + break; + } + } + + var sequences = []; + sequences.actions = []; + + while (toJoin.length) { + // start a new sequence + var item = toJoin.shift(); + var currWays = [item]; + var currNodes = resolve(item).slice(); // add to it + + while (toJoin.length) { + var start = currNodes[0]; + var end = currNodes[currNodes.length - 1]; + var fn = null; + var nodes = null; // Find the next way/member to join. + + for (i = 0; i < toJoin.length; i++) { + item = toJoin[i]; + nodes = resolve(item); // (for member ordering only, not way ordering - see #4872) + // Strongly prefer to generate a forward path that preserves the order + // of the members array. For multipolygons and most relations, member + // order does not matter - but for routes, it does. (see #4589) + // If we started this sequence backwards (i.e. next member way attaches to + // the start node and not the end node), reverse the initial way before continuing. + + if (joinAsMembers && currWays.length === 1 && nodes[0] !== end && nodes[nodes.length - 1] !== end && (nodes[nodes.length - 1] === start || nodes[0] === start)) { + currWays[0] = reverse(currWays[0]); + currNodes.reverse(); + start = currNodes[0]; + end = currNodes[currNodes.length - 1]; + } + + if (nodes[0] === end) { + fn = currNodes.push; // join to end + + nodes = nodes.slice(1); + break; + } else if (nodes[nodes.length - 1] === end) { + fn = currNodes.push; // join to end + + nodes = nodes.slice(0, -1).reverse(); + item = reverse(item); + break; + } else if (nodes[nodes.length - 1] === start) { + fn = currNodes.unshift; // join to beginning + + nodes = nodes.slice(0, -1); + break; + } else if (nodes[0] === start) { + fn = currNodes.unshift; // join to beginning + + nodes = nodes.slice(1).reverse(); + item = reverse(item); + break; + } else { + fn = nodes = null; + } + } + + if (!nodes) { + // couldn't find a joinable way/member + break; + } + + fn.apply(currWays, [item]); + fn.apply(currNodes, nodes); + toJoin.splice(i, 1); + } + + currWays.nodes = currNodes; + sequences.push(currWays); + } + + return sequences; + } + + function actionAddMember(relationId, member, memberIndex, insertPair) { + return function action(graph) { + var relation = graph.entity(relationId); // There are some special rules for Public Transport v2 routes. + + var isPTv2 = /stop|platform/.test(member.role); + + if ((isNaN(memberIndex) || insertPair) && member.type === 'way' && !isPTv2) { + // Try to perform sensible inserts based on how the ways join together + graph = addWayMember(relation, graph); + } else { + // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes + // Stops and Platforms for PTv2 should be ordered first. + // hack: We do not currently have the ability to place them in the exactly correct order. + if (isPTv2 && isNaN(memberIndex)) { + memberIndex = 0; + } + + graph = graph.replace(relation.addMember(member, memberIndex)); + } + + return graph; + }; // Add a way member into the relation "wherever it makes sense". + // In this situation we were not supplied a memberIndex. + + function addWayMember(relation, graph) { + var groups, tempWay, item, i, j, k; // remove PTv2 stops and platforms before doing anything. + + var PTv2members = []; + var members = []; + + for (i = 0; i < relation.members.length; i++) { + var m = relation.members[i]; + + if (/stop|platform/.test(m.role)) { + PTv2members.push(m); + } else { + members.push(m); + } + } + + relation = relation.update({ + members: members + }); + + if (insertPair) { + // We're adding a member that must stay paired with an existing member. + // (This feature is used by `actionSplit`) + // + // This is tricky because the members may exist multiple times in the + // member list, and with different A-B/B-A ordering and different roles. + // (e.g. a bus route that loops out and back - #4589). + // + // Replace the existing member with a temporary way, + // so that `osmJoinWays` can treat the pair like a single way. + tempWay = osmWay({ + id: 'wTemp', + nodes: insertPair.nodes + }); + graph = graph.replace(tempWay); + var tempMember = { + id: tempWay.id, + type: 'way', + role: member.role + }; + var tempRelation = relation.replaceMember({ + id: insertPair.originalID + }, tempMember, true); + groups = utilArrayGroupBy(tempRelation.members, 'type'); + groups.way = groups.way || []; + } else { + // Add the member anywhere, one time. Just push and let `osmJoinWays` decide where to put it. + groups = utilArrayGroupBy(relation.members, 'type'); + groups.way = groups.way || []; + groups.way.push(member); + } + + members = withIndex(groups.way); + var joined = osmJoinWays(members, graph); // `joined` might not contain all of the way members, + // But will contain only the completed (downloaded) members + + for (i = 0; i < joined.length; i++) { + var segment = joined[i]; + var nodes = segment.nodes.slice(); + var startIndex = segment[0].index; // j = array index in `members` where this segment starts + + for (j = 0; j < members.length; j++) { + if (members[j].index === startIndex) { + break; + } + } // k = each member in segment + + + for (k = 0; k < segment.length; k++) { + item = segment[k]; + var way = graph.entity(item.id); // If this is a paired item, generate members in correct order and role + + if (tempWay && item.id === tempWay.id) { + if (nodes[0].id === insertPair.nodes[0]) { + item.pair = [{ + id: insertPair.originalID, + type: 'way', + role: item.role + }, { + id: insertPair.insertedID, + type: 'way', + role: item.role + }]; + } else { + item.pair = [{ + id: insertPair.insertedID, + type: 'way', + role: item.role + }, { + id: insertPair.originalID, + type: 'way', + role: item.role + }]; + } + } // reorder `members` if necessary + + + if (k > 0) { + if (j + k >= members.length || item.index !== members[j + k].index) { + moveMember(members, item.index, j + k); + } + } + + nodes.splice(0, way.nodes.length - 1); + } + } + + if (tempWay) { + graph = graph.remove(tempWay); + } // Final pass: skip dead items, split pairs, remove index properties + + + var wayMembers = []; + + for (i = 0; i < members.length; i++) { + item = members[i]; + if (item.index === -1) continue; + + if (item.pair) { + wayMembers.push(item.pair[0]); + wayMembers.push(item.pair[1]); + } else { + wayMembers.push(utilObjectOmit(item, ['index'])); + } + } // Put stops and platforms first, then nodes, ways, relations + // This is recommended for Public Transport v2 routes: + // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes + + + var newMembers = PTv2members.concat(groups.node || [], wayMembers, groups.relation || []); + return graph.replace(relation.update({ + members: newMembers + })); // `moveMember()` changes the `members` array in place by splicing + // the item with `.index = findIndex` to where it belongs, + // and marking the old position as "dead" with `.index = -1` + // + // j=5, k=0 jk + // segment 5 4 7 6 + // members 0 1 2 3 4 5 6 7 8 9 keep 5 in j+k + // + // j=5, k=1 j k + // segment 5 4 7 6 + // members 0 1 2 3 4 5 6 7 8 9 move 4 to j+k + // members 0 1 2 3 x 5 4 6 7 8 9 moved + // + // j=5, k=2 j k + // segment 5 4 7 6 + // members 0 1 2 3 x 5 4 6 7 8 9 move 7 to j+k + // members 0 1 2 3 x 5 4 7 6 x 8 9 moved + // + // j=5, k=3 j k + // segment 5 4 7 6 + // members 0 1 2 3 x 5 4 7 6 x 8 9 keep 6 in j+k + // + + function moveMember(arr, findIndex, toIndex) { + var i; + + for (i = 0; i < arr.length; i++) { + if (arr[i].index === findIndex) { + break; + } + } + + var item = Object.assign({}, arr[i]); // shallow copy + + arr[i].index = -1; // mark as dead + + item.index = toIndex; + arr.splice(toIndex, 0, item); + } // This is the same as `Relation.indexedMembers`, + // Except we don't want to index all the members, only the ways + + + function withIndex(arr) { + var result = new Array(arr.length); + + for (var i = 0; i < arr.length; i++) { + result[i] = Object.assign({}, arr[i]); // shallow copy + + result[i].index = i; + } + + return result; + } + } + } + + function actionAddMidpoint(midpoint, node) { + return function (graph) { + graph = graph.replace(node.move(midpoint.loc)); + var parents = utilArrayIntersection(graph.parentWays(graph.entity(midpoint.edge[0])), graph.parentWays(graph.entity(midpoint.edge[1]))); + parents.forEach(function (way) { + for (var i = 0; i < way.nodes.length - 1; i++) { + if (geoEdgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) { + graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1)); // Add only one midpoint on doubled-back segments, + // turning them into self-intersections. + + return; + } + } + }); + return graph; + }; + } + + // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as + function actionAddVertex(wayId, nodeId, index) { + return function (graph) { + return graph.replace(graph.entity(wayId).addNode(nodeId, index)); + }; + } + + function actionChangeMember(relationId, member, memberIndex) { + return function (graph) { + return graph.replace(graph.entity(relationId).updateMember(member, memberIndex)); + }; + } + + function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefaults) { + return function action(graph) { + var entity = graph.entity(entityID); + var geometry = entity.geometry(graph); + var tags = entity.tags; // preserve tags that the new preset might care about, if any + + if (oldPreset) tags = oldPreset.unsetTags(tags, geometry, newPreset && newPreset.addTags ? Object.keys(newPreset.addTags) : null); + if (newPreset) tags = newPreset.setTags(tags, geometry, skipFieldDefaults); + return graph.replace(entity.update({ + tags: tags + })); + }; + } + + function actionChangeTags(entityId, tags) { + return function (graph) { + var entity = graph.entity(entityId); + return graph.replace(entity.update({ + tags: tags + })); + }; + } + + function osmNode() { + if (!(this instanceof osmNode)) { + return new osmNode().initialize(arguments); + } else if (arguments.length) { + this.initialize(arguments); + } + } + osmEntity.node = osmNode; + osmNode.prototype = Object.create(osmEntity.prototype); + Object.assign(osmNode.prototype, { + type: 'node', + loc: [9999, 9999], + extent: function extent() { + return new geoExtent(this.loc); + }, + geometry: function geometry(graph) { + return graph["transient"](this, 'geometry', function () { + return graph.isPoi(this) ? 'point' : 'vertex'; + }); + }, + move: function move(loc) { + return this.update({ + loc: loc + }); + }, + isDegenerate: function isDegenerate() { + return !(Array.isArray(this.loc) && this.loc.length === 2 && this.loc[0] >= -180 && this.loc[0] <= 180 && this.loc[1] >= -90 && this.loc[1] <= 90); + }, + // Inspect tags and geometry to determine which direction(s) this node/vertex points + directions: function directions(resolver, projection) { + var val; + var i; // which tag to use? + + if (this.isHighwayIntersection(resolver) && (this.tags.stop || '').toLowerCase() === 'all') { + // all-way stop tag on a highway intersection + val = 'all'; + } else { + // generic direction tag + val = (this.tags.direction || '').toLowerCase(); // better suffix-style direction tag + + var re = /:direction$/i; + var keys = Object.keys(this.tags); + + for (i = 0; i < keys.length; i++) { + if (re.test(keys[i])) { + val = this.tags[keys[i]].toLowerCase(); + break; + } + } + } + + if (val === '') return []; + var cardinal = { + north: 0, + n: 0, + northnortheast: 22, + nne: 22, + northeast: 45, + ne: 45, + eastnortheast: 67, + ene: 67, + east: 90, + e: 90, + eastsoutheast: 112, + ese: 112, + southeast: 135, + se: 135, + southsoutheast: 157, + sse: 157, + south: 180, + s: 180, + southsouthwest: 202, + ssw: 202, + southwest: 225, + sw: 225, + westsouthwest: 247, + wsw: 247, + west: 270, + w: 270, + westnorthwest: 292, + wnw: 292, + northwest: 315, + nw: 315, + northnorthwest: 337, + nnw: 337 + }; + var values = val.split(';'); + var results = []; + values.forEach(function (v) { + // swap cardinal for numeric directions + if (cardinal[v] !== undefined) { + v = cardinal[v]; + } // numeric direction - just add to results + + + if (v !== '' && !isNaN(+v)) { + results.push(+v); + return; + } // string direction - inspect parent ways + + + var lookBackward = this.tags['traffic_sign:backward'] || v === 'backward' || v === 'both' || v === 'all'; + var lookForward = this.tags['traffic_sign:forward'] || v === 'forward' || v === 'both' || v === 'all'; + if (!lookForward && !lookBackward) return; + var nodeIds = {}; + resolver.parentWays(this).forEach(function (parent) { + var nodes = parent.nodes; + + for (i = 0; i < nodes.length; i++) { + if (nodes[i] === this.id) { + // match current entity + if (lookForward && i > 0) { + nodeIds[nodes[i - 1]] = true; // look back to prev node + } + + if (lookBackward && i < nodes.length - 1) { + nodeIds[nodes[i + 1]] = true; // look ahead to next node + } + } + } + }, this); + Object.keys(nodeIds).forEach(function (nodeId) { + // +90 because geoAngle returns angle from X axis, not Y (north) + results.push(geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI) + 90); + }, this); + }, this); + return utilArrayUniq(results); + }, + isCrossing: function isCrossing() { + return this.tags.highway === 'crossing' || this.tags.railway && this.tags.railway.indexOf('crossing') !== -1; + }, + isEndpoint: function isEndpoint(resolver) { + return resolver["transient"](this, 'isEndpoint', function () { + var id = this.id; + return resolver.parentWays(this).filter(function (parent) { + return !parent.isClosed() && !!parent.affix(id); + }).length > 0; + }); + }, + isConnected: function isConnected(resolver) { + return resolver["transient"](this, 'isConnected', function () { + var parents = resolver.parentWays(this); + + if (parents.length > 1) { + // vertex is connected to multiple parent ways + for (var i in parents) { + if (parents[i].geometry(resolver) === 'line' && parents[i].hasInterestingTags()) return true; + } + } else if (parents.length === 1) { + var way = parents[0]; + var nodes = way.nodes.slice(); + + if (way.isClosed()) { + nodes.pop(); + } // ignore connecting node if closed + // return true if vertex appears multiple times (way is self intersecting) + + + return nodes.indexOf(this.id) !== nodes.lastIndexOf(this.id); + } + + return false; + }); + }, + parentIntersectionWays: function parentIntersectionWays(resolver) { + return resolver["transient"](this, 'parentIntersectionWays', function () { + return resolver.parentWays(this).filter(function (parent) { + return (parent.tags.highway || parent.tags.waterway || parent.tags.railway || parent.tags.aeroway) && parent.geometry(resolver) === 'line'; + }); + }); + }, + isIntersection: function isIntersection(resolver) { + return this.parentIntersectionWays(resolver).length > 1; + }, + isHighwayIntersection: function isHighwayIntersection(resolver) { + return resolver["transient"](this, 'isHighwayIntersection', function () { + return resolver.parentWays(this).filter(function (parent) { + return parent.tags.highway && parent.geometry(resolver) === 'line'; + }).length > 1; + }); + }, + isOnAddressLine: function isOnAddressLine(resolver) { + return resolver["transient"](this, 'isOnAddressLine', function () { + return resolver.parentWays(this).filter(function (parent) { + return parent.tags.hasOwnProperty('addr:interpolation') && parent.geometry(resolver) === 'line'; + }).length > 0; + }); + }, + asJXON: function asJXON(changeset_id) { + var r = { + node: { + '@id': this.osmId(), + '@lon': this.loc[0], + '@lat': this.loc[1], + '@version': this.version || 0, + tag: Object.keys(this.tags).map(function (k) { + return { + keyAttributes: { + k: k, + v: this.tags[k] + } + }; + }, this) + } + }; + if (changeset_id) r.node['@changeset'] = changeset_id; + return r; + }, + asGeoJSON: function asGeoJSON() { + return { + type: 'Point', + coordinates: this.loc + }; + } + }); + + function actionCircularize(wayId, projection, maxAngle) { + maxAngle = (maxAngle || 20) * Math.PI / 180; + + var action = function action(graph, t) { + if (t === null || !isFinite(t)) t = 1; + t = Math.min(Math.max(+t, 0), 1); + var way = graph.entity(wayId); + var origNodes = {}; + graph.childNodes(way).forEach(function (node) { + if (!origNodes[node.id]) origNodes[node.id] = node; + }); + + if (!way.isConvex(graph)) { + graph = action.makeConvex(graph); + } + + var nodes = utilArrayUniq(graph.childNodes(way)); + var keyNodes = nodes.filter(function (n) { + return graph.parentWays(n).length !== 1; + }); + var points = nodes.map(function (n) { + return projection(n.loc); + }); + var keyPoints = keyNodes.map(function (n) { + return projection(n.loc); + }); + var centroid = points.length === 2 ? geoVecInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points); + var radius = d3_median(points, function (p) { + return geoVecLength(centroid, p); + }); + var sign = d3_polygonArea(points) > 0 ? 1 : -1; + var ids, i, j, k; // we need at least two key nodes for the algorithm to work + + if (!keyNodes.length) { + keyNodes = [nodes[0]]; + keyPoints = [points[0]]; + } + + if (keyNodes.length === 1) { + var index = nodes.indexOf(keyNodes[0]); + var oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length); + keyNodes.push(nodes[oppositeIndex]); + keyPoints.push(points[oppositeIndex]); + } // key points and nodes are those connected to the ways, + // they are projected onto the circle, in between nodes are moved + // to constant intervals between key nodes, extra in between nodes are + // added if necessary. + + + for (i = 0; i < keyPoints.length; i++) { + var nextKeyNodeIndex = (i + 1) % keyNodes.length; + var startNode = keyNodes[i]; + var endNode = keyNodes[nextKeyNodeIndex]; + var startNodeIndex = nodes.indexOf(startNode); + var endNodeIndex = nodes.indexOf(endNode); + var numberNewPoints = -1; + var indexRange = endNodeIndex - startNodeIndex; + var nearNodes = {}; + var inBetweenNodes = []; + var startAngle, endAngle, totalAngle, eachAngle; + var angle, loc, node, origNode; + + if (indexRange < 0) { + indexRange += nodes.length; + } // position this key node + + + var distance = geoVecLength(centroid, keyPoints[i]) || 1e-4; + keyPoints[i] = [centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius, centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius]; + loc = projection.invert(keyPoints[i]); + node = keyNodes[i]; + origNode = origNodes[node.id]; + node = node.move(geoVecInterp(origNode.loc, loc, t)); + graph = graph.replace(node); // figure out the between delta angle we want to match to + + startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]); + endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]); + totalAngle = endAngle - startAngle; // detects looping around -pi/pi + + if (totalAngle * sign > 0) { + totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle)); + } + + do { + numberNewPoints++; + eachAngle = totalAngle / (indexRange + numberNewPoints); + } while (Math.abs(eachAngle) > maxAngle); // move existing nodes + + + for (j = 1; j < indexRange; j++) { + angle = startAngle + j * eachAngle; + loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]); + node = nodes[(j + startNodeIndex) % nodes.length]; + origNode = origNodes[node.id]; + nearNodes[node.id] = angle; + node = node.move(geoVecInterp(origNode.loc, loc, t)); + graph = graph.replace(node); + } // add new in between nodes if necessary + + + for (j = 0; j < numberNewPoints; j++) { + angle = startAngle + (indexRange + j) * eachAngle; + loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]); // choose a nearnode to use as the original + + var min = Infinity; + + for (var nodeId in nearNodes) { + var nearAngle = nearNodes[nodeId]; + var dist = Math.abs(nearAngle - angle); + + if (dist < min) { + min = dist; + origNode = origNodes[nodeId]; + } + } + + node = osmNode({ + loc: geoVecInterp(origNode.loc, loc, t) + }); + graph = graph.replace(node); + nodes.splice(endNodeIndex + j, 0, node); + inBetweenNodes.push(node.id); + } // Check for other ways that share these keyNodes.. + // If keyNodes are adjacent in both ways, + // we can add inBetweenNodes to that shared way too.. + + + if (indexRange === 1 && inBetweenNodes.length) { + var startIndex1 = way.nodes.lastIndexOf(startNode.id); + var endIndex1 = way.nodes.lastIndexOf(endNode.id); + var wayDirection1 = endIndex1 - startIndex1; + + if (wayDirection1 < -1) { + wayDirection1 = 1; + } + + var parentWays = graph.parentWays(keyNodes[i]); + + for (j = 0; j < parentWays.length; j++) { + var sharedWay = parentWays[j]; + if (sharedWay === way) continue; + + if (sharedWay.areAdjacent(startNode.id, endNode.id)) { + var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id); + var endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id); + var wayDirection2 = endIndex2 - startIndex2; + var insertAt = endIndex2; + + if (wayDirection2 < -1) { + wayDirection2 = 1; + } + + if (wayDirection1 !== wayDirection2) { + inBetweenNodes.reverse(); + insertAt = startIndex2; + } + + for (k = 0; k < inBetweenNodes.length; k++) { + sharedWay = sharedWay.addNode(inBetweenNodes[k], insertAt + k); + } + + graph = graph.replace(sharedWay); + } + } + } + } // update the way to have all the new nodes + + + ids = nodes.map(function (n) { + return n.id; + }); + ids.push(ids[0]); + way = way.update({ + nodes: ids + }); + graph = graph.replace(way); + return graph; + }; + + action.makeConvex = function (graph) { + var way = graph.entity(wayId); + var nodes = utilArrayUniq(graph.childNodes(way)); + var points = nodes.map(function (n) { + return projection(n.loc); + }); + var sign = d3_polygonArea(points) > 0 ? 1 : -1; + var hull = d3_polygonHull(points); + var i, j; // D3 convex hulls go counterclockwise.. + + if (sign === -1) { + nodes.reverse(); + points.reverse(); + } + + for (i = 0; i < hull.length - 1; i++) { + var startIndex = points.indexOf(hull[i]); + var endIndex = points.indexOf(hull[i + 1]); + var indexRange = endIndex - startIndex; + + if (indexRange < 0) { + indexRange += nodes.length; + } // move interior nodes to the surface of the convex hull.. + + + for (j = 1; j < indexRange; j++) { + var point = geoVecInterp(hull[i], hull[i + 1], j / indexRange); + var node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point)); + graph = graph.replace(node); + } + } + + return graph; + }; + + action.disabled = function (graph) { + if (!graph.entity(wayId).isClosed()) { + return 'not_closed'; + } //disable when already circular + + + var way = graph.entity(wayId); + var nodes = utilArrayUniq(graph.childNodes(way)); + var points = nodes.map(function (n) { + return projection(n.loc); + }); + var hull = d3_polygonHull(points); + var epsilonAngle = Math.PI / 180; + + if (hull.length !== points.length || hull.length < 3) { + return false; + } + + var centroid = d3_polygonCentroid(points); + var radius = geoVecLengthSquare(centroid, points[0]); + var i, actualPoint; // compare distances between centroid and points + + for (i = 0; i < hull.length; i++) { + actualPoint = hull[i]; + var actualDist = geoVecLengthSquare(actualPoint, centroid); + var diff = Math.abs(actualDist - radius); //compare distances with epsilon-error (5%) + + if (diff > 0.05 * radius) { + return false; + } + } //check if central angles are smaller than maxAngle + + + for (i = 0; i < hull.length; i++) { + actualPoint = hull[i]; + var nextPoint = hull[(i + 1) % hull.length]; + var startAngle = Math.atan2(actualPoint[1] - centroid[1], actualPoint[0] - centroid[0]); + var endAngle = Math.atan2(nextPoint[1] - centroid[1], nextPoint[0] - centroid[0]); + var angle = endAngle - startAngle; + + if (angle < 0) { + angle = -angle; + } + + if (angle > Math.PI) { + angle = 2 * Math.PI - angle; + } + + if (angle > maxAngle + epsilonAngle) { + return false; + } + } + + return 'already_circular'; + }; + + action.transitionable = true; + return action; + } + + function actionDeleteWay(wayID) { + function canDeleteNode(node, graph) { + // don't delete nodes still attached to ways or relations + if (graph.parentWays(node).length || graph.parentRelations(node).length) return false; + var geometries = osmNodeGeometriesForTags(node.tags); // don't delete if this node can be a standalone point + + if (geometries.point) return false; // delete if this node only be a vertex + + if (geometries.vertex) return true; // iD doesn't know if this should be a point or vertex, + // so only delete if there are no interesting tags + + return !node.hasInterestingTags(); + } + + var action = function action(graph) { + var way = graph.entity(wayID); + graph.parentRelations(way).forEach(function (parent) { + parent = parent.removeMembersWithID(wayID); + graph = graph.replace(parent); + + if (parent.isDegenerate()) { + graph = actionDeleteRelation(parent.id)(graph); + } + }); + new Set(way.nodes).forEach(function (nodeID) { + graph = graph.replace(way.removeNode(nodeID)); + var node = graph.entity(nodeID); + + if (canDeleteNode(node, graph)) { + graph = graph.remove(node); + } + }); + return graph.remove(way); + }; + + return action; + } + + function actionDeleteMultiple(ids) { + var actions = { + way: actionDeleteWay, + node: actionDeleteNode, + relation: actionDeleteRelation + }; + + var action = function action(graph) { + ids.forEach(function (id) { + if (graph.hasEntity(id)) { + // It may have been deleted already. + graph = actions[graph.entity(id).type](id)(graph); + } + }); + return graph; + }; + + return action; + } + + function actionDeleteRelation(relationID, allowUntaggedMembers) { + function canDeleteEntity(entity, graph) { + return !graph.parentWays(entity).length && !graph.parentRelations(entity).length && !entity.hasInterestingTags() && !allowUntaggedMembers; + } + + var action = function action(graph) { + var relation = graph.entity(relationID); + graph.parentRelations(relation).forEach(function (parent) { + parent = parent.removeMembersWithID(relationID); + graph = graph.replace(parent); + + if (parent.isDegenerate()) { + graph = actionDeleteRelation(parent.id)(graph); + } + }); + var memberIDs = utilArrayUniq(relation.members.map(function (m) { + return m.id; + })); + memberIDs.forEach(function (memberID) { + graph = graph.replace(relation.removeMembersWithID(memberID)); + var entity = graph.entity(memberID); + + if (canDeleteEntity(entity, graph)) { + graph = actionDeleteMultiple([memberID])(graph); + } + }); + return graph.remove(relation); + }; + + return action; + } + + function actionDeleteNode(nodeId) { + var action = function action(graph) { + var node = graph.entity(nodeId); + graph.parentWays(node).forEach(function (parent) { + parent = parent.removeNode(nodeId); + graph = graph.replace(parent); + + if (parent.isDegenerate()) { + graph = actionDeleteWay(parent.id)(graph); + } + }); + graph.parentRelations(node).forEach(function (parent) { + parent = parent.removeMembersWithID(nodeId); + graph = graph.replace(parent); + + if (parent.isDegenerate()) { + graph = actionDeleteRelation(parent.id)(graph); + } + }); + return graph.remove(node); + }; + + return action; + } + + // + // First choose a node to be the survivor, with preference given + // to an existing (not new) node. + // + // Tags and relation memberships of of non-surviving nodes are merged + // to the survivor. + // + // This is the inverse of `iD.actionDisconnect`. + // + // Reference: + // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as + // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java + // + + function actionConnect(nodeIDs) { + var action = function action(graph) { + var survivor; + var node; + var parents; + var i, j; // Choose a survivor node, prefer an existing (not new) node - #4974 + + for (i = 0; i < nodeIDs.length; i++) { + survivor = graph.entity(nodeIDs[i]); + if (survivor.version) break; // found one + } // Replace all non-surviving nodes with the survivor and merge tags. + + + for (i = 0; i < nodeIDs.length; i++) { + node = graph.entity(nodeIDs[i]); + if (node.id === survivor.id) continue; + parents = graph.parentWays(node); + + for (j = 0; j < parents.length; j++) { + graph = graph.replace(parents[j].replaceNode(node.id, survivor.id)); + } + + parents = graph.parentRelations(node); + + for (j = 0; j < parents.length; j++) { + graph = graph.replace(parents[j].replaceMember(node, survivor)); + } + + survivor = survivor.mergeTags(node.tags); + graph = actionDeleteNode(node.id)(graph); + } + + graph = graph.replace(survivor); // find and delete any degenerate ways created by connecting adjacent vertices + + parents = graph.parentWays(survivor); + + for (i = 0; i < parents.length; i++) { + if (parents[i].isDegenerate()) { + graph = actionDeleteWay(parents[i].id)(graph); + } + } + + return graph; + }; + + action.disabled = function (graph) { + var seen = {}; + var restrictionIDs = []; + var survivor; + var node, way; + var relations, relation, role; + var i, j, k; // Choose a survivor node, prefer an existing (not new) node - #4974 + + for (i = 0; i < nodeIDs.length; i++) { + survivor = graph.entity(nodeIDs[i]); + if (survivor.version) break; // found one + } // 1. disable if the nodes being connected have conflicting relation roles + + + for (i = 0; i < nodeIDs.length; i++) { + node = graph.entity(nodeIDs[i]); + relations = graph.parentRelations(node); + + for (j = 0; j < relations.length; j++) { + relation = relations[j]; + role = relation.memberById(node.id).role || ''; // if this node is a via node in a restriction, remember for later + + if (relation.hasFromViaTo()) { + restrictionIDs.push(relation.id); + } + + if (seen[relation.id] !== undefined && seen[relation.id] !== role) { + return 'relation'; + } else { + seen[relation.id] = role; + } + } + } // gather restrictions for parent ways + + + for (i = 0; i < nodeIDs.length; i++) { + node = graph.entity(nodeIDs[i]); + var parents = graph.parentWays(node); + + for (j = 0; j < parents.length; j++) { + var parent = parents[j]; + relations = graph.parentRelations(parent); + + for (k = 0; k < relations.length; k++) { + relation = relations[k]; + + if (relation.hasFromViaTo()) { + restrictionIDs.push(relation.id); + } + } + } + } // test restrictions + + + restrictionIDs = utilArrayUniq(restrictionIDs); + + for (i = 0; i < restrictionIDs.length; i++) { + relation = graph.entity(restrictionIDs[i]); + if (!relation.isComplete(graph)) continue; + var memberWays = relation.members.filter(function (m) { + return m.type === 'way'; + }).map(function (m) { + return graph.entity(m.id); + }); + memberWays = utilArrayUniq(memberWays); + var f = relation.memberByRole('from'); + var t = relation.memberByRole('to'); + var isUturn = f.id === t.id; // 2a. disable if connection would damage a restriction + // (a key node is a node at the junction of ways) + + var nodes = { + from: [], + via: [], + to: [], + keyfrom: [], + keyto: [] + }; + + for (j = 0; j < relation.members.length; j++) { + collectNodes(relation.members[j], nodes); + } + + nodes.keyfrom = utilArrayUniq(nodes.keyfrom.filter(hasDuplicates)); + nodes.keyto = utilArrayUniq(nodes.keyto.filter(hasDuplicates)); + var filter = keyNodeFilter(nodes.keyfrom, nodes.keyto); + nodes.from = nodes.from.filter(filter); + nodes.via = nodes.via.filter(filter); + nodes.to = nodes.to.filter(filter); + var connectFrom = false; + var connectVia = false; + var connectTo = false; + var connectKeyFrom = false; + var connectKeyTo = false; + + for (j = 0; j < nodeIDs.length; j++) { + var n = nodeIDs[j]; + + if (nodes.from.indexOf(n) !== -1) { + connectFrom = true; + } + + if (nodes.via.indexOf(n) !== -1) { + connectVia = true; + } + + if (nodes.to.indexOf(n) !== -1) { + connectTo = true; + } + + if (nodes.keyfrom.indexOf(n) !== -1) { + connectKeyFrom = true; + } + + if (nodes.keyto.indexOf(n) !== -1) { + connectKeyTo = true; + } + } + + if (connectFrom && connectTo && !isUturn) { + return 'restriction'; + } + + if (connectFrom && connectVia) { + return 'restriction'; + } + + if (connectTo && connectVia) { + return 'restriction'; + } // connecting to a key node - + // if both nodes are on a member way (i.e. part of the turn restriction), + // the connecting node must be adjacent to the key node. + + + if (connectKeyFrom || connectKeyTo) { + if (nodeIDs.length !== 2) { + return 'restriction'; + } + + var n0 = null; + var n1 = null; + + for (j = 0; j < memberWays.length; j++) { + way = memberWays[j]; + + if (way.contains(nodeIDs[0])) { + n0 = nodeIDs[0]; + } + + if (way.contains(nodeIDs[1])) { + n1 = nodeIDs[1]; + } + } + + if (n0 && n1) { + // both nodes are part of the restriction + var ok = false; + + for (j = 0; j < memberWays.length; j++) { + way = memberWays[j]; + + if (way.areAdjacent(n0, n1)) { + ok = true; + break; + } + } + + if (!ok) { + return 'restriction'; + } + } + } // 2b. disable if nodes being connected will destroy a member way in a restriction + // (to test, make a copy and try actually connecting the nodes) + + + for (j = 0; j < memberWays.length; j++) { + way = memberWays[j].update({}); // make copy + + for (k = 0; k < nodeIDs.length; k++) { + if (nodeIDs[k] === survivor.id) continue; + + if (way.areAdjacent(nodeIDs[k], survivor.id)) { + way = way.removeNode(nodeIDs[k]); + } else { + way = way.replaceNode(nodeIDs[k], survivor.id); + } + } + + if (way.isDegenerate()) { + return 'restriction'; + } + } + } + + return false; // if a key node appears multiple times (indexOf !== lastIndexOf) it's a FROM-VIA or TO-VIA junction + + function hasDuplicates(n, i, arr) { + return arr.indexOf(n) !== arr.lastIndexOf(n); + } + + function keyNodeFilter(froms, tos) { + return function (n) { + return froms.indexOf(n) === -1 && tos.indexOf(n) === -1; + }; + } + + function collectNodes(member, collection) { + var entity = graph.hasEntity(member.id); + if (!entity) return; + var role = member.role || ''; + + if (!collection[role]) { + collection[role] = []; + } + + if (member.type === 'node') { + collection[role].push(member.id); + + if (role === 'via') { + collection.keyfrom.push(member.id); + collection.keyto.push(member.id); + } + } else if (member.type === 'way') { + collection[role].push.apply(collection[role], entity.nodes); + + if (role === 'from' || role === 'via') { + collection.keyfrom.push(entity.first()); + collection.keyfrom.push(entity.last()); + } + + if (role === 'to' || role === 'via') { + collection.keyto.push(entity.first()); + collection.keyto.push(entity.last()); + } + } + } + }; + + return action; + } + + function actionCopyEntities(ids, fromGraph) { + var _copies = {}; + + var action = function action(graph) { + ids.forEach(function (id) { + fromGraph.entity(id).copy(fromGraph, _copies); + }); + + for (var id in _copies) { + graph = graph.replace(_copies[id]); + } + + return graph; + }; + + action.copies = function () { + return _copies; + }; + + return action; + } + + function actionDeleteMember(relationId, memberIndex) { + return function (graph) { + var relation = graph.entity(relationId).removeMember(memberIndex); + graph = graph.replace(relation); + + if (relation.isDegenerate()) { + graph = actionDeleteRelation(relation.id)(graph); + } + + return graph; + }; + } + + function actionDiscardTags(difference, discardTags) { + discardTags = discardTags || {}; + return function (graph) { + difference.modified().forEach(checkTags); + difference.created().forEach(checkTags); + return graph; + + function checkTags(entity) { + var keys = Object.keys(entity.tags); + var didDiscard = false; + var tags = {}; + + for (var i = 0; i < keys.length; i++) { + var k = keys[i]; + + if (discardTags[k] || !entity.tags[k]) { + didDiscard = true; + } else { + tags[k] = entity.tags[k]; + } + } + + if (didDiscard) { + graph = graph.replace(entity.update({ + tags: tags + })); + } + } + }; + } + + // + // Optionally, disconnect only the given ways. + // + // For testing convenience, accepts an ID to assign to the (first) new node. + // Normally, this will be undefined and the way will automatically + // be assigned a new ID. + // + // This is the inverse of `iD.actionConnect`. + // + // Reference: + // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as + // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java + // + + function actionDisconnect(nodeId, newNodeId) { + var wayIds; + + var action = function action(graph) { + var node = graph.entity(nodeId); + var connections = action.connections(graph); + connections.forEach(function (connection) { + var way = graph.entity(connection.wayID); + var newNode = osmNode({ + id: newNodeId, + loc: node.loc, + tags: node.tags + }); + graph = graph.replace(newNode); + + if (connection.index === 0 && way.isArea()) { + // replace shared node with shared node.. + graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id)); + } else if (way.isClosed() && connection.index === way.nodes.length - 1) { + // replace closing node with new new node.. + graph = graph.replace(way.unclose().addNode(newNode.id)); + } else { + // replace shared node with multiple new nodes.. + graph = graph.replace(way.updateNode(newNode.id, connection.index)); + } + }); + return graph; + }; + + action.connections = function (graph) { + var candidates = []; + var keeping = false; + var parentWays = graph.parentWays(graph.entity(nodeId)); + var way, waynode; + + for (var i = 0; i < parentWays.length; i++) { + way = parentWays[i]; + + if (wayIds && wayIds.indexOf(way.id) === -1) { + keeping = true; + continue; + } + + if (way.isArea() && way.nodes[0] === nodeId) { + candidates.push({ + wayID: way.id, + index: 0 + }); + } else { + for (var j = 0; j < way.nodes.length; j++) { + waynode = way.nodes[j]; + + if (waynode === nodeId) { + if (way.isClosed() && parentWays.length > 1 && wayIds && wayIds.indexOf(way.id) !== -1 && j === way.nodes.length - 1) { + continue; + } + + candidates.push({ + wayID: way.id, + index: j + }); + } + } + } + } + + return keeping ? candidates : candidates.slice(1); + }; + + action.disabled = function (graph) { + var connections = action.connections(graph); + if (connections.length === 0) return 'not_connected'; + var parentWays = graph.parentWays(graph.entity(nodeId)); + var seenRelationIds = {}; + var sharedRelation; + parentWays.forEach(function (way) { + var relations = graph.parentRelations(way); + relations.forEach(function (relation) { + if (relation.id in seenRelationIds) { + if (wayIds) { + if (wayIds.indexOf(way.id) !== -1 || wayIds.indexOf(seenRelationIds[relation.id]) !== -1) { + sharedRelation = relation; + } + } else { + sharedRelation = relation; + } + } else { + seenRelationIds[relation.id] = way.id; + } + }); + }); + if (sharedRelation) return 'relation'; + }; + + action.limitWays = function (val) { + if (!arguments.length) return wayIds; + wayIds = val; + return action; + }; + + return action; + } + + function actionExtract(entityID, projection) { + var extractedNodeID; + + var action = function action(graph) { + var entity = graph.entity(entityID); + + if (entity.type === 'node') { + return extractFromNode(entity, graph); + } + + return extractFromWayOrRelation(entity, graph); + }; + + function extractFromNode(node, graph) { + extractedNodeID = node.id; // Create a new node to replace the one we will detach + + var replacement = osmNode({ + loc: node.loc + }); + graph = graph.replace(replacement); // Process each way in turn, updating the graph as we go + + graph = graph.parentWays(node).reduce(function (accGraph, parentWay) { + return accGraph.replace(parentWay.replaceNode(entityID, replacement.id)); + }, graph); // Process any relations too + + return graph.parentRelations(node).reduce(function (accGraph, parentRel) { + return accGraph.replace(parentRel.replaceMember(node, replacement)); + }, graph); + } + + function extractFromWayOrRelation(entity, graph) { + var fromGeometry = entity.geometry(graph); + var keysToCopyAndRetain = ['source', 'wheelchair']; + var keysToRetain = ['area']; + var buildingKeysToRetain = ['architect', 'building', 'height', 'layer']; + var extractedLoc = d3_geoPath(projection).centroid(entity.asGeoJSON(graph)); + extractedLoc = extractedLoc && projection.invert(extractedLoc); + + if (!extractedLoc || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) { + extractedLoc = entity.extent(graph).center(); + } + + var indoorAreaValues = { + area: true, + corridor: true, + elevator: true, + level: true, + room: true + }; + var isBuilding = entity.tags.building && entity.tags.building !== 'no' || entity.tags['building:part'] && entity.tags['building:part'] !== 'no'; + var isIndoorArea = fromGeometry === 'area' && entity.tags.indoor && indoorAreaValues[entity.tags.indoor]; + var entityTags = Object.assign({}, entity.tags); // shallow copy + + var pointTags = {}; + + for (var key in entityTags) { + if (entity.type === 'relation' && key === 'type') { + continue; + } + + if (keysToRetain.indexOf(key) !== -1) { + continue; + } + + if (isBuilding) { + // don't transfer building-related tags + if (buildingKeysToRetain.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue; + } // leave `indoor` tag on the area + + + if (isIndoorArea && key === 'indoor') { + continue; + } // copy the tag from the entity to the point + + + pointTags[key] = entityTags[key]; // leave addresses and some other tags so they're on both features + + if (keysToCopyAndRetain.indexOf(key) !== -1 || key.match(/^addr:.{1,}/)) { + continue; + } else if (isIndoorArea && key === 'level') { + // leave `level` on both features + continue; + } // remove the tag from the entity + + + delete entityTags[key]; + } + + if (!isBuilding && !isIndoorArea && fromGeometry === 'area') { + // ensure that areas keep area geometry + entityTags.area = 'yes'; + } + + var replacement = osmNode({ + loc: extractedLoc, + tags: pointTags + }); + graph = graph.replace(replacement); + extractedNodeID = replacement.id; + return graph.replace(entity.update({ + tags: entityTags + })); + } + + action.getExtractedNodeID = function () { + return extractedNodeID; + }; + + return action; + } + + // + // This is the inverse of `iD.actionSplit`. + // + // Reference: + // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as + // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java + // + + function actionJoin(ids) { + function groupEntitiesByGeometry(graph) { + var entities = ids.map(function (id) { + return graph.entity(id); + }); + return Object.assign({ + line: [] + }, utilArrayGroupBy(entities, function (entity) { + return entity.geometry(graph); + })); + } + + var action = function action(graph) { + var ways = ids.map(graph.entity, graph); // if any of the ways are sided (e.g. coastline, cliff, kerb) + // sort them first so they establish the overall order - #6033 + + ways.sort(function (a, b) { + var aSided = a.isSided(); + var bSided = b.isSided(); + return aSided && !bSided ? -1 : bSided && !aSided ? 1 : 0; + }); // Prefer to keep an existing way. + // if there are multiple existing ways, keep the oldest one + // the oldest way is determined by the ID of the way + + var survivorID = (ways.filter(function (way) { + return !way.isNew(); + }).sort(function (a, b) { + return +a.osmId() - +b.osmId(); + })[0] || ways[0]).id; + var sequences = osmJoinWays(ways, graph); + var joined = sequences[0]; // We might need to reverse some of these ways before joining them. #4688 + // `joined.actions` property will contain any actions we need to apply. + + graph = sequences.actions.reduce(function (g, action) { + return action(g); + }, graph); + var survivor = graph.entity(survivorID); + survivor = survivor.update({ + nodes: joined.nodes.map(function (n) { + return n.id; + }) + }); + graph = graph.replace(survivor); + joined.forEach(function (way) { + if (way.id === survivorID) return; + graph.parentRelations(way).forEach(function (parent) { + graph = graph.replace(parent.replaceMember(way, survivor)); + }); + survivor = survivor.mergeTags(way.tags); + graph = graph.replace(survivor); + graph = actionDeleteWay(way.id)(graph); + }); // Finds if the join created a single-member multipolygon, + // and if so turns it into a basic area instead + + function checkForSimpleMultipolygon() { + if (!survivor.isClosed()) return; + var multipolygons = graph.parentMultipolygons(survivor).filter(function (multipolygon) { + // find multipolygons where the survivor is the only member + return multipolygon.members.length === 1; + }); // skip if this is the single member of multiple multipolygons + + if (multipolygons.length !== 1) return; + var multipolygon = multipolygons[0]; + + for (var key in survivor.tags) { + if (multipolygon.tags[key] && // don't collapse if tags cannot be cleanly merged + multipolygon.tags[key] !== survivor.tags[key]) return; + } + + survivor = survivor.mergeTags(multipolygon.tags); + graph = graph.replace(survivor); + graph = actionDeleteRelation(multipolygon.id, true + /* allow untagged members */ + )(graph); + var tags = Object.assign({}, survivor.tags); + + if (survivor.geometry(graph) !== 'area') { + // ensure the feature persists as an area + tags.area = 'yes'; + } + + delete tags.type; // remove type=multipolygon + + survivor = survivor.update({ + tags: tags + }); + graph = graph.replace(survivor); + } + + checkForSimpleMultipolygon(); + return graph; + }; // Returns the number of nodes the resultant way is expected to have + + + action.resultingWayNodesLength = function (graph) { + return ids.reduce(function (count, id) { + return count + graph.entity(id).nodes.length; + }, 0) - ids.length - 1; + }; + + action.disabled = function (graph) { + var geometries = groupEntitiesByGeometry(graph); + + if (ids.length < 2 || ids.length !== geometries.line.length) { + return 'not_eligible'; + } + + var joined = osmJoinWays(ids.map(graph.entity, graph), graph); + + if (joined.length > 1) { + return 'not_adjacent'; + } + + var i; // All joined ways must belong to the same set of (non-restriction) relations. + // Restriction relations have different logic, below, which allows some cases + // this prohibits, and prohibits some cases this allows. + + var sortedParentRelations = function sortedParentRelations(id) { + return graph.parentRelations(graph.entity(id)).filter(function (rel) { + return !rel.isRestriction() && !rel.isConnectivity(); + }).sort(function (a, b) { + return a.id - b.id; + }); + }; + + var relsA = sortedParentRelations(ids[0]); + + for (i = 1; i < ids.length; i++) { + var relsB = sortedParentRelations(ids[i]); + + if (!utilArrayIdentical(relsA, relsB)) { + return 'conflicting_relations'; + } + } // Loop through all combinations of path-pairs + // to check potential intersections between all pairs + + + for (i = 0; i < ids.length - 1; i++) { + for (var j = i + 1; j < ids.length; j++) { + var path1 = graph.childNodes(graph.entity(ids[i])).map(function (e) { + return e.loc; + }); + var path2 = graph.childNodes(graph.entity(ids[j])).map(function (e) { + return e.loc; + }); + var intersections = geoPathIntersections(path1, path2); // Check if intersections are just nodes lying on top of + // each other/the line, as opposed to crossing it + + var common = utilArrayIntersection(joined[0].nodes.map(function (n) { + return n.loc.toString(); + }), intersections.map(function (n) { + return n.toString(); + })); + + if (common.length !== intersections.length) { + return 'paths_intersect'; + } + } + } + + var nodeIds = joined[0].nodes.map(function (n) { + return n.id; + }).slice(1, -1); + var relation; + var tags = {}; + var conflicting = false; + joined[0].forEach(function (way) { + var parents = graph.parentRelations(way); + parents.forEach(function (parent) { + if ((parent.isRestriction() || parent.isConnectivity()) && parent.members.some(function (m) { + return nodeIds.indexOf(m.id) >= 0; + })) { + relation = parent; + } + }); + + for (var k in way.tags) { + if (!(k in tags)) { + tags[k] = way.tags[k]; + } else if (tags[k] && osmIsInterestingTag(k) && tags[k] !== way.tags[k]) { + conflicting = true; + } + } + }); + + if (relation) { + return relation.isRestriction() ? 'restriction' : 'connectivity'; + } + + if (conflicting) { + return 'conflicting_tags'; + } + }; + + return action; + } + + function actionMerge(ids) { + function groupEntitiesByGeometry(graph) { + var entities = ids.map(function (id) { + return graph.entity(id); + }); + return Object.assign({ + point: [], + area: [], + line: [], + relation: [] + }, utilArrayGroupBy(entities, function (entity) { + return entity.geometry(graph); + })); + } + + var action = function action(graph) { + var geometries = groupEntitiesByGeometry(graph); + var target = geometries.area[0] || geometries.line[0]; + var points = geometries.point; + points.forEach(function (point) { + target = target.mergeTags(point.tags); + graph = graph.replace(target); + graph.parentRelations(point).forEach(function (parent) { + graph = graph.replace(parent.replaceMember(point, target)); + }); + var nodes = utilArrayUniq(graph.childNodes(target)); + var removeNode = point; + + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + + if (graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags()) { + continue; + } // Found an uninteresting child node on the target way. + // Move orig point into its place to preserve point's history. #3683 + + + graph = graph.replace(point.update({ + tags: {}, + loc: node.loc + })); + target = target.replaceNode(node.id, point.id); + graph = graph.replace(target); + removeNode = node; + break; + } + + graph = graph.remove(removeNode); + }); + + if (target.tags.area === 'yes') { + var tags = Object.assign({}, target.tags); // shallow copy + + delete tags.area; + + if (osmTagSuggestingArea(tags)) { + // remove the `area` tag if area geometry is now implied - #3851 + target = target.update({ + tags: tags + }); + graph = graph.replace(target); + } + } + + return graph; + }; + + action.disabled = function (graph) { + var geometries = groupEntitiesByGeometry(graph); + + if (geometries.point.length === 0 || geometries.area.length + geometries.line.length !== 1 || geometries.relation.length !== 0) { + return 'not_eligible'; + } + }; + + return action; + } + + // + // 1. move all the nodes to a common location + // 2. `actionConnect` them + + function actionMergeNodes(nodeIDs, loc) { + // If there is a single "interesting" node, use that as the location. + // Otherwise return the average location of all the nodes. + function chooseLoc(graph) { + if (!nodeIDs.length) return null; + var sum = [0, 0]; + var interestingCount = 0; + var interestingLoc; + + for (var i = 0; i < nodeIDs.length; i++) { + var node = graph.entity(nodeIDs[i]); + + if (node.hasInterestingTags()) { + interestingLoc = ++interestingCount === 1 ? node.loc : null; + } + + sum = geoVecAdd(sum, node.loc); + } + + return interestingLoc || geoVecScale(sum, 1 / nodeIDs.length); + } + + var action = function action(graph) { + if (nodeIDs.length < 2) return graph; + var toLoc = loc; + + if (!toLoc) { + toLoc = chooseLoc(graph); + } + + for (var i = 0; i < nodeIDs.length; i++) { + var node = graph.entity(nodeIDs[i]); + + if (node.loc !== toLoc) { + graph = graph.replace(node.move(toLoc)); + } + } + + return actionConnect(nodeIDs)(graph); + }; + + action.disabled = function (graph) { + if (nodeIDs.length < 2) return 'not_eligible'; + + for (var i = 0; i < nodeIDs.length; i++) { + var entity = graph.entity(nodeIDs[i]); + if (entity.type !== 'node') return 'not_eligible'; + } + + return actionConnect(nodeIDs).disabled(graph); + }; + + return action; + } + + function osmChangeset() { + if (!(this instanceof osmChangeset)) { + return new osmChangeset().initialize(arguments); + } else if (arguments.length) { + this.initialize(arguments); + } + } + osmEntity.changeset = osmChangeset; + osmChangeset.prototype = Object.create(osmEntity.prototype); + Object.assign(osmChangeset.prototype, { + type: 'changeset', + extent: function extent() { + return new geoExtent(); + }, + geometry: function geometry() { + return 'changeset'; + }, + asJXON: function asJXON() { + return { + osm: { + changeset: { + tag: Object.keys(this.tags).map(function (k) { + return { + '@k': k, + '@v': this.tags[k] + }; + }, this), + '@version': 0.6, + '@generator': 'iD' + } + } + }; + }, + // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange) + // XML. Returns a string. + osmChangeJXON: function osmChangeJXON(changes) { + var changeset_id = this.id; + + function nest(x, order) { + var groups = {}; + + for (var i = 0; i < x.length; i++) { + var tagName = Object.keys(x[i])[0]; + if (!groups[tagName]) groups[tagName] = []; + groups[tagName].push(x[i][tagName]); + } + + var ordered = {}; + order.forEach(function (o) { + if (groups[o]) ordered[o] = groups[o]; + }); + return ordered; + } // sort relations in a changeset by dependencies + + + function sort(changes) { + // find a referenced relation in the current changeset + function resolve(item) { + return relations.find(function (relation) { + return item.keyAttributes.type === 'relation' && item.keyAttributes.ref === relation['@id']; + }); + } // a new item is an item that has not been already processed + + + function isNew(item) { + return !sorted[item['@id']] && !processing.find(function (proc) { + return proc['@id'] === item['@id']; + }); + } + + var processing = []; + var sorted = {}; + var relations = changes.relation; + if (!relations) return changes; + + for (var i = 0; i < relations.length; i++) { + var relation = relations[i]; // skip relation if already sorted + + if (!sorted[relation['@id']]) { + processing.push(relation); + } + + while (processing.length > 0) { + var next = processing[0], + deps = next.member.map(resolve).filter(Boolean).filter(isNew); + + if (deps.length === 0) { + sorted[next['@id']] = next; + processing.shift(); + } else { + processing = deps.concat(processing); + } + } + } + + changes.relation = Object.values(sorted); + return changes; + } + + function rep(entity) { + return entity.asJXON(changeset_id); + } + + return { + osmChange: { + '@version': 0.6, + '@generator': 'iD', + 'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])), + 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']), + 'delete': Object.assign(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), { + '@if-unused': true + }) + } + }; + }, + asGeoJSON: function asGeoJSON() { + return {}; + } + }); + + function osmNote() { + if (!(this instanceof osmNote)) { + return new osmNote().initialize(arguments); + } else if (arguments.length) { + this.initialize(arguments); + } + } + + osmNote.id = function () { + return osmNote.id.next--; + }; + + osmNote.id.next = -1; + Object.assign(osmNote.prototype, { + type: 'note', + initialize: function initialize(sources) { + for (var i = 0; i < sources.length; ++i) { + var source = sources[i]; + + for (var prop in source) { + if (Object.prototype.hasOwnProperty.call(source, prop)) { + if (source[prop] === undefined) { + delete this[prop]; + } else { + this[prop] = source[prop]; + } + } + } + } + + if (!this.id) { + this.id = osmNote.id().toString(); + } + + return this; + }, + extent: function extent() { + return new geoExtent(this.loc); + }, + update: function update(attrs) { + return osmNote(this, attrs); // {v: 1 + (this.v || 0)} + }, + isNew: function isNew() { + return this.id < 0; + }, + move: function move(loc) { + return this.update({ + loc: loc + }); + } + }); + + function osmRelation() { + if (!(this instanceof osmRelation)) { + return new osmRelation().initialize(arguments); + } else if (arguments.length) { + this.initialize(arguments); + } + } + osmEntity.relation = osmRelation; + osmRelation.prototype = Object.create(osmEntity.prototype); + + osmRelation.creationOrder = function (a, b) { + var aId = parseInt(osmEntity.id.toOSM(a.id), 10); + var bId = parseInt(osmEntity.id.toOSM(b.id), 10); + if (aId < 0 || bId < 0) return aId - bId; + return bId - aId; + }; + + Object.assign(osmRelation.prototype, { + type: 'relation', + members: [], + copy: function copy(resolver, copies) { + if (copies[this.id]) return copies[this.id]; + var copy = osmEntity.prototype.copy.call(this, resolver, copies); + var members = this.members.map(function (member) { + return Object.assign({}, member, { + id: resolver.entity(member.id).copy(resolver, copies).id + }); + }); + copy = copy.update({ + members: members + }); + copies[this.id] = copy; + return copy; + }, + extent: function extent(resolver, memo) { + return resolver["transient"](this, 'extent', function () { + if (memo && memo[this.id]) return geoExtent(); + memo = memo || {}; + memo[this.id] = true; + var extent = geoExtent(); + + for (var i = 0; i < this.members.length; i++) { + var member = resolver.hasEntity(this.members[i].id); + + if (member) { + extent._extend(member.extent(resolver, memo)); + } + } + + return extent; + }); + }, + geometry: function geometry(graph) { + return graph["transient"](this, 'geometry', function () { + return this.isMultipolygon() ? 'area' : 'relation'; + }); + }, + isDegenerate: function isDegenerate() { + return this.members.length === 0; + }, + // Return an array of members, each extended with an 'index' property whose value + // is the member index. + indexedMembers: function indexedMembers() { + var result = new Array(this.members.length); + + for (var i = 0; i < this.members.length; i++) { + result[i] = Object.assign({}, this.members[i], { + index: i + }); + } + + return result; + }, + // Return the first member with the given role. A copy of the member object + // is returned, extended with an 'index' property whose value is the member index. + memberByRole: function memberByRole(role) { + for (var i = 0; i < this.members.length; i++) { + if (this.members[i].role === role) { + return Object.assign({}, this.members[i], { + index: i + }); + } + } + }, + // Same as memberByRole, but returns all members with the given role + membersByRole: function membersByRole(role) { + var result = []; + + for (var i = 0; i < this.members.length; i++) { + if (this.members[i].role === role) { + result.push(Object.assign({}, this.members[i], { + index: i + })); + } + } + + return result; + }, + // Return the first member with the given id. A copy of the member object + // is returned, extended with an 'index' property whose value is the member index. + memberById: function memberById(id) { + for (var i = 0; i < this.members.length; i++) { + if (this.members[i].id === id) { + return Object.assign({}, this.members[i], { + index: i + }); + } + } + }, + // Return the first member with the given id and role. A copy of the member object + // is returned, extended with an 'index' property whose value is the member index. + memberByIdAndRole: function memberByIdAndRole(id, role) { + for (var i = 0; i < this.members.length; i++) { + if (this.members[i].id === id && this.members[i].role === role) { + return Object.assign({}, this.members[i], { + index: i + }); + } + } + }, + addMember: function addMember(member, index) { + var members = this.members.slice(); + members.splice(index === undefined ? members.length : index, 0, member); + return this.update({ + members: members + }); + }, + updateMember: function updateMember(member, index) { + var members = this.members.slice(); + members.splice(index, 1, Object.assign({}, members[index], member)); + return this.update({ + members: members + }); + }, + removeMember: function removeMember(index) { + var members = this.members.slice(); + members.splice(index, 1); + return this.update({ + members: members + }); + }, + removeMembersWithID: function removeMembersWithID(id) { + var members = this.members.filter(function (m) { + return m.id !== id; + }); + return this.update({ + members: members + }); + }, + moveMember: function moveMember(fromIndex, toIndex) { + var members = this.members.slice(); + members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]); + return this.update({ + members: members + }); + }, + // Wherever a member appears with id `needle.id`, replace it with a member + // with id `replacement.id`, type `replacement.type`, and the original role, + // By default, adding a duplicate member (by id and role) is prevented. + // Return an updated relation. + replaceMember: function replaceMember(needle, replacement, keepDuplicates) { + if (!this.memberById(needle.id)) return this; + var members = []; + + for (var i = 0; i < this.members.length; i++) { + var member = this.members[i]; + + if (member.id !== needle.id) { + members.push(member); + } else if (keepDuplicates || !this.memberByIdAndRole(replacement.id, member.role)) { + members.push({ + id: replacement.id, + type: replacement.type, + role: member.role + }); + } + } + + return this.update({ + members: members + }); + }, + asJXON: function asJXON(changeset_id) { + var r = { + relation: { + '@id': this.osmId(), + '@version': this.version || 0, + member: this.members.map(function (member) { + return { + keyAttributes: { + type: member.type, + role: member.role, + ref: osmEntity.id.toOSM(member.id) + } + }; + }, this), + tag: Object.keys(this.tags).map(function (k) { + return { + keyAttributes: { + k: k, + v: this.tags[k] + } + }; + }, this) + } + }; + + if (changeset_id) { + r.relation['@changeset'] = changeset_id; + } + + return r; + }, + asGeoJSON: function asGeoJSON(resolver) { + return resolver["transient"](this, 'GeoJSON', function () { + if (this.isMultipolygon()) { + return { + type: 'MultiPolygon', + coordinates: this.multipolygon(resolver) + }; + } else { + return { + type: 'FeatureCollection', + properties: this.tags, + features: this.members.map(function (member) { + return Object.assign({ + role: member.role + }, resolver.entity(member.id).asGeoJSON(resolver)); + }) + }; + } + }); + }, + area: function area(resolver) { + return resolver["transient"](this, 'area', function () { + return d3_geoArea(this.asGeoJSON(resolver)); + }); + }, + isMultipolygon: function isMultipolygon() { + return this.tags.type === 'multipolygon'; + }, + isComplete: function isComplete(resolver) { + for (var i = 0; i < this.members.length; i++) { + if (!resolver.hasEntity(this.members[i].id)) { + return false; + } + } + + return true; + }, + hasFromViaTo: function hasFromViaTo() { + return this.members.some(function (m) { + return m.role === 'from'; + }) && this.members.some(function (m) { + return m.role === 'via'; + }) && this.members.some(function (m) { + return m.role === 'to'; + }); + }, + isRestriction: function isRestriction() { + return !!(this.tags.type && this.tags.type.match(/^restriction:?/)); + }, + isValidRestriction: function isValidRestriction() { + if (!this.isRestriction()) return false; + var froms = this.members.filter(function (m) { + return m.role === 'from'; + }); + var vias = this.members.filter(function (m) { + return m.role === 'via'; + }); + var tos = this.members.filter(function (m) { + return m.role === 'to'; + }); + if (froms.length !== 1 && this.tags.restriction !== 'no_entry') return false; + if (froms.some(function (m) { + return m.type !== 'way'; + })) return false; + if (tos.length !== 1 && this.tags.restriction !== 'no_exit') return false; + if (tos.some(function (m) { + return m.type !== 'way'; + })) return false; + if (vias.length === 0) return false; + if (vias.length > 1 && vias.some(function (m) { + return m.type !== 'way'; + })) return false; + return true; + }, + isConnectivity: function isConnectivity() { + return !!(this.tags.type && this.tags.type.match(/^connectivity:?/)); + }, + // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm], + // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings. + // + // This corresponds to the structure needed for rendering a multipolygon path using a + // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry. + // + // In the case of invalid geometries, this function will still return a result which + // includes the nodes of all way members, but some Nds may be unclosed and some inner + // rings not matched with the intended outer ring. + // + multipolygon: function multipolygon(resolver) { + var outers = this.members.filter(function (m) { + return 'outer' === (m.role || 'outer'); + }); + var inners = this.members.filter(function (m) { + return 'inner' === m.role; + }); + outers = osmJoinWays(outers, resolver); + inners = osmJoinWays(inners, resolver); + + var sequenceToLineString = function sequenceToLineString(sequence) { + if (sequence.nodes.length > 2 && sequence.nodes[0] !== sequence.nodes[sequence.nodes.length - 1]) { + // close unclosed parts to ensure correct area rendering - #2945 + sequence.nodes.push(sequence.nodes[0]); + } + + return sequence.nodes.map(function (node) { + return node.loc; + }); + }; + + outers = outers.map(sequenceToLineString); + inners = inners.map(sequenceToLineString); + var result = outers.map(function (o) { + // Heuristic for detecting counterclockwise winding order. Assumes + // that OpenStreetMap polygons are not hemisphere-spanning. + return [d3_geoArea({ + type: 'Polygon', + coordinates: [o] + }) > 2 * Math.PI ? o.reverse() : o]; + }); + + function findOuter(inner) { + var o, outer; + + for (o = 0; o < outers.length; o++) { + outer = outers[o]; + + if (geoPolygonContainsPolygon(outer, inner)) { + return o; + } + } + + for (o = 0; o < outers.length; o++) { + outer = outers[o]; + + if (geoPolygonIntersectsPolygon(outer, inner, false)) { + return o; + } + } + } + + for (var i = 0; i < inners.length; i++) { + var inner = inners[i]; + + if (d3_geoArea({ + type: 'Polygon', + coordinates: [inner] + }) < 2 * Math.PI) { + inner = inner.reverse(); + } + + var o = findOuter(inners[i]); + + if (o !== undefined) { + result[o].push(inners[i]); + } else { + result.push([inners[i]]); // Invalid geometry + } + } + + return result; + } + }); + + var QAItem = /*#__PURE__*/function () { + function QAItem(loc, service, itemType, id, props) { + _classCallCheck$1(this, QAItem); + + // Store required properties + this.loc = loc; + this.service = service.title; + this.itemType = itemType; // All issues must have an ID for selection, use generic if none specified + + this.id = id ? id : "".concat(QAItem.id()); + this.update(props); // Some QA services have marker icons to differentiate issues + + if (service && typeof service.getIcon === 'function') { + this.icon = service.getIcon(itemType); + } + } + + _createClass$1(QAItem, [{ + key: "update", + value: function update(props) { + var _this = this; + + // You can't override this initial information + var loc = this.loc, + service = this.service, + itemType = this.itemType, + id = this.id; + Object.keys(props).forEach(function (prop) { + return _this[prop] = props[prop]; + }); + this.loc = loc; + this.service = service; + this.itemType = itemType; + this.id = id; + return this; + } // Generic handling for newly created QAItems + + }], [{ + key: "id", + value: function id() { + return this.nextId--; + } + }]); + + return QAItem; + }(); + QAItem.nextId = -1; + + // + // Optionally, split only the given ways, if multiple ways share + // the given node. + // + // This is the inverse of `iD.actionJoin`. + // + // For testing convenience, accepts an ID to assign to the new way. + // Normally, this will be undefined and the way will automatically + // be assigned a new ID. + // + // Reference: + // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as + // + + function actionSplit(nodeIds, newWayIds) { + // accept single ID for backwards-compatiblity + if (typeof nodeIds === 'string') nodeIds = [nodeIds]; + + var _wayIDs; // the strategy for picking which way will have a new version and which way is newly created + + + var _keepHistoryOn = 'longest'; // 'longest', 'first' + // The IDs of the ways actually created by running this action + + var _createdWayIDs = []; + + function dist(graph, nA, nB) { + var locA = graph.entity(nA).loc; + var locB = graph.entity(nB).loc; + var epsilon = 1e-6; + return locA && locB ? geoSphericalDistance(locA, locB) : epsilon; + } // If the way is closed, we need to search for a partner node + // to split the way at. + // + // The following looks for a node that is both far away from + // the initial node in terms of way segment length and nearby + // in terms of beeline-distance. This assures that areas get + // split on the most "natural" points (independent of the number + // of nodes). + // For example: bone-shaped areas get split across their waist + // line, circles across the diameter. + + + function splitArea(nodes, idxA, graph) { + var lengths = new Array(nodes.length); + var length; + var i; + var best = 0; + var idxB; + + function wrap(index) { + return utilWrap(index, nodes.length); + } // calculate lengths + + + length = 0; + + for (i = wrap(idxA + 1); i !== idxA; i = wrap(i + 1)) { + length += dist(graph, nodes[i], nodes[wrap(i - 1)]); + lengths[i] = length; + } + + length = 0; + + for (i = wrap(idxA - 1); i !== idxA; i = wrap(i - 1)) { + length += dist(graph, nodes[i], nodes[wrap(i + 1)]); + + if (length < lengths[i]) { + lengths[i] = length; + } + } // determine best opposite node to split + + + for (i = 0; i < nodes.length; i++) { + var cost = lengths[i] / dist(graph, nodes[idxA], nodes[i]); + + if (cost > best) { + idxB = i; + best = cost; + } + } + + return idxB; + } + + function totalLengthBetweenNodes(graph, nodes) { + var totalLength = 0; + + for (var i = 0; i < nodes.length - 1; i++) { + totalLength += dist(graph, nodes[i], nodes[i + 1]); + } + + return totalLength; + } + + function split(graph, nodeId, wayA, newWayId) { + var wayB = osmWay({ + id: newWayId, + tags: wayA.tags + }); // `wayB` is the NEW way + + var origNodes = wayA.nodes.slice(); + var nodesA; + var nodesB; + var isArea = wayA.isArea(); + var isOuter = osmIsOldMultipolygonOuterMember(wayA, graph); + + if (wayA.isClosed()) { + var nodes = wayA.nodes.slice(0, -1); + var idxA = nodes.indexOf(nodeId); + var idxB = splitArea(nodes, idxA, graph); + + if (idxB < idxA) { + nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1)); + nodesB = nodes.slice(idxB, idxA + 1); + } else { + nodesA = nodes.slice(idxA, idxB + 1); + nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1)); + } + } else { + var idx = wayA.nodes.indexOf(nodeId, 1); + nodesA = wayA.nodes.slice(0, idx + 1); + nodesB = wayA.nodes.slice(idx); + } + + var lengthA = totalLengthBetweenNodes(graph, nodesA); + var lengthB = totalLengthBetweenNodes(graph, nodesB); + + if (_keepHistoryOn === 'longest' && lengthB > lengthA) { + // keep the history on the longer way, regardless of the node count + wayA = wayA.update({ + nodes: nodesB + }); + wayB = wayB.update({ + nodes: nodesA + }); + var temp = lengthA; + lengthA = lengthB; + lengthB = temp; + } else { + wayA = wayA.update({ + nodes: nodesA + }); + wayB = wayB.update({ + nodes: nodesB + }); + } + + if (wayA.tags.step_count) { + // divide up the the step count proportionally between the two ways + var stepCount = parseFloat(wayA.tags.step_count); + + if (stepCount && // ensure a number + isFinite(stepCount) && // ensure positive + stepCount > 0 && // ensure integer + Math.round(stepCount) === stepCount) { + var tagsA = Object.assign({}, wayA.tags); + var tagsB = Object.assign({}, wayB.tags); + var ratioA = lengthA / (lengthA + lengthB); + var countA = Math.round(stepCount * ratioA); + tagsA.step_count = countA.toString(); + tagsB.step_count = (stepCount - countA).toString(); + wayA = wayA.update({ + tags: tagsA + }); + wayB = wayB.update({ + tags: tagsB + }); + } + } + + graph = graph.replace(wayA); + graph = graph.replace(wayB); + graph.parentRelations(wayA).forEach(function (relation) { + var member; // Turn restrictions - make sure: + // 1. Splitting a FROM/TO way - only `wayA` OR `wayB` remains in relation + // (whichever one is connected to the VIA node/ways) + // 2. Splitting a VIA way - `wayB` remains in relation as a VIA way + + if (relation.hasFromViaTo()) { + var f = relation.memberByRole('from'); + var v = relation.membersByRole('via'); + var t = relation.memberByRole('to'); + var i; // 1. split a FROM/TO + + if (f.id === wayA.id || t.id === wayA.id) { + var keepB = false; + + if (v.length === 1 && v[0].type === 'node') { + // check via node + keepB = wayB.contains(v[0].id); + } else { + // check via way(s) + for (i = 0; i < v.length; i++) { + if (v[i].type === 'way') { + var wayVia = graph.hasEntity(v[i].id); + + if (wayVia && utilArrayIntersection(wayB.nodes, wayVia.nodes).length) { + keepB = true; + break; + } + } + } + } + + if (keepB) { + relation = relation.replaceMember(wayA, wayB); + graph = graph.replace(relation); + } // 2. split a VIA + + } else { + for (i = 0; i < v.length; i++) { + if (v[i].type === 'way' && v[i].id === wayA.id) { + member = { + id: wayB.id, + type: 'way', + role: 'via' + }; + graph = actionAddMember(relation.id, member, v[i].index + 1)(graph); + break; + } + } + } // All other relations (Routes, Multipolygons, etc): + // 1. Both `wayA` and `wayB` remain in the relation + // 2. But must be inserted as a pair (see `actionAddMember` for details) + + } else { + if (relation === isOuter) { + graph = graph.replace(relation.mergeTags(wayA.tags)); + graph = graph.replace(wayA.update({ + tags: {} + })); + graph = graph.replace(wayB.update({ + tags: {} + })); + } + + member = { + id: wayB.id, + type: 'way', + role: relation.memberById(wayA.id).role + }; + var insertPair = { + originalID: wayA.id, + insertedID: wayB.id, + nodes: origNodes + }; + graph = actionAddMember(relation.id, member, undefined, insertPair)(graph); + } + }); + + if (!isOuter && isArea) { + var multipolygon = osmRelation({ + tags: Object.assign({}, wayA.tags, { + type: 'multipolygon' + }), + members: [{ + id: wayA.id, + role: 'outer', + type: 'way' + }, { + id: wayB.id, + role: 'outer', + type: 'way' + }] + }); + graph = graph.replace(multipolygon); + graph = graph.replace(wayA.update({ + tags: {} + })); + graph = graph.replace(wayB.update({ + tags: {} + })); + } + + _createdWayIDs.push(wayB.id); + + return graph; + } + + var action = function action(graph) { + _createdWayIDs = []; + var newWayIndex = 0; + + for (var i = 0; i < nodeIds.length; i++) { + var nodeId = nodeIds[i]; + var candidates = action.waysForNode(nodeId, graph); + + for (var j = 0; j < candidates.length; j++) { + graph = split(graph, nodeId, candidates[j], newWayIds && newWayIds[newWayIndex]); + newWayIndex += 1; + } + } + + return graph; + }; + + action.getCreatedWayIDs = function () { + return _createdWayIDs; + }; + + action.waysForNode = function (nodeId, graph) { + var node = graph.entity(nodeId); + var splittableParents = graph.parentWays(node).filter(isSplittable); + + if (!_wayIDs) { + // If the ways to split aren't specified, only split the lines. + // If there are no lines to split, split the areas. + var hasLine = splittableParents.some(function (parent) { + return parent.geometry(graph) === 'line'; + }); + + if (hasLine) { + return splittableParents.filter(function (parent) { + return parent.geometry(graph) === 'line'; + }); + } + } + + return splittableParents; + + function isSplittable(parent) { + // If the ways to split are specified, ignore everything else. + if (_wayIDs && _wayIDs.indexOf(parent.id) === -1) return false; // We can fake splitting closed ways at their endpoints... + + if (parent.isClosed()) return true; // otherwise, we can't split nodes at their endpoints. + + for (var i = 1; i < parent.nodes.length - 1; i++) { + if (parent.nodes[i] === nodeId) return true; + } + + return false; + } + }; + + action.ways = function (graph) { + return utilArrayUniq([].concat.apply([], nodeIds.map(function (nodeId) { + return action.waysForNode(nodeId, graph); + }))); + }; + + action.disabled = function (graph) { + for (var i = 0; i < nodeIds.length; i++) { + var nodeId = nodeIds[i]; + var candidates = action.waysForNode(nodeId, graph); + + if (candidates.length === 0 || _wayIDs && _wayIDs.length !== candidates.length) { + return 'not_eligible'; + } + } + }; + + action.limitWays = function (val) { + if (!arguments.length) return _wayIDs; + _wayIDs = val; + return action; + }; + + action.keepHistoryOn = function (val) { + if (!arguments.length) return _keepHistoryOn; + _keepHistoryOn = val; + return action; + }; + + return action; + } + + function coreGraph(other, mutable) { + if (!(this instanceof coreGraph)) return new coreGraph(other, mutable); + + if (other instanceof coreGraph) { + var base = other.base(); + this.entities = Object.assign(Object.create(base.entities), other.entities); + this._parentWays = Object.assign(Object.create(base.parentWays), other._parentWays); + this._parentRels = Object.assign(Object.create(base.parentRels), other._parentRels); + } else { + this.entities = Object.create({}); + this._parentWays = Object.create({}); + this._parentRels = Object.create({}); + this.rebase(other || [], [this]); + } + + this.transients = {}; + this._childNodes = {}; + this.frozen = !mutable; + } + coreGraph.prototype = { + hasEntity: function hasEntity(id) { + return this.entities[id]; + }, + entity: function entity(id) { + var entity = this.entities[id]; //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376 + + if (!entity) { + entity = this.entities.__proto__[id]; // eslint-disable-line no-proto + } + + if (!entity) { + throw new Error('entity ' + id + ' not found'); + } + + return entity; + }, + geometry: function geometry(id) { + return this.entity(id).geometry(this); + }, + "transient": function transient(entity, key, fn) { + var id = entity.id; + var transients = this.transients[id] || (this.transients[id] = {}); + + if (transients[key] !== undefined) { + return transients[key]; + } + + transients[key] = fn.call(entity); + return transients[key]; + }, + parentWays: function parentWays(entity) { + var parents = this._parentWays[entity.id]; + var result = []; + + if (parents) { + parents.forEach(function (id) { + result.push(this.entity(id)); + }, this); + } + + return result; + }, + isPoi: function isPoi(entity) { + var parents = this._parentWays[entity.id]; + return !parents || parents.size === 0; + }, + isShared: function isShared(entity) { + var parents = this._parentWays[entity.id]; + return parents && parents.size > 1; + }, + parentRelations: function parentRelations(entity) { + var parents = this._parentRels[entity.id]; + var result = []; + + if (parents) { + parents.forEach(function (id) { + result.push(this.entity(id)); + }, this); + } + + return result; + }, + parentMultipolygons: function parentMultipolygons(entity) { + return this.parentRelations(entity).filter(function (relation) { + return relation.isMultipolygon(); + }); + }, + childNodes: function childNodes(entity) { + if (this._childNodes[entity.id]) return this._childNodes[entity.id]; + if (!entity.nodes) return []; + var nodes = []; + + for (var i = 0; i < entity.nodes.length; i++) { + nodes[i] = this.entity(entity.nodes[i]); + } + this._childNodes[entity.id] = nodes; + return this._childNodes[entity.id]; + }, + base: function base() { + return { + 'entities': Object.getPrototypeOf(this.entities), + 'parentWays': Object.getPrototypeOf(this._parentWays), + 'parentRels': Object.getPrototypeOf(this._parentRels) + }; + }, + // Unlike other graph methods, rebase mutates in place. This is because it + // is used only during the history operation that merges newly downloaded + // data into each state. To external consumers, it should appear as if the + // graph always contained the newly downloaded data. + rebase: function rebase(entities, stack, force) { + var base = this.base(); + var i, j, k, id; + + for (i = 0; i < entities.length; i++) { + var entity = entities[i]; + if (!entity.visible || !force && base.entities[entity.id]) continue; // Merging data into the base graph + + base.entities[entity.id] = entity; + + this._updateCalculated(undefined, entity, base.parentWays, base.parentRels); // Restore provisionally-deleted nodes that are discovered to have an extant parent + + + if (entity.type === 'way') { + for (j = 0; j < entity.nodes.length; j++) { + id = entity.nodes[j]; + + for (k = 1; k < stack.length; k++) { + var ents = stack[k].entities; + + if (ents.hasOwnProperty(id) && ents[id] === undefined) { + delete ents[id]; + } + } + } + } + } + + for (i = 0; i < stack.length; i++) { + stack[i]._updateRebased(); + } + }, + _updateRebased: function _updateRebased() { + var base = this.base(); + Object.keys(this._parentWays).forEach(function (child) { + if (base.parentWays[child]) { + base.parentWays[child].forEach(function (id) { + if (!this.entities.hasOwnProperty(id)) { + this._parentWays[child].add(id); + } + }, this); + } + }, this); + Object.keys(this._parentRels).forEach(function (child) { + if (base.parentRels[child]) { + base.parentRels[child].forEach(function (id) { + if (!this.entities.hasOwnProperty(id)) { + this._parentRels[child].add(id); + } + }, this); + } + }, this); + this.transients = {}; // this._childNodes is not updated, under the assumption that + // ways are always downloaded with their child nodes. + }, + // Updates calculated properties (parentWays, parentRels) for the specified change + _updateCalculated: function _updateCalculated(oldentity, entity, parentWays, parentRels) { + parentWays = parentWays || this._parentWays; + parentRels = parentRels || this._parentRels; + var type = entity && entity.type || oldentity && oldentity.type; + var removed, added, i; + + if (type === 'way') { + // Update parentWays + if (oldentity && entity) { + removed = utilArrayDifference(oldentity.nodes, entity.nodes); + added = utilArrayDifference(entity.nodes, oldentity.nodes); + } else if (oldentity) { + removed = oldentity.nodes; + added = []; + } else if (entity) { + removed = []; + added = entity.nodes; + } + + for (i = 0; i < removed.length; i++) { + // make a copy of prototype property, store as own property, and update.. + parentWays[removed[i]] = new Set(parentWays[removed[i]]); + parentWays[removed[i]]["delete"](oldentity.id); + } + + for (i = 0; i < added.length; i++) { + // make a copy of prototype property, store as own property, and update.. + parentWays[added[i]] = new Set(parentWays[added[i]]); + parentWays[added[i]].add(entity.id); + } + } else if (type === 'relation') { + // Update parentRels + // diff only on the IDs since the same entity can be a member multiple times with different roles + var oldentityMemberIDs = oldentity ? oldentity.members.map(function (m) { + return m.id; + }) : []; + var entityMemberIDs = entity ? entity.members.map(function (m) { + return m.id; + }) : []; + + if (oldentity && entity) { + removed = utilArrayDifference(oldentityMemberIDs, entityMemberIDs); + added = utilArrayDifference(entityMemberIDs, oldentityMemberIDs); + } else if (oldentity) { + removed = oldentityMemberIDs; + added = []; + } else if (entity) { + removed = []; + added = entityMemberIDs; + } + + for (i = 0; i < removed.length; i++) { + // make a copy of prototype property, store as own property, and update.. + parentRels[removed[i]] = new Set(parentRels[removed[i]]); + parentRels[removed[i]]["delete"](oldentity.id); + } + + for (i = 0; i < added.length; i++) { + // make a copy of prototype property, store as own property, and update.. + parentRels[added[i]] = new Set(parentRels[added[i]]); + parentRels[added[i]].add(entity.id); + } + } + }, + replace: function replace(entity) { + if (this.entities[entity.id] === entity) return this; + return this.update(function () { + this._updateCalculated(this.entities[entity.id], entity); + + this.entities[entity.id] = entity; + }); + }, + remove: function remove(entity) { + return this.update(function () { + this._updateCalculated(entity, undefined); + + this.entities[entity.id] = undefined; + }); + }, + revert: function revert(id) { + var baseEntity = this.base().entities[id]; + var headEntity = this.entities[id]; + if (headEntity === baseEntity) return this; + return this.update(function () { + this._updateCalculated(headEntity, baseEntity); + + delete this.entities[id]; + }); + }, + update: function update() { + var graph = this.frozen ? coreGraph(this, true) : this; + + for (var i = 0; i < arguments.length; i++) { + arguments[i].call(graph, graph); + } + + if (this.frozen) graph.frozen = true; + return graph; + }, + // Obliterates any existing entities + load: function load(entities) { + var base = this.base(); + this.entities = Object.create(base.entities); + + for (var i in entities) { + this.entities[i] = entities[i]; + + this._updateCalculated(base.entities[i], this.entities[i]); + } + + return this; + } + }; + + function osmTurn(turn) { + if (!(this instanceof osmTurn)) { + return new osmTurn(turn); + } + + Object.assign(this, turn); + } + function osmIntersection(graph, startVertexId, maxDistance) { + maxDistance = maxDistance || 30; // in meters + + var vgraph = coreGraph(); // virtual graph + + var i, j, k; + + function memberOfRestriction(entity) { + return graph.parentRelations(entity).some(function (r) { + return r.isRestriction(); + }); + } + + function isRoad(way) { + if (way.isArea() || way.isDegenerate()) return false; + var roads = { + 'motorway': true, + 'motorway_link': true, + 'trunk': true, + 'trunk_link': true, + 'primary': true, + 'primary_link': true, + 'secondary': true, + 'secondary_link': true, + 'tertiary': true, + 'tertiary_link': true, + 'residential': true, + 'unclassified': true, + 'living_street': true, + 'service': true, + 'road': true, + 'track': true + }; + return roads[way.tags.highway]; + } + + var startNode = graph.entity(startVertexId); + var checkVertices = [startNode]; + var checkWays; + var vertices = []; + var vertexIds = []; + var vertex; + var ways = []; + var wayIds = []; + var way; + var nodes = []; + var node; + var parents = []; + var parent; // `actions` will store whatever actions must be performed to satisfy + // preconditions for adding a turn restriction to this intersection. + // - Remove any existing degenerate turn restrictions (missing from/to, etc) + // - Reverse oneways so that they are drawn in the forward direction + // - Split ways on key vertices + + var actions = []; // STEP 1: walk the graph outwards from starting vertex to search + // for more key vertices and ways to include in the intersection.. + + while (checkVertices.length) { + vertex = checkVertices.pop(); // check this vertex for parent ways that are roads + + checkWays = graph.parentWays(vertex); + var hasWays = false; + + for (i = 0; i < checkWays.length; i++) { + way = checkWays[i]; + if (!isRoad(way) && !memberOfRestriction(way)) continue; + ways.push(way); // it's a road, or it's already in a turn restriction + + hasWays = true; // check the way's children for more key vertices + + nodes = utilArrayUniq(graph.childNodes(way)); + + for (j = 0; j < nodes.length; j++) { + node = nodes[j]; + if (node === vertex) continue; // same thing + + if (vertices.indexOf(node) !== -1) continue; // seen it already + + if (geoSphericalDistance(node.loc, startNode.loc) > maxDistance) continue; // too far from start + // a key vertex will have parents that are also roads + + var hasParents = false; + parents = graph.parentWays(node); + + for (k = 0; k < parents.length; k++) { + parent = parents[k]; + if (parent === way) continue; // same thing + + if (ways.indexOf(parent) !== -1) continue; // seen it already + + if (!isRoad(parent)) continue; // not a road + + hasParents = true; + break; + } + + if (hasParents) { + checkVertices.push(node); + } + } + } + + if (hasWays) { + vertices.push(vertex); + } + } + + vertices = utilArrayUniq(vertices); + ways = utilArrayUniq(ways); // STEP 2: Build a virtual graph containing only the entities in the intersection.. + // Everything done after this step should act on the virtual graph + // Any actions that must be performed later to the main graph go in `actions` array + + ways.forEach(function (way) { + graph.childNodes(way).forEach(function (node) { + vgraph = vgraph.replace(node); + }); + vgraph = vgraph.replace(way); + graph.parentRelations(way).forEach(function (relation) { + if (relation.isRestriction()) { + if (relation.isValidRestriction(graph)) { + vgraph = vgraph.replace(relation); + } else if (relation.isComplete(graph)) { + actions.push(actionDeleteRelation(relation.id)); + } + } + }); + }); // STEP 3: Force all oneways to be drawn in the forward direction + + ways.forEach(function (w) { + var way = vgraph.entity(w.id); + + if (way.tags.oneway === '-1') { + var action = actionReverse(way.id, { + reverseOneway: true + }); + actions.push(action); + vgraph = action(vgraph); + } + }); // STEP 4: Split ways on key vertices + + var origCount = osmEntity.id.next.way; + vertices.forEach(function (v) { + // This is an odd way to do it, but we need to find all the ways that + // will be split here, then split them one at a time to ensure that these + // actions can be replayed on the main graph exactly in the same order. + // (It is unintuitive, but the order of ways returned from graph.parentWays() + // is arbitrary, depending on how the main graph and vgraph were built) + var splitAll = actionSplit([v.id]).keepHistoryOn('first'); + + if (!splitAll.disabled(vgraph)) { + splitAll.ways(vgraph).forEach(function (way) { + var splitOne = actionSplit([v.id]).limitWays([way.id]).keepHistoryOn('first'); + actions.push(splitOne); + vgraph = splitOne(vgraph); + }); + } + }); // In here is where we should also split the intersection at nearby junction. + // for https://github.com/mapbox/iD-internal/issues/31 + // nearbyVertices.forEach(function(v) { + // }); + // Reasons why we reset the way id count here: + // 1. Continuity with way ids created by the splits so that we can replay + // these actions later if the user decides to create a turn restriction + // 2. Avoids churning way ids just by hovering over a vertex + // and displaying the turn restriction editor + + osmEntity.id.next.way = origCount; // STEP 5: Update arrays to point to vgraph entities + + vertexIds = vertices.map(function (v) { + return v.id; + }); + vertices = []; + ways = []; + vertexIds.forEach(function (id) { + var vertex = vgraph.entity(id); + var parents = vgraph.parentWays(vertex); + vertices.push(vertex); + ways = ways.concat(parents); + }); + vertices = utilArrayUniq(vertices); + ways = utilArrayUniq(ways); + vertexIds = vertices.map(function (v) { + return v.id; + }); + wayIds = ways.map(function (w) { + return w.id; + }); // STEP 6: Update the ways with some metadata that will be useful for + // walking the intersection graph later and rendering turn arrows. + + function withMetadata(way, vertexIds) { + var __oneWay = way.isOneWay(); // which affixes are key vertices? + + + var __first = vertexIds.indexOf(way.first()) !== -1; + + var __last = vertexIds.indexOf(way.last()) !== -1; // what roles is this way eligible for? + + + var __via = __first && __last; + + var __from = __first && !__oneWay || __last; + + var __to = __first || __last && !__oneWay; + + return way.update({ + __first: __first, + __last: __last, + __from: __from, + __via: __via, + __to: __to, + __oneWay: __oneWay + }); + } + + ways = []; + wayIds.forEach(function (id) { + var way = withMetadata(vgraph.entity(id), vertexIds); + vgraph = vgraph.replace(way); + ways.push(way); + }); // STEP 7: Simplify - This is an iterative process where we: + // 1. Find trivial vertices with only 2 parents + // 2. trim off the leaf way from those vertices and remove from vgraph + + var keepGoing; + var removeWayIds = []; + var removeVertexIds = []; + + do { + keepGoing = false; + checkVertices = vertexIds.slice(); + + for (i = 0; i < checkVertices.length; i++) { + var vertexId = checkVertices[i]; + vertex = vgraph.hasEntity(vertexId); + + if (!vertex) { + if (vertexIds.indexOf(vertexId) !== -1) { + vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one + } + + removeVertexIds.push(vertexId); + continue; + } + + parents = vgraph.parentWays(vertex); + + if (parents.length < 3) { + if (vertexIds.indexOf(vertexId) !== -1) { + vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one + } + } + + if (parents.length === 2) { + // vertex with 2 parents is trivial + var a = parents[0]; + var b = parents[1]; + var aIsLeaf = a && !a.__via; + var bIsLeaf = b && !b.__via; + var leaf, survivor; + + if (aIsLeaf && !bIsLeaf) { + leaf = a; + survivor = b; + } else if (!aIsLeaf && bIsLeaf) { + leaf = b; + survivor = a; + } + + if (leaf && survivor) { + survivor = withMetadata(survivor, vertexIds); // update survivor way + + vgraph = vgraph.replace(survivor).remove(leaf); // update graph + + removeWayIds.push(leaf.id); + keepGoing = true; + } + } + + parents = vgraph.parentWays(vertex); + + if (parents.length < 2) { + // vertex is no longer a key vertex + if (vertexIds.indexOf(vertexId) !== -1) { + vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one + } + + removeVertexIds.push(vertexId); + keepGoing = true; + } + + if (parents.length < 1) { + // vertex is no longer attached to anything + vgraph = vgraph.remove(vertex); + } + } + } while (keepGoing); + + vertices = vertices.filter(function (vertex) { + return removeVertexIds.indexOf(vertex.id) === -1; + }).map(function (vertex) { + return vgraph.entity(vertex.id); + }); + ways = ways.filter(function (way) { + return removeWayIds.indexOf(way.id) === -1; + }).map(function (way) { + return vgraph.entity(way.id); + }); // OK! Here is our intersection.. + + var intersection = { + graph: vgraph, + actions: actions, + vertices: vertices, + ways: ways + }; // Get all the valid turns through this intersection given a starting way id. + // This operates on the virtual graph for everything. + // + // Basically, walk through all possible paths from starting way, + // honoring the existing turn restrictions as we go (watch out for loops!) + // + // For each path found, generate and return a `osmTurn` datastructure. + // + + intersection.turns = function (fromWayId, maxViaWay) { + if (!fromWayId) return []; + if (!maxViaWay) maxViaWay = 0; + var vgraph = intersection.graph; + var keyVertexIds = intersection.vertices.map(function (v) { + return v.id; + }); + var start = vgraph.entity(fromWayId); + if (!start || !(start.__from || start.__via)) return []; // maxViaWay=0 from-*-to (0 vias) + // maxViaWay=1 from-*-via-*-to (1 via max) + // maxViaWay=2 from-*-via-*-via-*-to (2 vias max) + + var maxPathLength = maxViaWay * 2 + 3; + var turns = []; + step(start); + return turns; // traverse the intersection graph and find all the valid paths + + function step(entity, currPath, currRestrictions, matchedRestriction) { + currPath = (currPath || []).slice(); // shallow copy + + if (currPath.length >= maxPathLength) return; + currPath.push(entity.id); + currRestrictions = (currRestrictions || []).slice(); // shallow copy + + var i, j; + + if (entity.type === 'node') { + var parents = vgraph.parentWays(entity); + var nextWays = []; // which ways can we step into? + + for (i = 0; i < parents.length; i++) { + var way = parents[i]; // if next way is a oneway incoming to this vertex, skip + + if (way.__oneWay && way.nodes[0] !== entity.id) continue; // if we have seen it before (allowing for an initial u-turn), skip + + if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) continue; // Check all "current" restrictions (where we've already walked the `FROM`) + + var restrict = null; + + for (j = 0; j < currRestrictions.length; j++) { + var restriction = currRestrictions[j]; + var f = restriction.memberByRole('from'); + var v = restriction.membersByRole('via'); + var t = restriction.memberByRole('to'); + var isOnly = /^only_/.test(restriction.tags.restriction); // Does the current path match this turn restriction? + + var matchesFrom = f.id === fromWayId; + var matchesViaTo = false; + var isAlongOnlyPath = false; + + if (t.id === way.id) { + // match TO + if (v.length === 1 && v[0].type === 'node') { + // match VIA node + matchesViaTo = v[0].id === entity.id && (matchesFrom && currPath.length === 2 || !matchesFrom && currPath.length > 2); + } else { + // match all VIA ways + var pathVias = []; + + for (k = 2; k < currPath.length; k += 2) { + // k = 2 skips FROM + pathVias.push(currPath[k]); // (path goes way-node-way...) + } + + var restrictionVias = []; + + for (k = 0; k < v.length; k++) { + if (v[k].type === 'way') { + restrictionVias.push(v[k].id); + } + } + + var diff = utilArrayDifference(pathVias, restrictionVias); + matchesViaTo = !diff.length; + } + } else if (isOnly) { + for (k = 0; k < v.length; k++) { + // way doesn't match TO, but is one of the via ways along the path of an "only" + if (v[k].type === 'way' && v[k].id === way.id) { + isAlongOnlyPath = true; + break; + } + } + } + + if (matchesViaTo) { + if (isOnly) { + restrict = { + id: restriction.id, + direct: matchesFrom, + from: f.id, + only: true, + end: true + }; + } else { + restrict = { + id: restriction.id, + direct: matchesFrom, + from: f.id, + no: true, + end: true + }; + } + } else { + // indirect - caused by a different nearby restriction + if (isAlongOnlyPath) { + restrict = { + id: restriction.id, + direct: false, + from: f.id, + only: true, + end: false + }; + } else if (isOnly) { + restrict = { + id: restriction.id, + direct: false, + from: f.id, + no: true, + end: true + }; + } + } // stop looking if we find a "direct" restriction (matching FROM, VIA, TO) + + + if (restrict && restrict.direct) break; + } + + nextWays.push({ + way: way, + restrict: restrict + }); + } + + nextWays.forEach(function (nextWay) { + step(nextWay.way, currPath, currRestrictions, nextWay.restrict); + }); + } else { + // entity.type === 'way' + if (currPath.length >= 3) { + // this is a "complete" path.. + var turnPath = currPath.slice(); // shallow copy + // an indirect restriction - only include the partial path (starting at FROM) + + if (matchedRestriction && matchedRestriction.direct === false) { + for (i = 0; i < turnPath.length; i++) { + if (turnPath[i] === matchedRestriction.from) { + turnPath = turnPath.slice(i); + break; + } + } + } + + var turn = pathToTurn(turnPath); + + if (turn) { + if (matchedRestriction) { + turn.restrictionID = matchedRestriction.id; + turn.no = matchedRestriction.no; + turn.only = matchedRestriction.only; + turn.direct = matchedRestriction.direct; + } + + turns.push(osmTurn(turn)); + } + + if (currPath[0] === currPath[2]) return; // if we made a u-turn - stop here + } + + if (matchedRestriction && matchedRestriction.end) return; // don't advance any further + // which nodes can we step into? + + var n1 = vgraph.entity(entity.first()); + var n2 = vgraph.entity(entity.last()); + var dist = geoSphericalDistance(n1.loc, n2.loc); + var nextNodes = []; + + if (currPath.length > 1) { + if (dist > maxDistance) return; // the next node is too far + + if (!entity.__via) return; // this way is a leaf / can't be a via + } + + if (!entity.__oneWay && // bidirectional.. + keyVertexIds.indexOf(n1.id) !== -1 && // key vertex.. + currPath.indexOf(n1.id) === -1) { + // haven't seen it yet.. + nextNodes.push(n1); // can advance to first node + } + + if (keyVertexIds.indexOf(n2.id) !== -1 && // key vertex.. + currPath.indexOf(n2.id) === -1) { + // haven't seen it yet.. + nextNodes.push(n2); // can advance to last node + } + + nextNodes.forEach(function (nextNode) { + // gather restrictions FROM this way + var fromRestrictions = vgraph.parentRelations(entity).filter(function (r) { + if (!r.isRestriction()) return false; + var f = r.memberByRole('from'); + if (!f || f.id !== entity.id) return false; + var isOnly = /^only_/.test(r.tags.restriction); + if (!isOnly) return true; // `only_` restrictions only matter along the direction of the VIA - #4849 + + var isOnlyVia = false; + var v = r.membersByRole('via'); + + if (v.length === 1 && v[0].type === 'node') { + // via node + isOnlyVia = v[0].id === nextNode.id; + } else { + // via way(s) + for (var i = 0; i < v.length; i++) { + if (v[i].type !== 'way') continue; + var viaWay = vgraph.entity(v[i].id); + + if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) { + isOnlyVia = true; + break; + } + } + } + + return isOnlyVia; + }); + step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false); + }); + } + } // assumes path is alternating way-node-way of odd length + + + function pathToTurn(path) { + if (path.length < 3) return; + var fromWayId, fromNodeId, fromVertexId; + var toWayId, toNodeId, toVertexId; + var viaWayIds, viaNodeId, isUturn; + fromWayId = path[0]; + toWayId = path[path.length - 1]; + + if (path.length === 3 && fromWayId === toWayId) { + // u turn + var way = vgraph.entity(fromWayId); + if (way.__oneWay) return null; + isUturn = true; + viaNodeId = fromVertexId = toVertexId = path[1]; + fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId); + } else { + isUturn = false; + fromVertexId = path[1]; + fromNodeId = adjacentNode(fromWayId, fromVertexId); + toVertexId = path[path.length - 2]; + toNodeId = adjacentNode(toWayId, toVertexId); + + if (path.length === 3) { + viaNodeId = path[1]; + } else { + viaWayIds = path.filter(function (entityId) { + return entityId[0] === 'w'; + }); + viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1); // remove first, last + } + } + + return { + key: path.join('_'), + path: path, + from: { + node: fromNodeId, + way: fromWayId, + vertex: fromVertexId + }, + via: { + node: viaNodeId, + ways: viaWayIds + }, + to: { + node: toNodeId, + way: toWayId, + vertex: toVertexId + }, + u: isUturn + }; + + function adjacentNode(wayId, affixId) { + var nodes = vgraph.entity(wayId).nodes; + return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2]; + } + } + }; + + return intersection; + } + function osmInferRestriction(graph, turn, projection) { + var fromWay = graph.entity(turn.from.way); + var fromNode = graph.entity(turn.from.node); + var fromVertex = graph.entity(turn.from.vertex); + var toWay = graph.entity(turn.to.way); + var toNode = graph.entity(turn.to.node); + var toVertex = graph.entity(turn.to.vertex); + var fromOneWay = fromWay.tags.oneway === 'yes'; + var toOneWay = toWay.tags.oneway === 'yes'; + var angle = (geoAngle(fromVertex, fromNode, projection) - geoAngle(toVertex, toNode, projection)) * 180 / Math.PI; + + while (angle < 0) { + angle += 360; + } + + if (fromNode === toNode) { + return 'no_u_turn'; + } + + if ((angle < 23 || angle > 336) && fromOneWay && toOneWay) { + return 'no_u_turn'; // wider tolerance for u-turn if both ways are oneway + } + + if ((angle < 40 || angle > 319) && fromOneWay && toOneWay && turn.from.vertex !== turn.to.vertex) { + return 'no_u_turn'; // even wider tolerance for u-turn if there is a via way (from !== to) + } + + if (angle < 158) { + return 'no_right_turn'; + } + + if (angle > 202) { + return 'no_left_turn'; + } + + return 'no_straight_on'; + } + + function actionMergePolygon(ids, newRelationId) { + function groupEntities(graph) { + var entities = ids.map(function (id) { + return graph.entity(id); + }); + var geometryGroups = utilArrayGroupBy(entities, function (entity) { + if (entity.type === 'way' && entity.isClosed()) { + return 'closedWay'; + } else if (entity.type === 'relation' && entity.isMultipolygon()) { + return 'multipolygon'; + } else { + return 'other'; + } + }); + return Object.assign({ + closedWay: [], + multipolygon: [], + other: [] + }, geometryGroups); + } + + var action = function action(graph) { + var entities = groupEntities(graph); // An array representing all the polygons that are part of the multipolygon. + // + // Each element is itself an array of objects with an id property, and has a + // locs property which is an array of the locations forming the polygon. + + var polygons = entities.multipolygon.reduce(function (polygons, m) { + return polygons.concat(osmJoinWays(m.members, graph)); + }, []).concat(entities.closedWay.map(function (d) { + var member = [{ + id: d.id + }]; + member.nodes = graph.childNodes(d); + return member; + })); // contained is an array of arrays of boolean values, + // where contained[j][k] is true iff the jth way is + // contained by the kth way. + + var contained = polygons.map(function (w, i) { + return polygons.map(function (d, n) { + if (i === n) return null; + return geoPolygonContainsPolygon(d.nodes.map(function (n) { + return n.loc; + }), w.nodes.map(function (n) { + return n.loc; + })); + }); + }); // Sort all polygons as either outer or inner ways + + var members = []; + var outer = true; + + while (polygons.length) { + extractUncontained(polygons); + polygons = polygons.filter(isContained); + contained = contained.filter(isContained).map(filterContained); + } + + function isContained(d, i) { + return contained[i].some(function (val) { + return val; + }); + } + + function filterContained(d) { + return d.filter(isContained); + } + + function extractUncontained(polygons) { + polygons.forEach(function (d, i) { + if (!isContained(d, i)) { + d.forEach(function (member) { + members.push({ + type: 'way', + id: member.id, + role: outer ? 'outer' : 'inner' + }); + }); + } + }); + outer = !outer; + } // Move all tags to one relation + + + var relation = entities.multipolygon[0] || osmRelation({ + id: newRelationId, + tags: { + type: 'multipolygon' + } + }); + entities.multipolygon.slice(1).forEach(function (m) { + relation = relation.mergeTags(m.tags); + graph = graph.remove(m); + }); + entities.closedWay.forEach(function (way) { + function isThisOuter(m) { + return m.id === way.id && m.role !== 'inner'; + } + + if (members.some(isThisOuter)) { + relation = relation.mergeTags(way.tags); + graph = graph.replace(way.update({ + tags: {} + })); + } + }); + return graph.replace(relation.update({ + members: members, + tags: utilObjectOmit(relation.tags, ['area']) + })); + }; + + action.disabled = function (graph) { + var entities = groupEntities(graph); + + if (entities.other.length > 0 || entities.closedWay.length + entities.multipolygon.length < 2) { + return 'not_eligible'; + } + + if (!entities.multipolygon.every(function (r) { + return r.isComplete(graph); + })) { + return 'incomplete_relation'; + } + + if (!entities.multipolygon.length) { + var sharedMultipolygons = []; + entities.closedWay.forEach(function (way, i) { + if (i === 0) { + sharedMultipolygons = graph.parentMultipolygons(way); + } else { + sharedMultipolygons = utilArrayIntersection(sharedMultipolygons, graph.parentMultipolygons(way)); + } + }); + sharedMultipolygons = sharedMultipolygons.filter(function (relation) { + return relation.members.length === entities.closedWay.length; + }); + + if (sharedMultipolygons.length) { + // don't create a new multipolygon if it'd be redundant + return 'not_eligible'; + } + } else if (entities.closedWay.some(function (way) { + return utilArrayIntersection(graph.parentMultipolygons(way), entities.multipolygon).length; + })) { + // don't add a way to a multipolygon again if it's already a member + return 'not_eligible'; + } + }; + + return action; + } + + var DESCRIPTORS = descriptors; + var objectDefinePropertyModule = objectDefineProperty; + var regExpFlags = regexpFlags$1; + var fails$4 = fails$N; + + var FORCED$2 = DESCRIPTORS && fails$4(function () { + // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe + return Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags').get.call({ dotAll: true, sticky: true }) !== 'sy'; + }); + + // `RegExp.prototype.flags` getter + // https://tc39.es/ecma262/#sec-get-regexp.prototype.flags + if (FORCED$2) objectDefinePropertyModule.f(RegExp.prototype, 'flags', { + configurable: true, + get: regExpFlags + }); + + var fastDeepEqual = function equal(a, b) { + if (a === b) return true; + + if (a && b && _typeof(a) == 'object' && _typeof(b) == 'object') { + if (a.constructor !== b.constructor) return false; + var length, i, keys; + + if (Array.isArray(a)) { + length = a.length; + if (length != b.length) return false; + + for (i = length; i-- !== 0;) { + if (!equal(a[i], b[i])) return false; + } + + return true; + } + + if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags; + if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf(); + if (a.toString !== Object.prototype.toString) return a.toString() === b.toString(); + keys = Object.keys(a); + length = keys.length; + if (length !== Object.keys(b).length) return false; + + for (i = length; i-- !== 0;) { + if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; + } + + for (i = length; i-- !== 0;) { + var key = keys[i]; + if (!equal(a[key], b[key])) return false; + } + + return true; + } // true if both NaN, false otherwise + + + return a !== a && b !== b; + }; + + // J. W. Hunt and M. D. McIlroy, An algorithm for differential buffer + // comparison, Bell Telephone Laboratories CSTR #41 (1976) + // http://www.cs.dartmouth.edu/~doug/ + // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem + // + // Expects two arrays, finds longest common sequence + + function LCS(buffer1, buffer2) { + var equivalenceClasses = {}; + + for (var j = 0; j < buffer2.length; j++) { + var item = buffer2[j]; + + if (equivalenceClasses[item]) { + equivalenceClasses[item].push(j); + } else { + equivalenceClasses[item] = [j]; + } + } + + var NULLRESULT = { + buffer1index: -1, + buffer2index: -1, + chain: null + }; + var candidates = [NULLRESULT]; + + for (var i = 0; i < buffer1.length; i++) { + var _item = buffer1[i]; + var buffer2indices = equivalenceClasses[_item] || []; + var r = 0; + var c = candidates[0]; + + for (var jx = 0; jx < buffer2indices.length; jx++) { + var _j = buffer2indices[jx]; + var s = void 0; + + for (s = r; s < candidates.length; s++) { + if (candidates[s].buffer2index < _j && (s === candidates.length - 1 || candidates[s + 1].buffer2index > _j)) { + break; + } + } + + if (s < candidates.length) { + var newCandidate = { + buffer1index: i, + buffer2index: _j, + chain: candidates[s] + }; + + if (r === candidates.length) { + candidates.push(c); + } else { + candidates[r] = c; + } + + r = s + 1; + c = newCandidate; + + if (r === candidates.length) { + break; // no point in examining further (j)s + } + } + } + + candidates[r] = c; + } // At this point, we know the LCS: it's in the reverse of the + // linked-list through .chain of candidates[candidates.length - 1]. + + + return candidates[candidates.length - 1]; + } // We apply the LCS to build a 'comm'-style picture of the + // offsets and lengths of mismatched chunks in the input + // buffers. This is used by diff3MergeRegions. + + + function diffIndices(buffer1, buffer2) { + var lcs = LCS(buffer1, buffer2); + var result = []; + var tail1 = buffer1.length; + var tail2 = buffer2.length; + + for (var candidate = lcs; candidate !== null; candidate = candidate.chain) { + var mismatchLength1 = tail1 - candidate.buffer1index - 1; + var mismatchLength2 = tail2 - candidate.buffer2index - 1; + tail1 = candidate.buffer1index; + tail2 = candidate.buffer2index; + + if (mismatchLength1 || mismatchLength2) { + result.push({ + buffer1: [tail1 + 1, mismatchLength1], + buffer1Content: buffer1.slice(tail1 + 1, tail1 + 1 + mismatchLength1), + buffer2: [tail2 + 1, mismatchLength2], + buffer2Content: buffer2.slice(tail2 + 1, tail2 + 1 + mismatchLength2) + }); + } + } + + result.reverse(); + return result; + } // We apply the LCS to build a JSON representation of a + // independently derived from O, returns a fairly complicated + // internal representation of merge decisions it's taken. The + // interested reader may wish to consult + // + // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce. + // 'A Formal Investigation of ' In Arvind and Prasad, + // editors, Foundations of Software Technology and Theoretical + // Computer Science (FSTTCS), December 2007. + // + // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf) + // + + + function diff3MergeRegions(a, o, b) { + // "hunks" are array subsets where `a` or `b` are different from `o` + // https://www.gnu.org/software/diffutils/manual/html_node/diff3-Hunks.html + var hunks = []; + + function addHunk(h, ab) { + hunks.push({ + ab: ab, + oStart: h.buffer1[0], + oLength: h.buffer1[1], + // length of o to remove + abStart: h.buffer2[0], + abLength: h.buffer2[1] // length of a/b to insert + // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1]) + + }); + } + + diffIndices(o, a).forEach(function (item) { + return addHunk(item, 'a'); + }); + diffIndices(o, b).forEach(function (item) { + return addHunk(item, 'b'); + }); + hunks.sort(function (x, y) { + return x.oStart - y.oStart; + }); + var results = []; + var currOffset = 0; + + function advanceTo(endOffset) { + if (endOffset > currOffset) { + results.push({ + stable: true, + buffer: 'o', + bufferStart: currOffset, + bufferLength: endOffset - currOffset, + bufferContent: o.slice(currOffset, endOffset) + }); + currOffset = endOffset; + } + } + + while (hunks.length) { + var hunk = hunks.shift(); + var regionStart = hunk.oStart; + var regionEnd = hunk.oStart + hunk.oLength; + var regionHunks = [hunk]; + advanceTo(regionStart); // Try to pull next overlapping hunk into this region + + while (hunks.length) { + var nextHunk = hunks[0]; + var nextHunkStart = nextHunk.oStart; + if (nextHunkStart > regionEnd) break; // no overlap + + regionEnd = Math.max(regionEnd, nextHunkStart + nextHunk.oLength); + regionHunks.push(hunks.shift()); + } + + if (regionHunks.length === 1) { + // Only one hunk touches this region, meaning that there is no conflict here. + // Either `a` or `b` is inserting into a region of `o` unchanged by the other. + if (hunk.abLength > 0) { + var buffer = hunk.ab === 'a' ? a : b; + results.push({ + stable: true, + buffer: hunk.ab, + bufferStart: hunk.abStart, + bufferLength: hunk.abLength, + bufferContent: buffer.slice(hunk.abStart, hunk.abStart + hunk.abLength) + }); + } + } else { + // A true a/b conflict. Determine the bounds involved from `a`, `o`, and `b`. + // Effectively merge all the `a` hunks into one giant hunk, then do the + // same for the `b` hunks; then, correct for skew in the regions of `o` + // that each side changed, and report appropriate spans for the three sides. + var bounds = { + a: [a.length, -1, o.length, -1], + b: [b.length, -1, o.length, -1] + }; + + while (regionHunks.length) { + hunk = regionHunks.shift(); + var oStart = hunk.oStart; + var oEnd = oStart + hunk.oLength; + var abStart = hunk.abStart; + var abEnd = abStart + hunk.abLength; + var _b = bounds[hunk.ab]; + _b[0] = Math.min(abStart, _b[0]); + _b[1] = Math.max(abEnd, _b[1]); + _b[2] = Math.min(oStart, _b[2]); + _b[3] = Math.max(oEnd, _b[3]); + } + + var aStart = bounds.a[0] + (regionStart - bounds.a[2]); + var aEnd = bounds.a[1] + (regionEnd - bounds.a[3]); + var bStart = bounds.b[0] + (regionStart - bounds.b[2]); + var bEnd = bounds.b[1] + (regionEnd - bounds.b[3]); + var result = { + stable: false, + aStart: aStart, + aLength: aEnd - aStart, + aContent: a.slice(aStart, aEnd), + oStart: regionStart, + oLength: regionEnd - regionStart, + oContent: o.slice(regionStart, regionEnd), + bStart: bStart, + bLength: bEnd - bStart, + bContent: b.slice(bStart, bEnd) + }; + results.push(result); + } + + currOffset = regionEnd; + } + + advanceTo(o.length); + return results; + } // Applies the output of diff3MergeRegions to actually + // construct the merged buffer; the returned result alternates + // between 'ok' and 'conflict' blocks. + // A "false conflict" is where `a` and `b` both change the same from `o` + + + function diff3Merge(a, o, b, options) { + var defaults = { + excludeFalseConflicts: true, + stringSeparator: /\s+/ + }; + options = Object.assign(defaults, options); + var aString = typeof a === 'string'; + var oString = typeof o === 'string'; + var bString = typeof b === 'string'; + if (aString) a = a.split(options.stringSeparator); + if (oString) o = o.split(options.stringSeparator); + if (bString) b = b.split(options.stringSeparator); + var results = []; + var regions = diff3MergeRegions(a, o, b); + var okBuffer = []; + + function flushOk() { + if (okBuffer.length) { + results.push({ + ok: okBuffer + }); + } + + okBuffer = []; + } + + function isFalseConflict(a, b) { + if (a.length !== b.length) return false; + + for (var i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; + } + + return true; + } + + regions.forEach(function (region) { + if (region.stable) { + var _okBuffer; + + (_okBuffer = okBuffer).push.apply(_okBuffer, _toConsumableArray(region.bufferContent)); + } else { + if (options.excludeFalseConflicts && isFalseConflict(region.aContent, region.bContent)) { + var _okBuffer2; + + (_okBuffer2 = okBuffer).push.apply(_okBuffer2, _toConsumableArray(region.aContent)); + } else { + flushOk(); + results.push({ + conflict: { + a: region.aContent, + aIndex: region.aStart, + o: region.oContent, + oIndex: region.oStart, + b: region.bContent, + bIndex: region.bStart + } + }); + } + } + }); + flushOk(); + return results; + } + + function actionMergeRemoteChanges(id, localGraph, remoteGraph, discardTags, formatUser) { + discardTags = discardTags || {}; + var _option = 'safe'; // 'safe', 'force_local', 'force_remote' + + var _conflicts = []; + + function user(d) { + return typeof formatUser === 'function' ? formatUser(d) : d; + } + + function mergeLocation(remote, target) { + function pointEqual(a, b) { + var epsilon = 1e-6; + return Math.abs(a[0] - b[0]) < epsilon && Math.abs(a[1] - b[1]) < epsilon; + } + + if (_option === 'force_local' || pointEqual(target.loc, remote.loc)) { + return target; + } + + if (_option === 'force_remote') { + return target.update({ + loc: remote.loc + }); + } + + _conflicts.push(_t('merge_remote_changes.conflict.location', { + user: user(remote.user) + })); + + return target; + } + + function mergeNodes(base, remote, target) { + if (_option === 'force_local' || fastDeepEqual(target.nodes, remote.nodes)) { + return target; + } + + if (_option === 'force_remote') { + return target.update({ + nodes: remote.nodes + }); + } + + var ccount = _conflicts.length; + var o = base.nodes || []; + var a = target.nodes || []; + var b = remote.nodes || []; + var nodes = []; + var hunks = diff3Merge(a, o, b, { + excludeFalseConflicts: true + }); + + for (var i = 0; i < hunks.length; i++) { + var hunk = hunks[i]; + + if (hunk.ok) { + nodes.push.apply(nodes, hunk.ok); + } else { + // for all conflicts, we can assume c.a !== c.b + // because `diff3Merge` called with `true` option to exclude false conflicts.. + var c = hunk.conflict; + + if (fastDeepEqual(c.o, c.a)) { + // only changed remotely + nodes.push.apply(nodes, c.b); + } else if (fastDeepEqual(c.o, c.b)) { + // only changed locally + nodes.push.apply(nodes, c.a); + } else { + // changed both locally and remotely + _conflicts.push(_t('merge_remote_changes.conflict.nodelist', { + user: user(remote.user) + })); + + break; + } + } + } + + return _conflicts.length === ccount ? target.update({ + nodes: nodes + }) : target; + } + + function mergeChildren(targetWay, children, updates, graph) { + function isUsed(node, targetWay) { + var hasInterestingParent = graph.parentWays(node).some(function (way) { + return way.id !== targetWay.id; + }); + return node.hasInterestingTags() || hasInterestingParent || graph.parentRelations(node).length > 0; + } + + var ccount = _conflicts.length; + + for (var i = 0; i < children.length; i++) { + var id = children[i]; + var node = graph.hasEntity(id); // remove unused childNodes.. + + if (targetWay.nodes.indexOf(id) === -1) { + if (node && !isUsed(node, targetWay)) { + updates.removeIds.push(id); + } + + continue; + } // restore used childNodes.. + + + var local = localGraph.hasEntity(id); + var remote = remoteGraph.hasEntity(id); + var target; + + if (_option === 'force_remote' && remote && remote.visible) { + updates.replacements.push(remote); + } else if (_option === 'force_local' && local) { + target = osmEntity(local); + + if (remote) { + target = target.update({ + version: remote.version + }); + } + + updates.replacements.push(target); + } else if (_option === 'safe' && local && remote && local.version !== remote.version) { + target = osmEntity(local, { + version: remote.version + }); + + if (remote.visible) { + target = mergeLocation(remote, target); + } else { + _conflicts.push(_t('merge_remote_changes.conflict.deleted', { + user: user(remote.user) + })); + } + + if (_conflicts.length !== ccount) break; + updates.replacements.push(target); + } + } + + return targetWay; + } + + function updateChildren(updates, graph) { + for (var i = 0; i < updates.replacements.length; i++) { + graph = graph.replace(updates.replacements[i]); + } + + if (updates.removeIds.length) { + graph = actionDeleteMultiple(updates.removeIds)(graph); + } + + return graph; + } + + function mergeMembers(remote, target) { + if (_option === 'force_local' || fastDeepEqual(target.members, remote.members)) { + return target; + } + + if (_option === 'force_remote') { + return target.update({ + members: remote.members + }); + } + + _conflicts.push(_t('merge_remote_changes.conflict.memberlist', { + user: user(remote.user) + })); + + return target; + } + + function mergeTags(base, remote, target) { + if (_option === 'force_local' || fastDeepEqual(target.tags, remote.tags)) { + return target; + } + + if (_option === 'force_remote') { + return target.update({ + tags: remote.tags + }); + } + + var ccount = _conflicts.length; + var o = base.tags || {}; + var a = target.tags || {}; + var b = remote.tags || {}; + var keys = utilArrayUnion(utilArrayUnion(Object.keys(o), Object.keys(a)), Object.keys(b)).filter(function (k) { + return !discardTags[k]; + }); + var tags = Object.assign({}, a); // shallow copy + + var changed = false; + + for (var i = 0; i < keys.length; i++) { + var k = keys[i]; + + if (o[k] !== b[k] && a[k] !== b[k]) { + // changed remotely.. + if (o[k] !== a[k]) { + // changed locally.. + _conflicts.push(_t('merge_remote_changes.conflict.tags', { + tag: k, + local: a[k], + remote: b[k], + user: user(remote.user) + })); + } else { + // unchanged locally, accept remote change.. + if (b.hasOwnProperty(k)) { + tags[k] = b[k]; + } else { + delete tags[k]; + } + + changed = true; + } + } + } + + return changed && _conflicts.length === ccount ? target.update({ + tags: tags + }) : target; + } // `graph.base()` is the common ancestor of the two graphs. + // `localGraph` contains user's edits up to saving + // `remoteGraph` contains remote edits to modified nodes + // `graph` must be a descendent of `localGraph` and may include + // some conflict resolution actions performed on it. + // + // --- ... --- `localGraph` -- ... -- `graph` + // / + // `graph.base()` --- ... --- `remoteGraph` + // + + + var action = function action(graph) { + var updates = { + replacements: [], + removeIds: [] + }; + var base = graph.base().entities[id]; + var local = localGraph.entity(id); + var remote = remoteGraph.entity(id); + var target = osmEntity(local, { + version: remote.version + }); // delete/undelete + + if (!remote.visible) { + if (_option === 'force_remote') { + return actionDeleteMultiple([id])(graph); + } else if (_option === 'force_local') { + if (target.type === 'way') { + target = mergeChildren(target, utilArrayUniq(local.nodes), updates, graph); + graph = updateChildren(updates, graph); + } + + return graph.replace(target); + } else { + _conflicts.push(_t('merge_remote_changes.conflict.deleted', { + user: user(remote.user) + })); + + return graph; // do nothing + } + } // merge + + + if (target.type === 'node') { + target = mergeLocation(remote, target); + } else if (target.type === 'way') { + // pull in any child nodes that may not be present locally.. + graph.rebase(remoteGraph.childNodes(remote), [graph], false); + target = mergeNodes(base, remote, target); + target = mergeChildren(target, utilArrayUnion(local.nodes, remote.nodes), updates, graph); + } else if (target.type === 'relation') { + target = mergeMembers(remote, target); + } + + target = mergeTags(base, remote, target); + + if (!_conflicts.length) { + graph = updateChildren(updates, graph).replace(target); + } + + return graph; + }; + + action.withOption = function (opt) { + _option = opt; + return action; + }; + + action.conflicts = function () { + return _conflicts; + }; + + return action; + } + + // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as + + function actionMove(moveIDs, tryDelta, projection, cache) { + var _delta = tryDelta; + + function setupCache(graph) { + function canMove(nodeID) { + // Allow movement of any node that is in the selectedIDs list.. + if (moveIDs.indexOf(nodeID) !== -1) return true; // Allow movement of a vertex where 2 ways meet.. + + var parents = graph.parentWays(graph.entity(nodeID)); + if (parents.length < 3) return true; // Restrict movement of a vertex where >2 ways meet, unless all parentWays are moving too.. + + var parentsMoving = parents.every(function (way) { + return cache.moving[way.id]; + }); + if (!parentsMoving) delete cache.moving[nodeID]; + return parentsMoving; + } + + function cacheEntities(ids) { + for (var i = 0; i < ids.length; i++) { + var id = ids[i]; + if (cache.moving[id]) continue; + cache.moving[id] = true; + var entity = graph.hasEntity(id); + if (!entity) continue; + + if (entity.type === 'node') { + cache.nodes.push(id); + cache.startLoc[id] = entity.loc; + } else if (entity.type === 'way') { + cache.ways.push(id); + cacheEntities(entity.nodes); + } else { + cacheEntities(entity.members.map(function (member) { + return member.id; + })); + } + } + } + + function cacheIntersections(ids) { + function isEndpoint(way, id) { + return !way.isClosed() && !!way.affix(id); + } + + for (var i = 0; i < ids.length; i++) { + var id = ids[i]; // consider only intersections with 1 moved and 1 unmoved way. + + var childNodes = graph.childNodes(graph.entity(id)); + + for (var j = 0; j < childNodes.length; j++) { + var node = childNodes[j]; + var parents = graph.parentWays(node); + if (parents.length !== 2) continue; + var moved = graph.entity(id); + var unmoved = null; + + for (var k = 0; k < parents.length; k++) { + var way = parents[k]; + + if (!cache.moving[way.id]) { + unmoved = way; + break; + } + } + + if (!unmoved) continue; // exclude ways that are overly connected.. + + if (utilArrayIntersection(moved.nodes, unmoved.nodes).length > 2) continue; + if (moved.isArea() || unmoved.isArea()) continue; + cache.intersections.push({ + nodeId: node.id, + movedId: moved.id, + unmovedId: unmoved.id, + movedIsEP: isEndpoint(moved, node.id), + unmovedIsEP: isEndpoint(unmoved, node.id) + }); + } + } + } + + if (!cache) { + cache = {}; + } + + if (!cache.ok) { + cache.moving = {}; + cache.intersections = []; + cache.replacedVertex = {}; + cache.startLoc = {}; + cache.nodes = []; + cache.ways = []; + cacheEntities(moveIDs); + cacheIntersections(cache.ways); + cache.nodes = cache.nodes.filter(canMove); + cache.ok = true; + } + } // Place a vertex where the moved vertex used to be, to preserve way shape.. + // + // Start: + // b ---- e + // / \ + // / \ + // / \ + // a c + // + // * node '*' added to preserve shape + // / \ + // / b ---- e way `b,e` moved here: + // / \ + // a c + // + // + + + function replaceMovedVertex(nodeId, wayId, graph, delta) { + var way = graph.entity(wayId); + var moved = graph.entity(nodeId); + var movedIndex = way.nodes.indexOf(nodeId); + var len, prevIndex, nextIndex; + + if (way.isClosed()) { + len = way.nodes.length - 1; + prevIndex = (movedIndex + len - 1) % len; + nextIndex = (movedIndex + len + 1) % len; + } else { + len = way.nodes.length; + prevIndex = movedIndex - 1; + nextIndex = movedIndex + 1; + } + + var prev = graph.hasEntity(way.nodes[prevIndex]); + var next = graph.hasEntity(way.nodes[nextIndex]); // Don't add orig vertex at endpoint.. + + if (!prev || !next) return graph; + var key = wayId + '_' + nodeId; + var orig = cache.replacedVertex[key]; + + if (!orig) { + orig = osmNode(); + cache.replacedVertex[key] = orig; + cache.startLoc[orig.id] = cache.startLoc[nodeId]; + } + + var start, end; + + if (delta) { + start = projection(cache.startLoc[nodeId]); + end = projection.invert(geoVecAdd(start, delta)); + } else { + end = cache.startLoc[nodeId]; + } + + orig = orig.move(end); + var angle = Math.abs(geoAngle(orig, prev, projection) - geoAngle(orig, next, projection)) * 180 / Math.PI; // Don't add orig vertex if it would just make a straight line.. + + if (angle > 175 && angle < 185) return graph; // moving forward or backward along way? + + var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection); + var p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection); + var d1 = geoPathLength(p1); + var d2 = geoPathLength(p2); + var insertAt = d1 <= d2 ? movedIndex : nextIndex; // moving around closed loop? + + if (way.isClosed() && insertAt === 0) insertAt = len; + way = way.addNode(orig.id, insertAt); + return graph.replace(orig).replace(way); + } // Remove duplicate vertex that might have been added by + // replaceMovedVertex. This is done after the unzorro checks. + + + function removeDuplicateVertices(wayId, graph) { + var way = graph.entity(wayId); + var epsilon = 1e-6; + var prev, curr; + + function isInteresting(node, graph) { + return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags(); + } + + for (var i = 0; i < way.nodes.length; i++) { + curr = graph.entity(way.nodes[i]); + + if (prev && curr && geoVecEqual(prev.loc, curr.loc, epsilon)) { + if (!isInteresting(prev, graph)) { + way = way.removeNode(prev.id); + graph = graph.replace(way).remove(prev); + } else if (!isInteresting(curr, graph)) { + way = way.removeNode(curr.id); + graph = graph.replace(way).remove(curr); + } + } + + prev = curr; + } + + return graph; + } // Reorder nodes around intersections that have moved.. + // + // Start: way1.nodes: b,e (moving) + // a - b - c ----- d way2.nodes: a,b,c,d (static) + // | vertex: b + // e isEP1: true, isEP2, false + // + // way1 `b,e` moved here: + // a ----- c = b - d + // | + // e + // + // reorder nodes way1.nodes: b,e + // a ----- c - b - d way2.nodes: a,c,b,d + // | + // e + // + + + function unZorroIntersection(intersection, graph) { + var vertex = graph.entity(intersection.nodeId); + var way1 = graph.entity(intersection.movedId); + var way2 = graph.entity(intersection.unmovedId); + var isEP1 = intersection.movedIsEP; + var isEP2 = intersection.unmovedIsEP; // don't move the vertex if it is the endpoint of both ways. + + if (isEP1 && isEP2) return graph; + var nodes1 = graph.childNodes(way1).filter(function (n) { + return n !== vertex; + }); + var nodes2 = graph.childNodes(way2).filter(function (n) { + return n !== vertex; + }); + if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]); + if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]); + var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection); + var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection); + var loc; // snap vertex to nearest edge (or some point between them).. + + if (!isEP1 && !isEP2) { + var epsilon = 1e-6, + maxIter = 10; + + for (var i = 0; i < maxIter; i++) { + loc = geoVecInterp(edge1.loc, edge2.loc, 0.5); + edge1 = geoChooseEdge(nodes1, projection(loc), projection); + edge2 = geoChooseEdge(nodes2, projection(loc), projection); + if (Math.abs(edge1.distance - edge2.distance) < epsilon) break; + } + } else if (!isEP1) { + loc = edge1.loc; + } else { + loc = edge2.loc; + } + + graph = graph.replace(vertex.move(loc)); // if zorro happened, reorder nodes.. + + if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) { + way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index); + graph = graph.replace(way1); + } + + if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) { + way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index); + graph = graph.replace(way2); + } + + return graph; + } + + function cleanupIntersections(graph) { + for (var i = 0; i < cache.intersections.length; i++) { + var obj = cache.intersections[i]; + graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta); + graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null); + graph = unZorroIntersection(obj, graph); + graph = removeDuplicateVertices(obj.movedId, graph); + graph = removeDuplicateVertices(obj.unmovedId, graph); + } + + return graph; + } // check if moving way endpoint can cross an unmoved way, if so limit delta.. + + + function limitDelta(graph) { + function moveNode(loc) { + return geoVecAdd(projection(loc), _delta); + } + + for (var i = 0; i < cache.intersections.length; i++) { + var obj = cache.intersections[i]; // Don't limit movement if this is vertex joins 2 endpoints.. + + if (obj.movedIsEP && obj.unmovedIsEP) continue; // Don't limit movement if this vertex is not an endpoint anyway.. + + if (!obj.movedIsEP) continue; + var node = graph.entity(obj.nodeId); + var start = projection(node.loc); + var end = geoVecAdd(start, _delta); + var movedNodes = graph.childNodes(graph.entity(obj.movedId)); + var movedPath = movedNodes.map(function (n) { + return moveNode(n.loc); + }); + var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId)); + var unmovedPath = unmovedNodes.map(function (n) { + return projection(n.loc); + }); + var hits = geoPathIntersections(movedPath, unmovedPath); + + for (var j = 0; i < hits.length; i++) { + if (geoVecEqual(hits[j], end)) continue; + var edge = geoChooseEdge(unmovedNodes, end, projection); + _delta = geoVecSubtract(projection(edge.loc), start); + } + } + } + + var action = function action(graph) { + if (_delta[0] === 0 && _delta[1] === 0) return graph; + setupCache(graph); + + if (cache.intersections.length) { + limitDelta(graph); + } + + for (var i = 0; i < cache.nodes.length; i++) { + var node = graph.entity(cache.nodes[i]); + var start = projection(node.loc); + var end = geoVecAdd(start, _delta); + graph = graph.replace(node.move(projection.invert(end))); + } + + if (cache.intersections.length) { + graph = cleanupIntersections(graph); + } + + return graph; + }; + + action.delta = function () { + return _delta; + }; + + return action; + } + + function actionMoveMember(relationId, fromIndex, toIndex) { + return function (graph) { + return graph.replace(graph.entity(relationId).moveMember(fromIndex, toIndex)); + }; + } + + function actionMoveNode(nodeID, toLoc) { + var action = function action(graph, t) { + if (t === null || !isFinite(t)) t = 1; + t = Math.min(Math.max(+t, 0), 1); + var node = graph.entity(nodeID); + return graph.replace(node.move(geoVecInterp(node.loc, toLoc, t))); + }; + + action.transitionable = true; + return action; + } + + function actionNoop() { + return function (graph) { + return graph; + }; + } + + function actionOrthogonalize(wayID, projection, vertexID, degThresh, ep) { + var epsilon = ep || 1e-4; + var threshold = degThresh || 13; // degrees within right or straight to alter + // We test normalized dot products so we can compare as cos(angle) + + var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180); + var upperThreshold = Math.cos(threshold * Math.PI / 180); + + var action = function action(graph, t) { + if (t === null || !isFinite(t)) t = 1; + t = Math.min(Math.max(+t, 0), 1); + var way = graph.entity(wayID); + way = way.removeNode(''); // sanity check - remove any consecutive duplicates + + if (way.tags.nonsquare) { + var tags = Object.assign({}, way.tags); // since we're squaring, remove indication that this is physically unsquare + + delete tags.nonsquare; + way = way.update({ + tags: tags + }); + } + + graph = graph.replace(way); + var isClosed = way.isClosed(); + var nodes = graph.childNodes(way).slice(); // shallow copy + + if (isClosed) nodes.pop(); + + if (vertexID !== undefined) { + nodes = nodeSubset(nodes, vertexID, isClosed); + if (nodes.length !== 3) return graph; + } // note: all geometry functions here use the unclosed node/point/coord list + + + var nodeCount = {}; + var points = []; + var corner = { + i: 0, + dotp: 1 + }; + var node, point, loc, score, motions, i, j; + + for (i = 0; i < nodes.length; i++) { + node = nodes[i]; + nodeCount[node.id] = (nodeCount[node.id] || 0) + 1; + points.push({ + id: node.id, + coord: projection(node.loc) + }); + } + + if (points.length === 3) { + // move only one vertex for right triangle + for (i = 0; i < 1000; i++) { + motions = points.map(calcMotion); + points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]); + score = corner.dotp; + + if (score < epsilon) { + break; + } + } + + node = graph.entity(nodes[corner.i].id); + loc = projection.invert(points[corner.i].coord); + graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t))); + } else { + var straights = []; + var simplified = []; // Remove points from nearly straight sections.. + // This produces a simplified shape to orthogonalize + + for (i = 0; i < points.length; i++) { + point = points[i]; + var dotp = 0; + + if (isClosed || i > 0 && i < points.length - 1) { + var a = points[(i - 1 + points.length) % points.length]; + var b = points[(i + 1) % points.length]; + dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord)); + } + + if (dotp > upperThreshold) { + straights.push(point); + } else { + simplified.push(point); + } + } // Orthogonalize the simplified shape + + + var bestPoints = clonePoints(simplified); + var originalPoints = clonePoints(simplified); + score = Infinity; + + for (i = 0; i < 1000; i++) { + motions = simplified.map(calcMotion); + + for (j = 0; j < motions.length; j++) { + simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]); + } + + var newScore = geoOrthoCalcScore(simplified, isClosed, epsilon, threshold); + + if (newScore < score) { + bestPoints = clonePoints(simplified); + score = newScore; + } + + if (score < epsilon) { + break; + } + } + + var bestCoords = bestPoints.map(function (p) { + return p.coord; + }); + if (isClosed) bestCoords.push(bestCoords[0]); // move the nodes that should move + + for (i = 0; i < bestPoints.length; i++) { + point = bestPoints[i]; + + if (!geoVecEqual(originalPoints[i].coord, point.coord)) { + node = graph.entity(point.id); + loc = projection.invert(point.coord); + graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t))); + } + } // move the nodes along straight segments + + + for (i = 0; i < straights.length; i++) { + point = straights[i]; + if (nodeCount[point.id] > 1) continue; // skip self-intersections + + node = graph.entity(point.id); + + if (t === 1 && graph.parentWays(node).length === 1 && graph.parentRelations(node).length === 0 && !node.hasInterestingTags()) { + // remove uninteresting points.. + graph = actionDeleteNode(node.id)(graph); + } else { + // move interesting points to the nearest edge.. + var choice = geoVecProject(point.coord, bestCoords); + + if (choice) { + loc = projection.invert(choice.target); + graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t))); + } + } + } + } + + return graph; + + function clonePoints(array) { + return array.map(function (p) { + return { + id: p.id, + coord: [p.coord[0], p.coord[1]] + }; + }); + } + + function calcMotion(point, i, array) { + // don't try to move the endpoints of a non-closed way. + if (!isClosed && (i === 0 || i === array.length - 1)) return [0, 0]; // don't try to move a node that appears more than once (self intersection) + + if (nodeCount[array[i].id] > 1) return [0, 0]; + var a = array[(i - 1 + array.length) % array.length].coord; + var origin = point.coord; + var b = array[(i + 1) % array.length].coord; + var p = geoVecSubtract(a, origin); + var q = geoVecSubtract(b, origin); + var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q)); + p = geoVecNormalize(p); + q = geoVecNormalize(q); + var dotp = p[0] * q[0] + p[1] * q[1]; + var val = Math.abs(dotp); + + if (val < lowerThreshold) { + // nearly orthogonal + corner.i = i; + corner.dotp = val; + var vec = geoVecNormalize(geoVecAdd(p, q)); + return geoVecScale(vec, 0.1 * dotp * scale); + } + + return [0, 0]; // do nothing + } + }; // if we are only orthogonalizing one vertex, + // get that vertex and the previous and next + + + function nodeSubset(nodes, vertexID, isClosed) { + var first = isClosed ? 0 : 1; + var last = isClosed ? nodes.length : nodes.length - 1; + + for (var i = first; i < last; i++) { + if (nodes[i].id === vertexID) { + return [nodes[(i - 1 + nodes.length) % nodes.length], nodes[i], nodes[(i + 1) % nodes.length]]; + } + } + + return []; + } + + action.disabled = function (graph) { + var way = graph.entity(wayID); + way = way.removeNode(''); // sanity check - remove any consecutive duplicates + + graph = graph.replace(way); + var isClosed = way.isClosed(); + var nodes = graph.childNodes(way).slice(); // shallow copy + + if (isClosed) nodes.pop(); + var allowStraightAngles = false; + + if (vertexID !== undefined) { + allowStraightAngles = true; + nodes = nodeSubset(nodes, vertexID, isClosed); + if (nodes.length !== 3) return 'end_vertex'; + } + + var coords = nodes.map(function (n) { + return projection(n.loc); + }); + var score = geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles); + + if (score === null) { + return 'not_squarish'; + } else if (score === 0) { + return 'square_enough'; + } else { + return false; + } + }; + + action.transitionable = true; + return action; + } + + // + // `turn` must be an `osmTurn` object + // see osm/intersection.js, pathToTurn() + // + // This specifies a restriction of type `restriction` when traveling from + // `turn.from.way` toward `turn.to.way` via `turn.via.node` OR `turn.via.ways`. + // (The action does not check that these entities form a valid intersection.) + // + // From, to, and via ways should be split before calling this action. + // (old versions of the code would split the ways here, but we no longer do it) + // + // For testing convenience, accepts a restrictionID to assign to the new + // relation. Normally, this will be undefined and the relation will + // automatically be assigned a new ID. + // + + function actionRestrictTurn(turn, restrictionType, restrictionID) { + return function (graph) { + var fromWay = graph.entity(turn.from.way); + var toWay = graph.entity(turn.to.way); + var viaNode = turn.via.node && graph.entity(turn.via.node); + var viaWays = turn.via.ways && turn.via.ways.map(function (id) { + return graph.entity(id); + }); + var members = []; + members.push({ + id: fromWay.id, + type: 'way', + role: 'from' + }); + + if (viaNode) { + members.push({ + id: viaNode.id, + type: 'node', + role: 'via' + }); + } else if (viaWays) { + viaWays.forEach(function (viaWay) { + members.push({ + id: viaWay.id, + type: 'way', + role: 'via' + }); + }); + } + + members.push({ + id: toWay.id, + type: 'way', + role: 'to' + }); + return graph.replace(osmRelation({ + id: restrictionID, + tags: { + type: 'restriction', + restriction: restrictionType + }, + members: members + })); + }; + } + + function actionRevert(id) { + var action = function action(graph) { + var entity = graph.hasEntity(id), + base = graph.base().entities[id]; + + if (entity && !base) { + // entity will be removed.. + if (entity.type === 'node') { + graph.parentWays(entity).forEach(function (parent) { + parent = parent.removeNode(id); + graph = graph.replace(parent); + + if (parent.isDegenerate()) { + graph = actionDeleteWay(parent.id)(graph); + } + }); + } + + graph.parentRelations(entity).forEach(function (parent) { + parent = parent.removeMembersWithID(id); + graph = graph.replace(parent); + + if (parent.isDegenerate()) { + graph = actionDeleteRelation(parent.id)(graph); + } + }); + } + + return graph.revert(id); + }; + + return action; + } + + function actionRotate(rotateIds, pivot, angle, projection) { + var action = function action(graph) { + return graph.update(function (graph) { + utilGetAllNodes(rotateIds, graph).forEach(function (node) { + var point = geoRotate([projection(node.loc)], angle, pivot)[0]; + graph = graph.replace(node.move(projection.invert(point))); + }); + }); + }; + + return action; + } + + function actionScale(ids, pivotLoc, scaleFactor, projection) { + return function (graph) { + return graph.update(function (graph) { + var point, radial; + utilGetAllNodes(ids, graph).forEach(function (node) { + point = projection(node.loc); + radial = [point[0] - pivotLoc[0], point[1] - pivotLoc[1]]; + point = [pivotLoc[0] + scaleFactor * radial[0], pivotLoc[1] + scaleFactor * radial[1]]; + graph = graph.replace(node.move(projection.invert(point))); + }); + }); + }; + } + + /* Align nodes along their common axis */ + + function actionStraightenNodes(nodeIDs, projection) { + function positionAlongWay(a, o, b) { + return geoVecDot(a, b, o) / geoVecDot(b, b, o); + } // returns the endpoints of the long axis of symmetry of the `points` bounding rect + + + function getEndpoints(points) { + var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry. + // The shape's surrounding rectangle has 2 axes of symmetry. + // Snap points to the long axis + + var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2]; + var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2]; + var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2]; + var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2]; + var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2); + + if (isLong) { + return [p1, q1]; + } + + return [p2, q2]; + } + + var action = function action(graph, t) { + if (t === null || !isFinite(t)) t = 1; + t = Math.min(Math.max(+t, 0), 1); + var nodes = nodeIDs.map(function (id) { + return graph.entity(id); + }); + var points = nodes.map(function (n) { + return projection(n.loc); + }); + var endpoints = getEndpoints(points); + var startPoint = endpoints[0]; + var endPoint = endpoints[1]; // Move points onto the line connecting the endpoints + + for (var i = 0; i < points.length; i++) { + var node = nodes[i]; + var point = points[i]; + var u = positionAlongWay(point, startPoint, endPoint); + var point2 = geoVecInterp(startPoint, endPoint, u); + var loc2 = projection.invert(point2); + graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t))); + } + + return graph; + }; + + action.disabled = function (graph) { + var nodes = nodeIDs.map(function (id) { + return graph.entity(id); + }); + var points = nodes.map(function (n) { + return projection(n.loc); + }); + var endpoints = getEndpoints(points); + var startPoint = endpoints[0]; + var endPoint = endpoints[1]; + var maxDistance = 0; + + for (var i = 0; i < points.length; i++) { + var point = points[i]; + var u = positionAlongWay(point, startPoint, endPoint); + var p = geoVecInterp(startPoint, endPoint, u); + var dist = geoVecLength(p, point); + + if (!isNaN(dist) && dist > maxDistance) { + maxDistance = dist; + } + } + + if (maxDistance < 0.0001) { + return 'straight_enough'; + } + }; + + action.transitionable = true; + return action; + } + + /* + * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as + */ + + function actionStraightenWay(selectedIDs, projection) { + function positionAlongWay(a, o, b) { + return geoVecDot(a, b, o) / geoVecDot(b, b, o); + } // Return all selected ways as a continuous, ordered array of nodes + + + function allNodes(graph) { + var nodes = []; + var startNodes = []; + var endNodes = []; + var remainingWays = []; + var selectedWays = selectedIDs.filter(function (w) { + return graph.entity(w).type === 'way'; + }); + var selectedNodes = selectedIDs.filter(function (n) { + return graph.entity(n).type === 'node'; + }); + + for (var i = 0; i < selectedWays.length; i++) { + var way = graph.entity(selectedWays[i]); + nodes = way.nodes.slice(0); + remainingWays.push(nodes); + startNodes.push(nodes[0]); + endNodes.push(nodes[nodes.length - 1]); + } // Remove duplicate end/startNodes (duplicate nodes cannot be at the line end, + // and need to be removed so currNode difference calculation below works) + // i.e. ["n-1", "n-1", "n-2"] => ["n-2"] + + + startNodes = startNodes.filter(function (n) { + return startNodes.indexOf(n) === startNodes.lastIndexOf(n); + }); + endNodes = endNodes.filter(function (n) { + return endNodes.indexOf(n) === endNodes.lastIndexOf(n); + }); // Choose the initial endpoint to start from + + var currNode = utilArrayDifference(startNodes, endNodes).concat(utilArrayDifference(endNodes, startNodes))[0]; + var nextWay = []; + nodes = []; // Create nested function outside of loop to avoid "function in loop" lint error + + var getNextWay = function getNextWay(currNode, remainingWays) { + return remainingWays.filter(function (way) { + return way[0] === currNode || way[way.length - 1] === currNode; + })[0]; + }; // Add nodes to end of nodes array, until all ways are added + + + while (remainingWays.length) { + nextWay = getNextWay(currNode, remainingWays); + remainingWays = utilArrayDifference(remainingWays, [nextWay]); + + if (nextWay[0] !== currNode) { + nextWay.reverse(); + } + + nodes = nodes.concat(nextWay); + currNode = nodes[nodes.length - 1]; + } // If user selected 2 nodes to straighten between, then slice nodes array to those nodes + + + if (selectedNodes.length === 2) { + var startNodeIdx = nodes.indexOf(selectedNodes[0]); + var endNodeIdx = nodes.indexOf(selectedNodes[1]); + var sortedStartEnd = [startNodeIdx, endNodeIdx]; + sortedStartEnd.sort(function (a, b) { + return a - b; + }); + nodes = nodes.slice(sortedStartEnd[0], sortedStartEnd[1] + 1); + } + + return nodes.map(function (n) { + return graph.entity(n); + }); + } + + function shouldKeepNode(node, graph) { + return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags(); + } + + var action = function action(graph, t) { + if (t === null || !isFinite(t)) t = 1; + t = Math.min(Math.max(+t, 0), 1); + var nodes = allNodes(graph); + var points = nodes.map(function (n) { + return projection(n.loc); + }); + var startPoint = points[0]; + var endPoint = points[points.length - 1]; + var toDelete = []; + var i; + + for (i = 1; i < points.length - 1; i++) { + var node = nodes[i]; + var point = points[i]; + + if (t < 1 || shouldKeepNode(node, graph)) { + var u = positionAlongWay(point, startPoint, endPoint); + var p = geoVecInterp(startPoint, endPoint, u); + var loc2 = projection.invert(p); + graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t))); + } else { + // safe to delete + if (toDelete.indexOf(node) === -1) { + toDelete.push(node); + } + } + } + + for (i = 0; i < toDelete.length; i++) { + graph = actionDeleteNode(toDelete[i].id)(graph); + } + + return graph; + }; + + action.disabled = function (graph) { + // check way isn't too bendy + var nodes = allNodes(graph); + var points = nodes.map(function (n) { + return projection(n.loc); + }); + var startPoint = points[0]; + var endPoint = points[points.length - 1]; + var threshold = 0.2 * geoVecLength(startPoint, endPoint); + var i; + + if (threshold === 0) { + return 'too_bendy'; + } + + var maxDistance = 0; + + for (i = 1; i < points.length - 1; i++) { + var point = points[i]; + var u = positionAlongWay(point, startPoint, endPoint); + var p = geoVecInterp(startPoint, endPoint, u); + var dist = geoVecLength(p, point); // to bendy if point is off by 20% of total start/end distance in projected space + + if (isNaN(dist) || dist > threshold) { + return 'too_bendy'; + } else if (dist > maxDistance) { + maxDistance = dist; + } + } + + var keepingAllNodes = nodes.every(function (node, i) { + return i === 0 || i === nodes.length - 1 || shouldKeepNode(node, graph); + }); + + if (maxDistance < 0.0001 && // Allow straightening even if already straight in order to remove extraneous nodes + keepingAllNodes) { + return 'straight_enough'; + } + }; + + action.transitionable = true; + return action; + } + + // + // `turn` must be an `osmTurn` object with a `restrictionID` property. + // see osm/intersection.js, pathToTurn() + // + + function actionUnrestrictTurn(turn) { + return function (graph) { + return actionDeleteRelation(turn.restrictionID)(graph); + }; + } + + /* Reflect the given area around its axis of symmetry */ + + function actionReflect(reflectIds, projection) { + var _useLongAxis = true; + + var action = function action(graph, t) { + if (t === null || !isFinite(t)) t = 1; + t = Math.min(Math.max(+t, 0), 1); + var nodes = utilGetAllNodes(reflectIds, graph); + var points = nodes.map(function (n) { + return projection(n.loc); + }); + var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry. + // The shape's surrounding rectangle has 2 axes of symmetry. + // Reflect across the longer axis by default. + + var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2]; + var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2]; + var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2]; + var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2]; + var p, q; + var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2); + + if (_useLongAxis && isLong || !_useLongAxis && !isLong) { + p = p1; + q = q1; + } else { + p = p2; + q = q2; + } // reflect c across pq + // http://math.stackexchange.com/questions/65503/point-reflection-over-a-line + + + var dx = q[0] - p[0]; + var dy = q[1] - p[1]; + var a = (dx * dx - dy * dy) / (dx * dx + dy * dy); + var b = 2 * dx * dy / (dx * dx + dy * dy); + + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + var c = projection(node.loc); + var c2 = [a * (c[0] - p[0]) + b * (c[1] - p[1]) + p[0], b * (c[0] - p[0]) - a * (c[1] - p[1]) + p[1]]; + var loc2 = projection.invert(c2); + node = node.move(geoVecInterp(node.loc, loc2, t)); + graph = graph.replace(node); + } + + return graph; + }; + + action.useLongAxis = function (val) { + if (!arguments.length) return _useLongAxis; + _useLongAxis = val; + return action; + }; + + action.transitionable = true; + return action; + } + + function actionUpgradeTags(entityId, oldTags, replaceTags) { + return function (graph) { + var entity = graph.entity(entityId); + var tags = Object.assign({}, entity.tags); // shallow copy + + var transferValue; + var semiIndex; + + for (var oldTagKey in oldTags) { + if (!(oldTagKey in tags)) continue; // wildcard match + + if (oldTags[oldTagKey] === '*') { + // note the value since we might need to transfer it + transferValue = tags[oldTagKey]; + delete tags[oldTagKey]; // exact match + } else if (oldTags[oldTagKey] === tags[oldTagKey]) { + delete tags[oldTagKey]; // match is within semicolon-delimited values + } else { + var vals = tags[oldTagKey].split(';').filter(Boolean); + var oldIndex = vals.indexOf(oldTags[oldTagKey]); + + if (vals.length === 1 || oldIndex === -1) { + delete tags[oldTagKey]; + } else { + if (replaceTags && replaceTags[oldTagKey]) { + // replacing a value within a semicolon-delimited value, note the index + semiIndex = oldIndex; + } + + vals.splice(oldIndex, 1); + tags[oldTagKey] = vals.join(';'); + } + } + } + + if (replaceTags) { + for (var replaceKey in replaceTags) { + var replaceValue = replaceTags[replaceKey]; + + if (replaceValue === '*') { + if (tags[replaceKey] && tags[replaceKey] !== 'no') { + // allow any pre-existing value except `no` (troll tag) + continue; + } else { + // otherwise assume `yes` is okay + tags[replaceKey] = 'yes'; + } + } else if (replaceValue === '$1') { + tags[replaceKey] = transferValue; + } else { + if (tags[replaceKey] && oldTags[replaceKey] && semiIndex !== undefined) { + // don't override preexisting values + var existingVals = tags[replaceKey].split(';').filter(Boolean); + + if (existingVals.indexOf(replaceValue) === -1) { + existingVals.splice(semiIndex, 0, replaceValue); + tags[replaceKey] = existingVals.join(';'); + } + } else { + tags[replaceKey] = replaceValue; + } + } + } + } + + return graph.replace(entity.update({ + tags: tags + })); + }; + } + + function behaviorEdit(context) { + function behavior() { + context.map().minzoom(context.minEditableZoom()); + } + + behavior.off = function () { + context.map().minzoom(0); + }; + + return behavior; + } + + /* + The hover behavior adds the `.hover` class on pointerover to all elements to which + the identical datum is bound, and removes it on pointerout. + + The :hover pseudo-class is insufficient for iD's purposes because a datum's visual + representation may consist of several elements scattered throughout the DOM hierarchy. + Only one of these elements can have the :hover pseudo-class, but all of them will + have the .hover class. + */ + + function behaviorHover(context) { + var dispatch = dispatch$8('hover'); + + var _selection = select(null); + + var _newNodeId = null; + var _initialNodeID = null; + + var _altDisables; + + var _ignoreVertex; + + var _targets = []; // use pointer events on supported platforms; fallback to mouse events + + var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; + + function keydown(d3_event) { + if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) { + _selection.selectAll('.hover').classed('hover-suppressed', true).classed('hover', false); + + _selection.classed('hover-disabled', true); + + dispatch.call('hover', this, null); + } + } + + function keyup(d3_event) { + if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) { + _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false).classed('hover', true); + + _selection.classed('hover-disabled', false); + + dispatch.call('hover', this, _targets); + } + } + + function behavior(selection) { + _selection = selection; + _targets = []; + + if (_initialNodeID) { + _newNodeId = _initialNodeID; + _initialNodeID = null; + } else { + _newNodeId = null; + } + + _selection.on(_pointerPrefix + 'over.hover', pointerover).on(_pointerPrefix + 'out.hover', pointerout) // treat pointerdown as pointerover for touch devices + .on(_pointerPrefix + 'down.hover', pointerover); + + select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', pointerout, true).on('keydown.hover', keydown).on('keyup.hover', keyup); + + function eventTarget(d3_event) { + var datum = d3_event.target && d3_event.target.__data__; + if (_typeof(datum) !== 'object') return null; + + if (!(datum instanceof osmEntity) && datum.properties && datum.properties.entity instanceof osmEntity) { + return datum.properties.entity; + } + + return datum; + } + + function pointerover(d3_event) { + // ignore mouse hovers with buttons pressed unless dragging + if (context.mode().id.indexOf('drag') === -1 && (!d3_event.pointerType || d3_event.pointerType === 'mouse') && d3_event.buttons) return; + var target = eventTarget(d3_event); + + if (target && _targets.indexOf(target) === -1) { + _targets.push(target); + + updateHover(d3_event, _targets); + } + } + + function pointerout(d3_event) { + var target = eventTarget(d3_event); + + var index = _targets.indexOf(target); + + if (index !== -1) { + _targets.splice(index); + + updateHover(d3_event, _targets); + } + } + + function allowsVertex(d) { + return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph()); + } + + function modeAllowsHover(target) { + var mode = context.mode(); + + if (mode.id === 'add-point') { + return mode.preset.matchGeometry('vertex') || target.type !== 'way' && target.geometry(context.graph()) !== 'vertex'; + } + + return true; + } + + function updateHover(d3_event, targets) { + _selection.selectAll('.hover').classed('hover', false); + + _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false); + + var mode = context.mode(); + + if (!_newNodeId && (mode.id === 'draw-line' || mode.id === 'draw-area')) { + var node = targets.find(function (target) { + return target instanceof osmEntity && target.type === 'node'; + }); + _newNodeId = node && node.id; + } + + targets = targets.filter(function (datum) { + if (datum instanceof osmEntity) { + // If drawing a way, don't hover on a node that was just placed. #3974 + return datum.id !== _newNodeId && (datum.type !== 'node' || !_ignoreVertex || allowsVertex(datum)) && modeAllowsHover(datum); + } + + return true; + }); + var selector = ''; + + for (var i in targets) { + var datum = targets[i]; // What are we hovering over? + + if (datum.__featurehash__) { + // hovering custom data + selector += ', .data' + datum.__featurehash__; + } else if (datum instanceof QAItem) { + selector += ', .' + datum.service + '.itemId-' + datum.id; + } else if (datum instanceof osmNote) { + selector += ', .note-' + datum.id; + } else if (datum instanceof osmEntity) { + selector += ', .' + datum.id; + + if (datum.type === 'relation') { + for (var j in datum.members) { + selector += ', .' + datum.members[j].id; + } + } + } + } + + var suppressed = _altDisables && d3_event && d3_event.altKey; + + if (selector.trim().length) { + // remove the first comma + selector = selector.slice(1); + + _selection.selectAll(selector).classed(suppressed ? 'hover-suppressed' : 'hover', true); + } + + dispatch.call('hover', this, !suppressed && targets); + } + } + + behavior.off = function (selection) { + selection.selectAll('.hover').classed('hover', false); + selection.selectAll('.hover-suppressed').classed('hover-suppressed', false); + selection.classed('hover-disabled', false); + selection.on(_pointerPrefix + 'over.hover', null).on(_pointerPrefix + 'out.hover', null).on(_pointerPrefix + 'down.hover', null); + select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', null, true).on('keydown.hover', null).on('keyup.hover', null); + }; + + behavior.altDisables = function (val) { + if (!arguments.length) return _altDisables; + _altDisables = val; + return behavior; + }; + + behavior.ignoreVertex = function (val) { + if (!arguments.length) return _ignoreVertex; + _ignoreVertex = val; + return behavior; + }; + + behavior.initialNodeID = function (nodeId) { + _initialNodeID = nodeId; + return behavior; + }; + + return utilRebind(behavior, dispatch, 'on'); + } + + var _disableSpace = false; + var _lastSpace = null; + function behaviorDraw(context) { + var dispatch = dispatch$8('move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish'); + var keybinding = utilKeybinding('draw'); + + var _hover = behaviorHover(context).altDisables(true).ignoreVertex(true).on('hover', context.ui().sidebar.hover); + + var _edit = behaviorEdit(context); + + var _closeTolerance = 4; + var _tolerance = 12; + var _mouseLeave = false; + var _lastMouse = null; + + var _lastPointerUpEvent; + + var _downPointer; // use pointer events on supported platforms; fallback to mouse events + + + var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // related code + // - `mode/drag_node.js` `datum()` + + + function datum(d3_event) { + var mode = context.mode(); + var isNote = mode && mode.id.indexOf('note') !== -1; + if (d3_event.altKey || isNote) return {}; + var element; + + if (d3_event.type === 'keydown') { + element = _lastMouse && _lastMouse.target; + } else { + element = d3_event.target; + } // When drawing, snap only to touch targets.. + // (this excludes area fills and active drawing elements) + + + var d = element.__data__; + return d && d.properties && d.properties.target ? d : {}; + } + + function pointerdown(d3_event) { + if (_downPointer) return; + var pointerLocGetter = utilFastMouse(this); + _downPointer = { + id: d3_event.pointerId || 'mouse', + pointerLocGetter: pointerLocGetter, + downTime: +new Date(), + downLoc: pointerLocGetter(d3_event) + }; + dispatch.call('down', this, d3_event, datum(d3_event)); + } + + function pointerup(d3_event) { + if (!_downPointer || _downPointer.id !== (d3_event.pointerId || 'mouse')) return; + var downPointer = _downPointer; + _downPointer = null; + _lastPointerUpEvent = d3_event; + if (downPointer.isCancelled) return; + var t2 = +new Date(); + var p2 = downPointer.pointerLocGetter(d3_event); + var dist = geoVecLength(downPointer.downLoc, p2); + + if (dist < _closeTolerance || dist < _tolerance && t2 - downPointer.downTime < 500) { + // Prevent a quick second click + select(window).on('click.draw-block', function () { + d3_event.stopPropagation(); + }, true); + context.map().dblclickZoomEnable(false); + window.setTimeout(function () { + context.map().dblclickZoomEnable(true); + select(window).on('click.draw-block', null); + }, 500); + click(d3_event, p2); + } + } + + function pointermove(d3_event) { + if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse') && !_downPointer.isCancelled) { + var p2 = _downPointer.pointerLocGetter(d3_event); + + var dist = geoVecLength(_downPointer.downLoc, p2); + + if (dist >= _closeTolerance) { + _downPointer.isCancelled = true; + dispatch.call('downcancel', this); + } + } + + 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 + // events immediately after non-mouse pointerup events; detect and ignore them. + + if (_lastPointerUpEvent && _lastPointerUpEvent.pointerType !== 'mouse' && d3_event.timeStamp - _lastPointerUpEvent.timeStamp < 100) return; + _lastMouse = d3_event; + dispatch.call('move', this, d3_event, datum(d3_event)); + } + + function pointercancel(d3_event) { + if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse')) { + if (!_downPointer.isCancelled) { + dispatch.call('downcancel', this); + } + + _downPointer = null; + } + } + + function mouseenter() { + _mouseLeave = false; + } + + function mouseleave() { + _mouseLeave = true; + } + + function allowsVertex(d) { + return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph()); + } // related code + // - `mode/drag_node.js` `doMove()` + // - `behavior/draw.js` `click()` + // - `behavior/draw_way.js` `move()` + + + function click(d3_event, loc) { + var d = datum(d3_event); + var target = d && d.properties && d.properties.entity; + var mode = context.mode(); + + if (target && target.type === 'node' && allowsVertex(target)) { + // Snap to a node + dispatch.call('clickNode', this, target, d); + return; + } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) { + // Snap to a way + var choice = geoChooseEdge(context.graph().childNodes(target), loc, context.projection, context.activeID()); + + if (choice) { + var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]]; + dispatch.call('clickWay', this, choice.loc, edge, d); + return; + } + } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) { + var locLatLng = context.projection.invert(loc); + dispatch.call('click', this, locLatLng, d); + } + } // treat a spacebar press like a click + + + function space(d3_event) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + var currSpace = context.map().mouse(); + + if (_disableSpace && _lastSpace) { + var dist = geoVecLength(_lastSpace, currSpace); + + if (dist > _tolerance) { + _disableSpace = false; + } + } + + if (_disableSpace || _mouseLeave || !_lastMouse) return; // user must move mouse or release space bar to allow another click + + _lastSpace = currSpace; + _disableSpace = true; + select(window).on('keyup.space-block', function () { + d3_event.preventDefault(); + d3_event.stopPropagation(); + _disableSpace = false; + select(window).on('keyup.space-block', null); + }); // get the current mouse position + + var loc = context.map().mouse() || // or the map center if the mouse has never entered the map + context.projection(context.map().center()); + click(d3_event, loc); + } + + function backspace(d3_event) { + d3_event.preventDefault(); + dispatch.call('undo'); + } + + function del(d3_event) { + d3_event.preventDefault(); + dispatch.call('cancel'); + } + + function ret(d3_event) { + d3_event.preventDefault(); + dispatch.call('finish'); + } + + function behavior(selection) { + context.install(_hover); + context.install(_edit); + _downPointer = null; + keybinding.on('⌫', backspace).on('⌦', del).on('⎋', ret).on('↩', ret).on('space', space).on('⌥space', space); + selection.on('mouseenter.draw', mouseenter).on('mouseleave.draw', mouseleave).on(_pointerPrefix + 'down.draw', pointerdown).on(_pointerPrefix + 'move.draw', pointermove); + select(window).on(_pointerPrefix + 'up.draw', pointerup, true).on('pointercancel.draw', pointercancel, true); + select(document).call(keybinding); + return behavior; + } + + behavior.off = function (selection) { + context.ui().sidebar.hover.cancel(); + context.uninstall(_hover); + context.uninstall(_edit); + selection.on('mouseenter.draw', null).on('mouseleave.draw', null).on(_pointerPrefix + 'down.draw', null).on(_pointerPrefix + 'move.draw', null); + select(window).on(_pointerPrefix + 'up.draw', null).on('pointercancel.draw', null); // note: keyup.space-block, click.draw-block should remain + + select(document).call(keybinding.unbind); + }; + + behavior.hover = function () { + return _hover; + }; + + return utilRebind(behavior, dispatch, 'on'); + } + + function initRange(domain, range) { + switch (arguments.length) { + case 0: + break; + + case 1: + this.range(domain); + break; + + default: + this.range(range).domain(domain); + break; + } + + return this; + } + + function constants(x) { + return function () { + return x; + }; + } + + function number(x) { + return +x; + } + + var unit = [0, 1]; + function identity$1(x) { + return x; + } + + function normalize(a, b) { + return (b -= a = +a) ? function (x) { + return (x - a) / b; + } : constants(isNaN(b) ? NaN : 0.5); + } + + function clamper(a, b) { + var t; + if (a > b) t = a, a = b, b = t; + return function (x) { + return Math.max(a, Math.min(b, x)); + }; + } // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1]. + // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b]. + + + function bimap(domain, range, interpolate) { + var d0 = domain[0], + d1 = domain[1], + r0 = range[0], + r1 = range[1]; + if (d1 < d0) d0 = normalize(d1, d0), r0 = interpolate(r1, r0);else d0 = normalize(d0, d1), r0 = interpolate(r0, r1); + return function (x) { + return r0(d0(x)); + }; + } + + function polymap(domain, range, interpolate) { + var j = Math.min(domain.length, range.length) - 1, + d = new Array(j), + r = new Array(j), + i = -1; // Reverse descending domains. + + if (domain[j] < domain[0]) { + domain = domain.slice().reverse(); + range = range.slice().reverse(); + } + + while (++i < j) { + d[i] = normalize(domain[i], domain[i + 1]); + r[i] = interpolate(range[i], range[i + 1]); + } + + return function (x) { + var i = bisectRight(domain, x, 1, j) - 1; + return r[i](d[i](x)); + }; + } + + function copy(source, target) { + return target.domain(source.domain()).range(source.range()).interpolate(source.interpolate()).clamp(source.clamp()).unknown(source.unknown()); + } + function transformer() { + var domain = unit, + range = unit, + interpolate = interpolate$1, + transform, + untransform, + unknown, + clamp = identity$1, + piecewise, + output, + input; + + function rescale() { + var n = Math.min(domain.length, range.length); + if (clamp !== identity$1) clamp = clamper(domain[0], domain[n - 1]); + piecewise = n > 2 ? polymap : bimap; + output = input = null; + return scale; + } + + function scale(x) { + return x == null || isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate)))(transform(clamp(x))); + } + + scale.invert = function (y) { + return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3_interpolateNumber)))(y))); + }; + + scale.domain = function (_) { + return arguments.length ? (domain = Array.from(_, number), rescale()) : domain.slice(); + }; + + scale.range = function (_) { + return arguments.length ? (range = Array.from(_), rescale()) : range.slice(); + }; + + scale.rangeRound = function (_) { + return range = Array.from(_), interpolate = interpolateRound, rescale(); + }; + + scale.clamp = function (_) { + return arguments.length ? (clamp = _ ? true : identity$1, rescale()) : clamp !== identity$1; + }; + + scale.interpolate = function (_) { + return arguments.length ? (interpolate = _, rescale()) : interpolate; + }; + + scale.unknown = function (_) { + return arguments.length ? (unknown = _, scale) : unknown; + }; + + return function (t, u) { + transform = t, untransform = u; + return rescale(); + }; + } + function continuous() { + return transformer()(identity$1, identity$1); + } + + function formatDecimal (x) { + return Math.abs(x = Math.round(x)) >= 1e21 ? x.toLocaleString("en").replace(/,/g, "") : x.toString(10); + } // Computes the decimal coefficient and exponent of the specified number x with + // significant digits p, where x is positive and p is in [1, 21] or undefined. + // For example, formatDecimalParts(1.23) returns ["123", 0]. + + function formatDecimalParts(x, p) { + if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity + + var i, + coefficient = x.slice(0, i); // The string returned by toExponential either has the form \d\.\d+e[-+]\d+ + // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3). + + return [coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +x.slice(i + 1)]; + } + + function exponent (x) { + return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN; + } + + function formatGroup (grouping, thousands) { + return function (value, width) { + var i = value.length, + t = [], + j = 0, + g = grouping[0], + length = 0; + + while (i > 0 && g > 0) { + if (length + g + 1 > width) g = Math.max(1, width - length); + t.push(value.substring(i -= g, i + g)); + if ((length += g + 1) > width) break; + g = grouping[j = (j + 1) % grouping.length]; + } + + return t.reverse().join(thousands); + }; + } + + function formatNumerals (numerals) { + return function (value) { + return value.replace(/[0-9]/g, function (i) { + return numerals[+i]; + }); + }; + } + + // [[fill]align][sign][symbol][0][width][,][.precision][~][type] + var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i; + function formatSpecifier(specifier) { + if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier); + var match; + return new FormatSpecifier({ + fill: match[1], + align: match[2], + sign: match[3], + symbol: match[4], + zero: match[5], + width: match[6], + comma: match[7], + precision: match[8] && match[8].slice(1), + trim: match[9], + type: match[10] + }); + } + formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof + + function FormatSpecifier(specifier) { + this.fill = specifier.fill === undefined ? " " : specifier.fill + ""; + this.align = specifier.align === undefined ? ">" : specifier.align + ""; + this.sign = specifier.sign === undefined ? "-" : specifier.sign + ""; + this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + ""; + this.zero = !!specifier.zero; + this.width = specifier.width === undefined ? undefined : +specifier.width; + this.comma = !!specifier.comma; + this.precision = specifier.precision === undefined ? undefined : +specifier.precision; + this.trim = !!specifier.trim; + this.type = specifier.type === undefined ? "" : specifier.type + ""; + } + + FormatSpecifier.prototype.toString = function () { + return this.fill + this.align + this.sign + this.symbol + (this.zero ? "0" : "") + (this.width === undefined ? "" : Math.max(1, this.width | 0)) + (this.comma ? "," : "") + (this.precision === undefined ? "" : "." + Math.max(0, this.precision | 0)) + (this.trim ? "~" : "") + this.type; + }; + + // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k. + function formatTrim (s) { + out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) { + switch (s[i]) { + case ".": + i0 = i1 = i; + break; + + case "0": + if (i0 === 0) i0 = i; + i1 = i; + break; + + default: + if (!+s[i]) break out; + if (i0 > 0) i0 = 0; + break; + } + } + + return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s; + } + + var $$7 = _export; + var fails$3 = fails$N; + var thisNumberValue = thisNumberValue$2; + + var nativeToPrecision = 1.0.toPrecision; + + var FORCED$1 = fails$3(function () { + // IE7- + return nativeToPrecision.call(1, undefined) !== '1'; + }) || !fails$3(function () { + // V8 ~ Android 4.3- + nativeToPrecision.call({}); + }); + + // `Number.prototype.toPrecision` method + // https://tc39.es/ecma262/#sec-number.prototype.toprecision + $$7({ target: 'Number', proto: true, forced: FORCED$1 }, { + toPrecision: function toPrecision(precision) { + return precision === undefined + ? nativeToPrecision.call(thisNumberValue(this)) + : nativeToPrecision.call(thisNumberValue(this), precision); + } + }); + + var prefixExponent; + function formatPrefixAuto (x, p) { + var d = formatDecimalParts(x, p); + if (!d) return x + ""; + var coefficient = d[0], + exponent = d[1], + i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1, + n = coefficient.length; + return i === n ? coefficient : i > n ? coefficient + new Array(i - n + 1).join("0") : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i) : "0." + new Array(1 - i).join("0") + formatDecimalParts(x, Math.max(0, p + i - 1))[0]; // less than 1y! + } + + function formatRounded (x, p) { + var d = formatDecimalParts(x, p); + if (!d) return x + ""; + var coefficient = d[0], + exponent = d[1]; + return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1) : coefficient + new Array(exponent - coefficient.length + 2).join("0"); + } + + var formatTypes = { + "%": function _(x, p) { + return (x * 100).toFixed(p); + }, + "b": function b(x) { + return Math.round(x).toString(2); + }, + "c": function c(x) { + return x + ""; + }, + "d": formatDecimal, + "e": function e(x, p) { + return x.toExponential(p); + }, + "f": function f(x, p) { + return x.toFixed(p); + }, + "g": function g(x, p) { + return x.toPrecision(p); + }, + "o": function o(x) { + return Math.round(x).toString(8); + }, + "p": function p(x, _p) { + return formatRounded(x * 100, _p); + }, + "r": formatRounded, + "s": formatPrefixAuto, + "X": function X(x) { + return Math.round(x).toString(16).toUpperCase(); + }, + "x": function x(_x) { + return Math.round(_x).toString(16); + } + }; + + function identity (x) { + return x; + } + + var map$1 = Array.prototype.map, + prefixes = ["y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"]; + function formatLocale (locale) { + var group = locale.grouping === undefined || locale.thousands === undefined ? identity : formatGroup(map$1.call(locale.grouping, Number), locale.thousands + ""), + currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "", + currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "", + decimal = locale.decimal === undefined ? "." : locale.decimal + "", + numerals = locale.numerals === undefined ? identity : formatNumerals(map$1.call(locale.numerals, String)), + percent = locale.percent === undefined ? "%" : locale.percent + "", + minus = locale.minus === undefined ? "−" : locale.minus + "", + nan = locale.nan === undefined ? "NaN" : locale.nan + ""; + + function newFormat(specifier) { + specifier = formatSpecifier(specifier); + var fill = specifier.fill, + align = specifier.align, + sign = specifier.sign, + symbol = specifier.symbol, + zero = specifier.zero, + width = specifier.width, + comma = specifier.comma, + precision = specifier.precision, + trim = specifier.trim, + type = specifier.type; // The "n" type is an alias for ",g". + + if (type === "n") comma = true, type = "g"; // The "" type, and any invalid type, is an alias for ".12~g". + else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g"; // If zero fill is specified, padding goes after sign and before digits. + + if (zero || fill === "0" && align === "=") zero = true, fill = "0", align = "="; // Compute the prefix and suffix. + // For SI-prefix, the suffix is lazily computed. + + var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "", + suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : ""; // What format function should we use? + // Is this an integer type? + // Can this type generate exponential notation? + + var formatType = formatTypes[type], + maybeSuffix = /[defgprs%]/.test(type); // Set the default precision if not specified, + // or clamp the specified precision to the supported range. + // For significant precision, it must be in [1, 21]. + // For fixed precision, it must be in [0, 20]. + + precision = precision === undefined ? 6 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) : Math.max(0, Math.min(20, precision)); + + function format(value) { + var valuePrefix = prefix, + valueSuffix = suffix, + i, + n, + c; + + if (type === "c") { + valueSuffix = formatType(value) + valueSuffix; + value = ""; + } else { + value = +value; // Determine the sign. -0 is not less than 0, but 1 / -0 is! + + var valueNegative = value < 0 || 1 / value < 0; // Perform the initial formatting. + + value = isNaN(value) ? nan : formatType(Math.abs(value), precision); // Trim insignificant zeros. + + if (trim) value = formatTrim(value); // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign. + + if (valueNegative && +value === 0 && sign !== "+") valueNegative = false; // Compute the prefix and suffix. + + valuePrefix = (valueNegative ? sign === "(" ? sign : minus : sign === "-" || sign === "(" ? "" : sign) + valuePrefix; + valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); // Break the formatted value into the integer “value” part that can be + // grouped, and fractional or exponential “suffix” part that is not. + + if (maybeSuffix) { + i = -1, n = value.length; + + while (++i < n) { + if (c = value.charCodeAt(i), 48 > c || c > 57) { + valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix; + value = value.slice(0, i); + break; + } + } + } + } // If the fill character is not "0", grouping is applied before padding. + + + if (comma && !zero) value = group(value, Infinity); // Compute the padding. + + var length = valuePrefix.length + value.length + valueSuffix.length, + padding = length < width ? new Array(width - length + 1).join(fill) : ""; // If the fill character is "0", grouping is applied after padding. + + if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; // Reconstruct the final output based on the desired alignment. + + switch (align) { + case "<": + value = valuePrefix + value + valueSuffix + padding; + break; + + case "=": + value = valuePrefix + padding + value + valueSuffix; + break; + + case "^": + value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); + break; + + default: + value = padding + valuePrefix + value + valueSuffix; + break; + } + + return numerals(value); + } + + format.toString = function () { + return specifier + ""; + }; + + return format; + } + + function formatPrefix(specifier, value) { + var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)), + e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3, + k = Math.pow(10, -e), + prefix = prefixes[8 + e / 3]; + return function (value) { + return f(k * value) + prefix; + }; + } + + return { + format: newFormat, + formatPrefix: formatPrefix + }; + } + + var locale; + var format$1; + var formatPrefix; + defaultLocale({ + thousands: ",", + grouping: [3], + currency: ["$", ""] + }); + function defaultLocale(definition) { + locale = formatLocale(definition); + format$1 = locale.format; + formatPrefix = locale.formatPrefix; + return locale; + } + + function precisionFixed (step) { + return Math.max(0, -exponent(Math.abs(step))); + } + + function precisionPrefix (step, value) { + return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step))); + } + + function precisionRound (step, max) { + step = Math.abs(step), max = Math.abs(max) - step; + return Math.max(0, exponent(max) - exponent(step)) + 1; + } + + function tickFormat(start, stop, count, specifier) { + var step = tickStep(start, stop, count), + precision; + specifier = formatSpecifier(specifier == null ? ",f" : specifier); + + switch (specifier.type) { + case "s": + { + var value = Math.max(Math.abs(start), Math.abs(stop)); + if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision; + return formatPrefix(specifier, value); + } + + case "": + case "e": + case "g": + case "p": + case "r": + { + if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e"); + break; + } + + case "f": + case "%": + { + if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2; + break; + } + } + + return format$1(specifier); + } + + function linearish(scale) { + var domain = scale.domain; + + scale.ticks = function (count) { + var d = domain(); + return ticks(d[0], d[d.length - 1], count == null ? 10 : count); + }; + + scale.tickFormat = function (count, specifier) { + var d = domain(); + return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier); + }; + + scale.nice = function (count) { + if (count == null) count = 10; + var d = domain(); + var i0 = 0; + var i1 = d.length - 1; + var start = d[i0]; + var stop = d[i1]; + var prestep; + var step; + var maxIter = 10; + + if (stop < start) { + step = start, start = stop, stop = step; + step = i0, i0 = i1, i1 = step; + } + + while (maxIter-- > 0) { + step = tickIncrement(start, stop, count); + + if (step === prestep) { + d[i0] = start; + d[i1] = stop; + return domain(d); + } else if (step > 0) { + start = Math.floor(start / step) * step; + stop = Math.ceil(stop / step) * step; + } else if (step < 0) { + start = Math.ceil(start * step) / step; + stop = Math.floor(stop * step) / step; + } else { + break; + } + + prestep = step; + } + + return scale; + }; + + return scale; + } + function linear() { + var scale = continuous(); + + scale.copy = function () { + return copy(scale, linear()); + }; + + initRange.apply(scale, arguments); + return linearish(scale); + } + + // eslint-disable-next-line es/no-math-expm1 -- safe + var $expm1 = Math.expm1; + var exp$1 = Math.exp; + + // `Math.expm1` method implementation + // https://tc39.es/ecma262/#sec-math.expm1 + var mathExpm1 = (!$expm1 + // Old FF bug + || $expm1(10) > 22025.465794806719 || $expm1(10) < 22025.4657948067165168 + // Tor Browser bug + || $expm1(-2e-17) != -2e-17 + ) ? function expm1(x) { + return (x = +x) == 0 ? x : x > -1e-6 && x < 1e-6 ? x + x * x / 2 : exp$1(x) - 1; + } : $expm1; + + function quantize() { + var x0 = 0, + x1 = 1, + n = 1, + domain = [0.5], + range = [0, 1], + unknown; + + function scale(x) { + return x != null && x <= x ? range[bisectRight(domain, x, 0, n)] : unknown; + } + + function rescale() { + var i = -1; + domain = new Array(n); + + while (++i < n) { + domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1); + } + + return scale; + } + + scale.domain = function (_) { + var _ref, _ref2; + + return arguments.length ? ((_ref = _, _ref2 = _slicedToArray(_ref, 2), x0 = _ref2[0], x1 = _ref2[1], _ref), x0 = +x0, x1 = +x1, rescale()) : [x0, x1]; + }; + + scale.range = function (_) { + return arguments.length ? (n = (range = Array.from(_)).length - 1, rescale()) : range.slice(); + }; + + scale.invertExtent = function (y) { + var i = range.indexOf(y); + return i < 0 ? [NaN, NaN] : i < 1 ? [x0, domain[0]] : i >= n ? [domain[n - 1], x1] : [domain[i - 1], domain[i]]; + }; + + scale.unknown = function (_) { + return arguments.length ? (unknown = _, scale) : scale; + }; + + scale.thresholds = function () { + return domain.slice(); + }; + + scale.copy = function () { + return quantize().domain([x0, x1]).range(range).unknown(unknown); + }; + + return initRange.apply(linearish(scale), arguments); + } + + // https://github.com/tc39/proposal-string-pad-start-end + var toLength$2 = toLength$q; + var repeat$1 = stringRepeat; + var requireObjectCoercible$2 = requireObjectCoercible$e; + + var ceil = Math.ceil; + + // `String.prototype.{ padStart, padEnd }` methods implementation + var createMethod = function (IS_END) { + return function ($this, maxLength, fillString) { + var S = String(requireObjectCoercible$2($this)); + var stringLength = S.length; + var fillStr = fillString === undefined ? ' ' : String(fillString); + var intMaxLength = toLength$2(maxLength); + var fillLen, stringFiller; + if (intMaxLength <= stringLength || fillStr == '') return S; + fillLen = intMaxLength - stringLength; + stringFiller = repeat$1.call(fillStr, ceil(fillLen / fillStr.length)); + if (stringFiller.length > fillLen) stringFiller = stringFiller.slice(0, fillLen); + return IS_END ? S + stringFiller : stringFiller + S; + }; + }; + + var stringPad = { + // `String.prototype.padStart` method + // https://tc39.es/ecma262/#sec-string.prototype.padstart + start: createMethod(false), + // `String.prototype.padEnd` method + // https://tc39.es/ecma262/#sec-string.prototype.padend + end: createMethod(true) + }; + + var fails$2 = fails$N; + var padStart = stringPad.start; + + var abs$1 = Math.abs; + var DatePrototype = Date.prototype; + var getTime = DatePrototype.getTime; + var nativeDateToISOString = DatePrototype.toISOString; + + // `Date.prototype.toISOString` method implementation + // https://tc39.es/ecma262/#sec-date.prototype.toisostring + // PhantomJS / old WebKit fails here: + var dateToIsoString = (fails$2(function () { + return nativeDateToISOString.call(new Date(-5e13 - 1)) != '0385-07-25T07:06:39.999Z'; + }) || !fails$2(function () { + nativeDateToISOString.call(new Date(NaN)); + })) ? function toISOString() { + if (!isFinite(getTime.call(this))) throw RangeError('Invalid time value'); + var date = this; + var year = date.getUTCFullYear(); + var milliseconds = date.getUTCMilliseconds(); + var sign = year < 0 ? '-' : year > 9999 ? '+' : ''; + return sign + padStart(abs$1(year), sign ? 6 : 4, 0) + + '-' + padStart(date.getUTCMonth() + 1, 2, 0) + + '-' + padStart(date.getUTCDate(), 2, 0) + + 'T' + padStart(date.getUTCHours(), 2, 0) + + ':' + padStart(date.getUTCMinutes(), 2, 0) + + ':' + padStart(date.getUTCSeconds(), 2, 0) + + '.' + padStart(milliseconds, 3, 0) + + 'Z'; + } : nativeDateToISOString; + + var $$6 = _export; + var toISOString = dateToIsoString; + + // `Date.prototype.toISOString` method + // https://tc39.es/ecma262/#sec-date.prototype.toisostring + // PhantomJS / old WebKit has a broken implementations + $$6({ target: 'Date', proto: true, forced: Date.prototype.toISOString !== toISOString }, { + toISOString: toISOString + }); + + function behaviorBreathe() { + var duration = 800; + var steps = 4; + var selector = '.selected.shadow, .selected .shadow'; + + var _selected = select(null); + + var _classed = ''; + var _params = {}; + var _done = false; + + var _timer; + + function ratchetyInterpolator(a, b, steps, units) { + a = parseFloat(a); + b = parseFloat(b); + var sample = quantize().domain([0, 1]).range(d3_quantize(d3_interpolateNumber(a, b), steps)); + return function (t) { + return String(sample(t)) + (units || ''); + }; + } + + function reset(selection) { + selection.style('stroke-opacity', null).style('stroke-width', null).style('fill-opacity', null).style('r', null); + } + + function setAnimationParams(transition, fromTo) { + var toFrom = fromTo === 'from' ? 'to' : 'from'; + transition.styleTween('stroke-opacity', function (d) { + return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps); + }).styleTween('stroke-width', function (d) { + return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px'); + }).styleTween('fill-opacity', function (d) { + return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps); + }).styleTween('r', function (d) { + return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px'); + }); + } + + function calcAnimationParams(selection) { + selection.call(reset).each(function (d) { + var s = select(this); + var tag = s.node().tagName; + var p = { + 'from': {}, + 'to': {} + }; + var opacity; + var width; // determine base opacity and width + + if (tag === 'circle') { + opacity = parseFloat(s.style('fill-opacity') || 0.5); + width = parseFloat(s.style('r') || 15.5); + } else { + opacity = parseFloat(s.style('stroke-opacity') || 0.7); + width = parseFloat(s.style('stroke-width') || 10); + } // calculate from/to interpolation params.. + + + p.tag = tag; + p.from.opacity = opacity * 0.6; + p.to.opacity = opacity * 1.25; + p.from.width = width * 0.7; + p.to.width = width * (tag === 'circle' ? 1.5 : 1); + _params[d.id] = p; + }); + } + + function run(surface, fromTo) { + var toFrom = fromTo === 'from' ? 'to' : 'from'; + var currSelected = surface.selectAll(selector); + var currClassed = surface.attr('class'); + + if (_done || currSelected.empty()) { + _selected.call(reset); + + _selected = select(null); + return; + } + + if (!fastDeepEqual(currSelected.data(), _selected.data()) || currClassed !== _classed) { + _selected.call(reset); + + _classed = currClassed; + _selected = currSelected.call(calcAnimationParams); + } + + var didCallNextRun = false; + + _selected.transition().duration(duration).call(setAnimationParams, fromTo).on('end', function () { + // `end` event is called for each selected element, but we want + // it to run only once + if (!didCallNextRun) { + surface.call(run, toFrom); + didCallNextRun = true; + } // if entity was deselected, remove breathe styling + + + if (!select(this).classed('selected')) { + reset(select(this)); + } + }); + } + + function behavior(surface) { + _done = false; + _timer = timer(function () { + // wait for elements to actually become selected + if (surface.selectAll(selector).empty()) { + return false; + } + + surface.call(run, 'from'); + + _timer.stop(); + + return true; + }, 20); + } + + behavior.restartIfNeeded = function (surface) { + if (_selected.empty()) { + surface.call(run, 'from'); + + if (_timer) { + _timer.stop(); + } + } + }; + + behavior.off = function () { + _done = true; + + if (_timer) { + _timer.stop(); + } + + _selected.interrupt().call(reset); + }; + + return behavior; + } + + /* Creates a keybinding behavior for an operation */ + function behaviorOperation(context) { + var _operation; + + function keypress(d3_event) { + // prevent operations during low zoom selection + if (!context.map().withinEditableZoom()) return; + if (_operation.availableForKeypress && !_operation.availableForKeypress()) return; + d3_event.preventDefault(); + + var disabled = _operation.disabled(); + + if (disabled) { + context.ui().flash.duration(4000).iconName('#iD-operation-' + _operation.id).iconClass('operation disabled').label(_operation.tooltip)(); + } else { + context.ui().flash.duration(2000).iconName('#iD-operation-' + _operation.id).iconClass('operation').label(_operation.annotation() || _operation.title)(); + if (_operation.point) _operation.point(null); + + _operation(); + } + } + + function behavior() { + if (_operation && _operation.available()) { + context.keybinding().on(_operation.keys, keypress); + } + + return behavior; + } + + behavior.off = function () { + context.keybinding().off(_operation.keys); + }; + + behavior.which = function (_) { + if (!arguments.length) return _operation; + _operation = _; + return behavior; + }; + + return behavior; + } + + function operationCircularize(context, selectedIDs) { + var _extent; + + var _actions = selectedIDs.map(getAction).filter(Boolean); + + var _amount = _actions.length === 1 ? 'single' : 'multiple'; + + var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) { + return n.loc; + }); + + function getAction(entityID) { + var entity = context.entity(entityID); + if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) return null; + + if (!_extent) { + _extent = entity.extent(context.graph()); + } else { + _extent = _extent.extend(entity.extent(context.graph())); + } + + return actionCircularize(entityID, context.projection); + } + + var operation = function operation() { + if (!_actions.length) return; + + var combinedAction = function combinedAction(graph, t) { + _actions.forEach(function (action) { + if (!action.disabled(graph)) { + graph = action(graph, t); + } + }); + + return graph; + }; + + combinedAction.transitionable = true; + context.perform(combinedAction, operation.annotation()); + window.setTimeout(function () { + context.validator().validate(); + }, 300); // after any transition + }; + + operation.available = function () { + return _actions.length && selectedIDs.length === _actions.length; + }; // don't cache this because the visible extent could change + + + operation.disabled = function () { + if (!_actions.length) return ''; + + var actionDisableds = _actions.map(function (action) { + return action.disabled(context.graph()); + }).filter(Boolean); + + if (actionDisableds.length === _actions.length) { + // none of the features can be circularized + if (new Set(actionDisableds).size > 1) { + return 'multiple_blockers'; + } + + return actionDisableds[0]; + } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) { + return 'too_large'; + } else if (someMissing()) { + return 'not_downloaded'; + } else if (selectedIDs.some(context.hasHiddenConnections)) { + return 'connected_to_hidden'; + } + + return false; + + function someMissing() { + if (context.inIntro()) return false; + var osm = context.connection(); + + if (osm) { + var missing = _coords.filter(function (loc) { + return !osm.isDataLoaded(loc); + }); + + if (missing.length) { + missing.forEach(function (loc) { + context.loadTileAtLoc(loc); + }); + return true; + } + } + + return false; + } + }; + + operation.tooltip = function () { + var disable = operation.disabled(); + return disable ? _t('operations.circularize.' + disable + '.' + _amount) : _t('operations.circularize.description.' + _amount); + }; + + operation.annotation = function () { + return _t('operations.circularize.annotation.feature', { + n: _actions.length + }); + }; + + operation.id = 'circularize'; + operation.keys = [_t('operations.circularize.key')]; + operation.title = _t('operations.circularize.title'); + operation.behavior = behaviorOperation(context).which(operation); + return operation; + } + + // For example, ⌘Z -> Ctrl+Z + + var uiCmd = function uiCmd(code) { + var detected = utilDetect(); + + if (detected.os === 'mac') { + return code; + } + + if (detected.os === 'win') { + if (code === '⌘⇧Z') return 'Ctrl+Y'; + } + + var result = '', + replacements = { + '⌘': 'Ctrl', + '⇧': 'Shift', + '⌥': 'Alt', + '⌫': 'Backspace', + '⌦': 'Delete' + }; + + for (var i = 0; i < code.length; i++) { + if (code[i] in replacements) { + result += replacements[code[i]] + (i < code.length - 1 ? '+' : ''); + } else { + result += code[i]; + } + } + + return result; + }; // return a display-focused string for a given keyboard code + + uiCmd.display = function (code) { + if (code.length !== 1) return code; + var detected = utilDetect(); + var mac = detected.os === 'mac'; + var replacements = { + '⌘': mac ? '⌘ ' + _t('shortcuts.key.cmd') : _t('shortcuts.key.ctrl'), + '⇧': mac ? '⇧ ' + _t('shortcuts.key.shift') : _t('shortcuts.key.shift'), + '⌥': mac ? '⌥ ' + _t('shortcuts.key.option') : _t('shortcuts.key.alt'), + '⌃': mac ? '⌃ ' + _t('shortcuts.key.ctrl') : _t('shortcuts.key.ctrl'), + '⌫': mac ? '⌫ ' + _t('shortcuts.key.delete') : _t('shortcuts.key.backspace'), + '⌦': mac ? '⌦ ' + _t('shortcuts.key.del') : _t('shortcuts.key.del'), + '↖': mac ? '↖ ' + _t('shortcuts.key.pgup') : _t('shortcuts.key.pgup'), + '↘': mac ? '↘ ' + _t('shortcuts.key.pgdn') : _t('shortcuts.key.pgdn'), + '⇞': mac ? '⇞ ' + _t('shortcuts.key.home') : _t('shortcuts.key.home'), + '⇟': mac ? '⇟ ' + _t('shortcuts.key.end') : _t('shortcuts.key.end'), + '↵': mac ? '⏎ ' + _t('shortcuts.key.return') : _t('shortcuts.key.enter'), + '⎋': mac ? '⎋ ' + _t('shortcuts.key.esc') : _t('shortcuts.key.esc'), + '☰': mac ? '☰ ' + _t('shortcuts.key.menu') : _t('shortcuts.key.menu') + }; + return replacements[code] || code; + }; + + function operationDelete(context, selectedIDs) { + var multi = selectedIDs.length === 1 ? 'single' : 'multiple'; + var action = actionDeleteMultiple(selectedIDs); + var nodes = utilGetAllNodes(selectedIDs, context.graph()); + var coords = nodes.map(function (n) { + return n.loc; + }); + var extent = utilTotalExtent(selectedIDs, context.graph()); + + var operation = function operation() { + var nextSelectedID; + var nextSelectedLoc; + + if (selectedIDs.length === 1) { + var id = selectedIDs[0]; + var entity = context.entity(id); + var geometry = entity.geometry(context.graph()); + var parents = context.graph().parentWays(entity); + var parent = parents[0]; // Select the next closest node in the way. + + if (geometry === 'vertex') { + var nodes = parent.nodes; + var i = nodes.indexOf(id); + + if (i === 0) { + i++; + } else if (i === nodes.length - 1) { + i--; + } else { + var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc); + var b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc); + i = a < b ? i - 1 : i + 1; + } + + nextSelectedID = nodes[i]; + nextSelectedLoc = context.entity(nextSelectedID).loc; + } + } + + context.perform(action, operation.annotation()); + context.validator().validate(); + + if (nextSelectedID && nextSelectedLoc) { + if (context.hasEntity(nextSelectedID)) { + context.enter(modeSelect(context, [nextSelectedID]).follow(true)); + } else { + context.map().centerEase(nextSelectedLoc); + context.enter(modeBrowse(context)); + } + } else { + context.enter(modeBrowse(context)); + } + }; + + operation.available = function () { + return true; + }; + + operation.disabled = function () { + if (extent.percentContainedIn(context.map().extent()) < 0.8) { + return 'too_large'; + } else if (someMissing()) { + return 'not_downloaded'; + } else if (selectedIDs.some(context.hasHiddenConnections)) { + return 'connected_to_hidden'; + } else if (selectedIDs.some(protectedMember)) { + return 'part_of_relation'; + } else if (selectedIDs.some(incompleteRelation)) { + return 'incomplete_relation'; + } else if (selectedIDs.some(hasWikidataTag)) { + return 'has_wikidata_tag'; + } + + return false; + + function someMissing() { + if (context.inIntro()) return false; + var osm = context.connection(); + + if (osm) { + var missing = coords.filter(function (loc) { + return !osm.isDataLoaded(loc); + }); + + if (missing.length) { + missing.forEach(function (loc) { + context.loadTileAtLoc(loc); + }); + return true; + } + } + + return false; + } + + function hasWikidataTag(id) { + var entity = context.entity(id); + return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0; + } + + function incompleteRelation(id) { + var entity = context.entity(id); + return entity.type === 'relation' && !entity.isComplete(context.graph()); + } + + function protectedMember(id) { + var entity = context.entity(id); + if (entity.type !== 'way') return false; + var parents = context.graph().parentRelations(entity); + + for (var i = 0; i < parents.length; i++) { + var parent = parents[i]; + var type = parent.tags.type; + var role = parent.memberById(id).role || 'outer'; + + if (type === 'route' || type === 'boundary' || type === 'multipolygon' && role === 'outer') { + return true; + } + } + + return false; + } + }; + + operation.tooltip = function () { + var disable = operation.disabled(); + return disable ? _t('operations.delete.' + disable + '.' + multi) : _t('operations.delete.description.' + multi); + }; + + operation.annotation = function () { + return selectedIDs.length === 1 ? _t('operations.delete.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.delete.annotation.feature', { + n: selectedIDs.length + }); + }; + + operation.id = 'delete'; + operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')]; + operation.title = _t('operations.delete.title'); + operation.behavior = behaviorOperation(context).which(operation); + return operation; + } + + function operationOrthogonalize(context, selectedIDs) { + var _extent; + + var _type; + + var _actions = selectedIDs.map(chooseAction).filter(Boolean); + + var _amount = _actions.length === 1 ? 'single' : 'multiple'; + + var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) { + return n.loc; + }); + + function chooseAction(entityID) { + var entity = context.entity(entityID); + var geometry = entity.geometry(context.graph()); + + if (!_extent) { + _extent = entity.extent(context.graph()); + } else { + _extent = _extent.extend(entity.extent(context.graph())); + } // square a line/area + + + if (entity.type === 'way' && new Set(entity.nodes).size > 2) { + if (_type && _type !== 'feature') return null; + _type = 'feature'; + return actionOrthogonalize(entityID, context.projection); // square a single vertex + } else if (geometry === 'vertex') { + if (_type && _type !== 'corner') return null; + _type = 'corner'; + var graph = context.graph(); + var parents = graph.parentWays(entity); + + if (parents.length === 1) { + var way = parents[0]; + + if (way.nodes.indexOf(entityID) !== -1) { + return actionOrthogonalize(way.id, context.projection, entityID); + } + } + } + + return null; + } + + var operation = function operation() { + if (!_actions.length) return; + + var combinedAction = function combinedAction(graph, t) { + _actions.forEach(function (action) { + if (!action.disabled(graph)) { + graph = action(graph, t); + } + }); + + return graph; + }; + + combinedAction.transitionable = true; + context.perform(combinedAction, operation.annotation()); + window.setTimeout(function () { + context.validator().validate(); + }, 300); // after any transition + }; + + operation.available = function () { + return _actions.length && selectedIDs.length === _actions.length; + }; // don't cache this because the visible extent could change + + + operation.disabled = function () { + if (!_actions.length) return ''; + + var actionDisableds = _actions.map(function (action) { + return action.disabled(context.graph()); + }).filter(Boolean); + + if (actionDisableds.length === _actions.length) { + // none of the features can be squared + if (new Set(actionDisableds).size > 1) { + return 'multiple_blockers'; + } + + return actionDisableds[0]; + } else if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) { + return 'too_large'; + } else if (someMissing()) { + return 'not_downloaded'; + } else if (selectedIDs.some(context.hasHiddenConnections)) { + return 'connected_to_hidden'; + } + + return false; + + function someMissing() { + if (context.inIntro()) return false; + var osm = context.connection(); + + if (osm) { + var missing = _coords.filter(function (loc) { + return !osm.isDataLoaded(loc); + }); + + if (missing.length) { + missing.forEach(function (loc) { + context.loadTileAtLoc(loc); + }); + return true; + } + } + + return false; + } + }; + + operation.tooltip = function () { + var disable = operation.disabled(); + return disable ? _t('operations.orthogonalize.' + disable + '.' + _amount) : _t('operations.orthogonalize.description.' + _type + '.' + _amount); + }; + + operation.annotation = function () { + return _t('operations.orthogonalize.annotation.' + _type, { + n: _actions.length + }); + }; + + operation.id = 'orthogonalize'; + operation.keys = [_t('operations.orthogonalize.key')]; + operation.title = _t('operations.orthogonalize.title'); + operation.behavior = behaviorOperation(context).which(operation); + return operation; + } + + function operationReflectShort(context, selectedIDs) { + return operationReflect(context, selectedIDs, 'short'); + } + function operationReflectLong(context, selectedIDs) { + return operationReflect(context, selectedIDs, 'long'); + } + function operationReflect(context, selectedIDs, axis) { + axis = axis || 'long'; + var multi = selectedIDs.length === 1 ? 'single' : 'multiple'; + var nodes = utilGetAllNodes(selectedIDs, context.graph()); + var coords = nodes.map(function (n) { + return n.loc; + }); + var extent = utilTotalExtent(selectedIDs, context.graph()); + + var operation = function operation() { + var action = actionReflect(selectedIDs, context.projection).useLongAxis(Boolean(axis === 'long')); + context.perform(action, operation.annotation()); + window.setTimeout(function () { + context.validator().validate(); + }, 300); // after any transition + }; + + operation.available = function () { + return nodes.length >= 3; + }; // don't cache this because the visible extent could change + + + operation.disabled = function () { + if (extent.percentContainedIn(context.map().extent()) < 0.8) { + return 'too_large'; + } else if (someMissing()) { + return 'not_downloaded'; + } else if (selectedIDs.some(context.hasHiddenConnections)) { + return 'connected_to_hidden'; + } else if (selectedIDs.some(incompleteRelation)) { + return 'incomplete_relation'; + } + + return false; + + function someMissing() { + if (context.inIntro()) return false; + var osm = context.connection(); + + if (osm) { + var missing = coords.filter(function (loc) { + return !osm.isDataLoaded(loc); + }); + + if (missing.length) { + missing.forEach(function (loc) { + context.loadTileAtLoc(loc); + }); + return true; + } + } + + return false; + } + + function incompleteRelation(id) { + var entity = context.entity(id); + return entity.type === 'relation' && !entity.isComplete(context.graph()); + } + }; + + operation.tooltip = function () { + var disable = operation.disabled(); + return disable ? _t('operations.reflect.' + disable + '.' + multi) : _t('operations.reflect.description.' + axis + '.' + multi); + }; + + operation.annotation = function () { + return _t('operations.reflect.annotation.' + axis + '.feature', { + n: selectedIDs.length + }); + }; + + operation.id = 'reflect-' + axis; + operation.keys = [_t('operations.reflect.key.' + axis)]; + operation.title = _t('operations.reflect.title.' + axis); + operation.behavior = behaviorOperation(context).which(operation); + return operation; + } + + function operationMove(context, selectedIDs) { + var multi = selectedIDs.length === 1 ? 'single' : 'multiple'; + var nodes = utilGetAllNodes(selectedIDs, context.graph()); + var coords = nodes.map(function (n) { + return n.loc; + }); + var extent = utilTotalExtent(selectedIDs, context.graph()); + + var operation = function operation() { + context.enter(modeMove(context, selectedIDs)); + }; + + operation.available = function () { + return selectedIDs.length > 0; + }; + + operation.disabled = function () { + if (extent.percentContainedIn(context.map().extent()) < 0.8) { + return 'too_large'; + } else if (someMissing()) { + return 'not_downloaded'; + } else if (selectedIDs.some(context.hasHiddenConnections)) { + return 'connected_to_hidden'; + } else if (selectedIDs.some(incompleteRelation)) { + return 'incomplete_relation'; + } + + return false; + + function someMissing() { + if (context.inIntro()) return false; + var osm = context.connection(); + + if (osm) { + var missing = coords.filter(function (loc) { + return !osm.isDataLoaded(loc); + }); + + if (missing.length) { + missing.forEach(function (loc) { + context.loadTileAtLoc(loc); + }); + return true; + } + } + + return false; + } + + function incompleteRelation(id) { + var entity = context.entity(id); + return entity.type === 'relation' && !entity.isComplete(context.graph()); + } + }; + + operation.tooltip = function () { + var disable = operation.disabled(); + return disable ? _t('operations.move.' + disable + '.' + multi) : _t('operations.move.description.' + multi); + }; + + operation.annotation = function () { + return selectedIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.move.annotation.feature', { + n: selectedIDs.length + }); + }; + + operation.id = 'move'; + operation.keys = [_t('operations.move.key')]; + operation.title = _t('operations.move.title'); + operation.behavior = behaviorOperation(context).which(operation); + operation.mouseOnly = true; + return operation; + } + + function modeRotate(context, entityIDs) { + var _tolerancePx = 4; // see also behaviorDrag, behaviorSelect, modeMove + + var mode = { + id: 'rotate', + button: 'browse' + }; + var keybinding = utilKeybinding('rotate'); + var behaviors = [behaviorEdit(context), operationCircularize(context, entityIDs).behavior, operationDelete(context, entityIDs).behavior, operationMove(context, entityIDs).behavior, operationOrthogonalize(context, entityIDs).behavior, operationReflectLong(context, entityIDs).behavior, operationReflectShort(context, entityIDs).behavior]; + var annotation = entityIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.rotate.annotation.feature', { + n: entityIDs.length + }); + + var _prevGraph; + + var _prevAngle; + + var _prevTransform; + + var _pivot; // use pointer events on supported platforms; fallback to mouse events + + + var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; + + function doRotate(d3_event) { + var fn; + + if (context.graph() !== _prevGraph) { + fn = context.perform; + } else { + fn = context.replace; + } // projection changed, recalculate _pivot + + + var projection = context.projection; + var currTransform = projection.transform(); + + if (!_prevTransform || currTransform.k !== _prevTransform.k || currTransform.x !== _prevTransform.x || currTransform.y !== _prevTransform.y) { + var nodes = utilGetAllNodes(entityIDs, context.graph()); + var points = nodes.map(function (n) { + return projection(n.loc); + }); + _pivot = getPivot(points); + _prevAngle = undefined; + } + + var currMouse = context.map().mouse(d3_event); + var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]); + if (typeof _prevAngle === 'undefined') _prevAngle = currAngle; + var delta = currAngle - _prevAngle; + fn(actionRotate(entityIDs, _pivot, delta, projection)); + _prevTransform = currTransform; + _prevAngle = currAngle; + _prevGraph = context.graph(); + } + + function getPivot(points) { + var _pivot; + + if (points.length === 1) { + _pivot = points[0]; + } else if (points.length === 2) { + _pivot = geoVecInterp(points[0], points[1], 0.5); + } else { + var polygonHull = d3_polygonHull(points); + + if (polygonHull.length === 2) { + _pivot = geoVecInterp(points[0], points[1], 0.5); + } else { + _pivot = d3_polygonCentroid(d3_polygonHull(points)); + } + } + + return _pivot; + } + + function finish(d3_event) { + d3_event.stopPropagation(); + context.replace(actionNoop(), annotation); + context.enter(modeSelect(context, entityIDs)); + } + + function cancel() { + if (_prevGraph) context.pop(); // remove the rotate + + context.enter(modeSelect(context, entityIDs)); + } + + function undone() { + context.enter(modeBrowse(context)); + } + + mode.enter = function () { + _prevGraph = null; + context.features().forceVisible(entityIDs); + behaviors.forEach(context.install); + var downEvent; + context.surface().on(_pointerPrefix + 'down.modeRotate', function (d3_event) { + downEvent = d3_event; + }); + select(window).on(_pointerPrefix + 'move.modeRotate', doRotate, true).on(_pointerPrefix + 'up.modeRotate', function (d3_event) { + if (!downEvent) return; + var mapNode = context.container().select('.main-map').node(); + var pointGetter = utilFastMouse(mapNode); + var p1 = pointGetter(downEvent); + var p2 = pointGetter(d3_event); + var dist = geoVecLength(p1, p2); + if (dist <= _tolerancePx) finish(d3_event); + downEvent = null; + }, true); + context.history().on('undone.modeRotate', undone); + keybinding.on('⎋', cancel).on('↩', finish); + select(document).call(keybinding); + }; + + mode.exit = function () { + behaviors.forEach(context.uninstall); + context.surface().on(_pointerPrefix + 'down.modeRotate', null); + select(window).on(_pointerPrefix + 'move.modeRotate', null, true).on(_pointerPrefix + 'up.modeRotate', null, true); + context.history().on('undone.modeRotate', null); + select(document).call(keybinding.unbind); + context.features().forceVisible([]); + }; + + mode.selectedIDs = function () { + if (!arguments.length) return entityIDs; // no assign + + return mode; + }; + + return mode; + } + + function operationRotate(context, selectedIDs) { + var multi = selectedIDs.length === 1 ? 'single' : 'multiple'; + var nodes = utilGetAllNodes(selectedIDs, context.graph()); + var coords = nodes.map(function (n) { + return n.loc; + }); + var extent = utilTotalExtent(selectedIDs, context.graph()); + + var operation = function operation() { + context.enter(modeRotate(context, selectedIDs)); + }; + + operation.available = function () { + return nodes.length >= 2; + }; + + operation.disabled = function () { + if (extent.percentContainedIn(context.map().extent()) < 0.8) { + return 'too_large'; + } else if (someMissing()) { + return 'not_downloaded'; + } else if (selectedIDs.some(context.hasHiddenConnections)) { + return 'connected_to_hidden'; + } else if (selectedIDs.some(incompleteRelation)) { + return 'incomplete_relation'; + } + + return false; + + function someMissing() { + if (context.inIntro()) return false; + var osm = context.connection(); + + if (osm) { + var missing = coords.filter(function (loc) { + return !osm.isDataLoaded(loc); + }); + + if (missing.length) { + missing.forEach(function (loc) { + context.loadTileAtLoc(loc); + }); + return true; + } + } + + return false; + } + + function incompleteRelation(id) { + var entity = context.entity(id); + return entity.type === 'relation' && !entity.isComplete(context.graph()); + } + }; + + operation.tooltip = function () { + var disable = operation.disabled(); + return disable ? _t('operations.rotate.' + disable + '.' + multi) : _t('operations.rotate.description.' + multi); + }; + + operation.annotation = function () { + return selectedIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.rotate.annotation.feature', { + n: selectedIDs.length + }); + }; + + operation.id = 'rotate'; + operation.keys = [_t('operations.rotate.key')]; + operation.title = _t('operations.rotate.title'); + operation.behavior = behaviorOperation(context).which(operation); + operation.mouseOnly = true; + return operation; + } + + function modeMove(context, entityIDs, baseGraph) { + var _tolerancePx = 4; // see also behaviorDrag, behaviorSelect, modeRotate + + var mode = { + id: 'move', + button: 'browse' + }; + var keybinding = utilKeybinding('move'); + var behaviors = [behaviorEdit(context), operationCircularize(context, entityIDs).behavior, operationDelete(context, entityIDs).behavior, operationOrthogonalize(context, entityIDs).behavior, operationReflectLong(context, entityIDs).behavior, operationReflectShort(context, entityIDs).behavior, operationRotate(context, entityIDs).behavior]; + var annotation = entityIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.move.annotation.feature', { + n: entityIDs.length + }); + + var _prevGraph; + + var _cache; + + var _origin; + + var _nudgeInterval; // use pointer events on supported platforms; fallback to mouse events + + + var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; + + function doMove(nudge) { + nudge = nudge || [0, 0]; + var fn; + + if (_prevGraph !== context.graph()) { + _cache = {}; + _origin = context.map().mouseCoordinates(); + fn = context.perform; + } else { + fn = context.overwrite; + } + + var currMouse = context.map().mouse(); + var origMouse = context.projection(_origin); + var delta = geoVecSubtract(geoVecSubtract(currMouse, origMouse), nudge); + fn(actionMove(entityIDs, delta, context.projection, _cache)); + _prevGraph = context.graph(); + } + + function startNudge(nudge) { + if (_nudgeInterval) window.clearInterval(_nudgeInterval); + _nudgeInterval = window.setInterval(function () { + context.map().pan(nudge); + doMove(nudge); + }, 50); + } + + function stopNudge() { + if (_nudgeInterval) { + window.clearInterval(_nudgeInterval); + _nudgeInterval = null; + } + } + + function move() { + doMove(); + var nudge = geoViewportEdge(context.map().mouse(), context.map().dimensions()); + + if (nudge) { + startNudge(nudge); + } else { + stopNudge(); + } + } + + function finish(d3_event) { + d3_event.stopPropagation(); + context.replace(actionNoop(), annotation); + context.enter(modeSelect(context, entityIDs)); + stopNudge(); + } + + function cancel() { + if (baseGraph) { + while (context.graph() !== baseGraph) { + context.pop(); + } // reset to baseGraph + + + context.enter(modeBrowse(context)); + } else { + if (_prevGraph) context.pop(); // remove the move + + context.enter(modeSelect(context, entityIDs)); + } + + stopNudge(); + } + + function undone() { + context.enter(modeBrowse(context)); + } + + mode.enter = function () { + _origin = context.map().mouseCoordinates(); + _prevGraph = null; + _cache = {}; + context.features().forceVisible(entityIDs); + behaviors.forEach(context.install); + var downEvent; + context.surface().on(_pointerPrefix + 'down.modeMove', function (d3_event) { + downEvent = d3_event; + }); + select(window).on(_pointerPrefix + 'move.modeMove', move, true).on(_pointerPrefix + 'up.modeMove', function (d3_event) { + if (!downEvent) return; + var mapNode = context.container().select('.main-map').node(); + var pointGetter = utilFastMouse(mapNode); + var p1 = pointGetter(downEvent); + var p2 = pointGetter(d3_event); + var dist = geoVecLength(p1, p2); + if (dist <= _tolerancePx) finish(d3_event); + downEvent = null; + }, true); + context.history().on('undone.modeMove', undone); + keybinding.on('⎋', cancel).on('↩', finish); + select(document).call(keybinding); + }; + + mode.exit = function () { + stopNudge(); + behaviors.forEach(function (behavior) { + context.uninstall(behavior); + }); + context.surface().on(_pointerPrefix + 'down.modeMove', null); + select(window).on(_pointerPrefix + 'move.modeMove', null, true).on(_pointerPrefix + 'up.modeMove', null, true); + context.history().on('undone.modeMove', null); + select(document).call(keybinding.unbind); + context.features().forceVisible([]); + }; + + mode.selectedIDs = function () { + if (!arguments.length) return entityIDs; // no assign + + return mode; + }; + + return mode; + } + + function behaviorPaste(context) { + function doPaste(d3_event) { + // prevent paste during low zoom selection + if (!context.map().withinEditableZoom()) return; + d3_event.preventDefault(); + var baseGraph = context.graph(); + var mouse = context.map().mouse(); + var projection = context.projection; + var viewport = geoExtent(projection.clipExtent()).polygon(); + if (!geoPointInPolygon(mouse, viewport)) return; + var oldIDs = context.copyIDs(); + if (!oldIDs.length) return; + var extent = geoExtent(); + var oldGraph = context.copyGraph(); + var newIDs = []; + var action = actionCopyEntities(oldIDs, oldGraph); + context.perform(action); + var copies = action.copies(); + var originals = new Set(); + Object.values(copies).forEach(function (entity) { + originals.add(entity.id); + }); + + for (var id in copies) { + var oldEntity = oldGraph.entity(id); + var newEntity = copies[id]; + + extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied. + + + var parents = context.graph().parentWays(newEntity); + var parentCopied = parents.some(function (parent) { + return originals.has(parent.id); + }); + + if (!parentCopied) { + newIDs.push(newEntity.id); + } + } // Put pasted objects where mouse pointer is.. + + + var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center()); + var delta = geoVecSubtract(mouse, copyPoint); + context.perform(actionMove(newIDs, delta, projection)); + context.enter(modeMove(context, newIDs, baseGraph)); + } + + function behavior() { + context.keybinding().on(uiCmd('⌘V'), doPaste); + return behavior; + } + + behavior.off = function () { + context.keybinding().off(uiCmd('⌘V')); + }; + + return behavior; + } + + var $$5 = _export; + var repeat = stringRepeat; + + // `String.prototype.repeat` method + // https://tc39.es/ecma262/#sec-string.prototype.repeat + $$5({ target: 'String', proto: true }, { + repeat: repeat + }); + + /* + `behaviorDrag` is like `d3_behavior.drag`, with the following differences: + + * The `origin` function is expected to return an [x, y] tuple rather than an + {x, y} object. + * The events are `start`, `move`, and `end`. + (https://github.com/mbostock/d3/issues/563) + * The `start` event is not dispatched until the first cursor movement occurs. + (https://github.com/mbostock/d3/pull/368) + * The `move` event has a `point` and `delta` [x, y] tuple properties rather + than `x`, `y`, `dx`, and `dy` properties. + * The `end` event is not dispatched if no movement occurs. + * An `off` function is available that unbinds the drag's internal event handlers. + */ + + function behaviorDrag() { + var dispatch = dispatch$8('start', 'move', 'end'); // see also behaviorSelect + + var _tolerancePx = 1; // keep this low to facilitate pixel-perfect micromapping + + var _penTolerancePx = 4; // styluses can be touchy so require greater movement - #1981 + + var _origin = null; + var _selector = ''; + + var _targetNode; + + var _targetEntity; + + var _surface; + + var _pointerId; // use pointer events on supported platforms; fallback to mouse events + + + var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; + + var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect'); + + var d3_event_userSelectSuppress = function d3_event_userSelectSuppress() { + var selection$1 = selection(); + var select = selection$1.style(d3_event_userSelectProperty); + selection$1.style(d3_event_userSelectProperty, 'none'); + return function () { + selection$1.style(d3_event_userSelectProperty, select); + }; + }; + + function pointerdown(d3_event) { + if (_pointerId) return; + _pointerId = d3_event.pointerId || 'mouse'; + _targetNode = this; // only force reflow once per drag + + var pointerLocGetter = utilFastMouse(_surface || _targetNode.parentNode); + var offset; + var startOrigin = pointerLocGetter(d3_event); + var started = false; + var selectEnable = d3_event_userSelectSuppress(); + select(window).on(_pointerPrefix + 'move.drag', pointermove).on(_pointerPrefix + 'up.drag pointercancel.drag', pointerup, true); + + if (_origin) { + offset = _origin.call(_targetNode, _targetEntity); + offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]]; + } else { + offset = [0, 0]; + } + + d3_event.stopPropagation(); + + function pointermove(d3_event) { + if (_pointerId !== (d3_event.pointerId || 'mouse')) return; + var p = pointerLocGetter(d3_event); + + if (!started) { + var dist = geoVecLength(startOrigin, p); + var tolerance = d3_event.pointerType === 'pen' ? _penTolerancePx : _tolerancePx; // don't start until the drag has actually moved somewhat + + if (dist < tolerance) return; + started = true; + dispatch.call('start', this, d3_event, _targetEntity); // Don't send a `move` event in the same cycle as `start` since dragging + // a midpoint will convert the target to a node. + } else { + startOrigin = p; + d3_event.stopPropagation(); + d3_event.preventDefault(); + var dx = p[0] - startOrigin[0]; + var dy = p[1] - startOrigin[1]; + dispatch.call('move', this, d3_event, _targetEntity, [p[0] + offset[0], p[1] + offset[1]], [dx, dy]); + } + } + + function pointerup(d3_event) { + if (_pointerId !== (d3_event.pointerId || 'mouse')) return; + _pointerId = null; + + if (started) { + dispatch.call('end', this, d3_event, _targetEntity); + d3_event.preventDefault(); + } + + select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null); + selectEnable(); + } + } + + function behavior(selection) { + var matchesSelector = utilPrefixDOMProperty('matchesSelector'); + var delegate = pointerdown; + + if (_selector) { + delegate = function delegate(d3_event) { + var root = this; + var target = d3_event.target; + + for (; target && target !== root; target = target.parentNode) { + var datum = target.__data__; + _targetEntity = datum instanceof osmNote ? datum : datum && datum.properties && datum.properties.entity; + + if (_targetEntity && target[matchesSelector](_selector)) { + return pointerdown.call(target, d3_event); + } + } + }; + } + + selection.on(_pointerPrefix + 'down.drag' + _selector, delegate); + } + + behavior.off = function (selection) { + selection.on(_pointerPrefix + 'down.drag' + _selector, null); + }; + + behavior.selector = function (_) { + if (!arguments.length) return _selector; + _selector = _; + return behavior; + }; + + behavior.origin = function (_) { + if (!arguments.length) return _origin; + _origin = _; + return behavior; + }; + + behavior.cancel = function () { + select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null); + return behavior; + }; + + behavior.targetNode = function (_) { + if (!arguments.length) return _targetNode; + _targetNode = _; + return behavior; + }; + + behavior.targetEntity = function (_) { + if (!arguments.length) return _targetEntity; + _targetEntity = _; + return behavior; + }; + + behavior.surface = function (_) { + if (!arguments.length) return _surface; + _surface = _; + return behavior; + }; + + return utilRebind(behavior, dispatch, 'on'); + } + + function modeDragNode(context) { + var mode = { + id: 'drag-node', + button: 'browse' + }; + var hover = behaviorHover(context).altDisables(true).on('hover', context.ui().sidebar.hover); + var edit = behaviorEdit(context); + + var _nudgeInterval; + + var _restoreSelectedIDs = []; + var _wasMidpoint = false; + var _isCancelled = false; + + var _activeEntity; + + var _startLoc; + + var _lastLoc; + + function startNudge(d3_event, entity, nudge) { + if (_nudgeInterval) window.clearInterval(_nudgeInterval); + _nudgeInterval = window.setInterval(function () { + context.map().pan(nudge); + doMove(d3_event, entity, nudge); + }, 50); + } + + function stopNudge() { + if (_nudgeInterval) { + window.clearInterval(_nudgeInterval); + _nudgeInterval = null; + } + } + + function moveAnnotation(entity) { + return _t('operations.move.annotation.' + entity.geometry(context.graph())); + } + + function connectAnnotation(nodeEntity, targetEntity) { + var nodeGeometry = nodeEntity.geometry(context.graph()); + var targetGeometry = targetEntity.geometry(context.graph()); + + if (nodeGeometry === 'vertex' && targetGeometry === 'vertex') { + var nodeParentWayIDs = context.graph().parentWays(nodeEntity); + var targetParentWayIDs = context.graph().parentWays(targetEntity); + var sharedParentWays = utilArrayIntersection(nodeParentWayIDs, targetParentWayIDs); // if both vertices are part of the same way + + if (sharedParentWays.length !== 0) { + // if the nodes are next to each other, they are merged + if (sharedParentWays[0].areAdjacent(nodeEntity.id, targetEntity.id)) { + return _t('operations.connect.annotation.from_vertex.to_adjacent_vertex'); + } + + return _t('operations.connect.annotation.from_vertex.to_sibling_vertex'); + } + } + + return _t('operations.connect.annotation.from_' + nodeGeometry + '.to_' + targetGeometry); + } + + function shouldSnapToNode(target) { + if (!_activeEntity) return false; + return _activeEntity.geometry(context.graph()) !== 'vertex' || target.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(target, context.graph()); + } + + function origin(entity) { + return context.projection(entity.loc); + } + + function keydown(d3_event) { + if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) { + if (context.surface().classed('nope')) { + context.surface().classed('nope-suppressed', true); + } + + context.surface().classed('nope', false).classed('nope-disabled', true); + } + } + + function keyup(d3_event) { + if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) { + if (context.surface().classed('nope-suppressed')) { + context.surface().classed('nope', true); + } + + context.surface().classed('nope-suppressed', false).classed('nope-disabled', false); + } + } + + function start(d3_event, entity) { + _wasMidpoint = entity.type === 'midpoint'; + var hasHidden = context.features().hasHiddenConnections(entity, context.graph()); + _isCancelled = !context.editable() || d3_event.shiftKey || hasHidden; + + if (_isCancelled) { + if (hasHidden) { + context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('modes.drag_node.connected_to_hidden'))(); + } + + return drag.cancel(); + } + + if (_wasMidpoint) { + var midpoint = entity; + entity = osmNode(); + context.perform(actionAddMidpoint(midpoint, entity)); + entity = context.entity(entity.id); // get post-action entity + + var vertex = context.surface().selectAll('.' + entity.id); + drag.targetNode(vertex.node()).targetEntity(entity); + } else { + context.perform(actionNoop()); + } + + _activeEntity = entity; + _startLoc = entity.loc; + hover.ignoreVertex(entity.geometry(context.graph()) === 'vertex'); + context.surface().selectAll('.' + _activeEntity.id).classed('active', true); + context.enter(mode); + } // related code + // - `behavior/draw.js` `datum()` + + + function datum(d3_event) { + if (!d3_event || d3_event.altKey) { + return {}; + } else { + // When dragging, snap only to touch targets.. + // (this excludes area fills and active drawing elements) + var d = d3_event.target.__data__; + return d && d.properties && d.properties.target ? d : {}; + } + } + + function doMove(d3_event, entity, nudge) { + nudge = nudge || [0, 0]; + var currPoint = d3_event && d3_event.point || context.projection(_lastLoc); + var currMouse = geoVecSubtract(currPoint, nudge); + var loc = context.projection.invert(currMouse); + var target, edge; + + if (!_nudgeInterval) { + // If not nudging at the edge of the viewport, try to snap.. + // related code + // - `mode/drag_node.js` `doMove()` + // - `behavior/draw.js` `click()` + // - `behavior/draw_way.js` `move()` + var d = datum(d3_event); + target = d && d.properties && d.properties.entity; + var targetLoc = target && target.loc; + var targetNodes = d && d.properties && d.properties.nodes; + + if (targetLoc) { + // snap to node/vertex - a point target with `.loc` + if (shouldSnapToNode(target)) { + loc = targetLoc; + } + } else if (targetNodes) { + // snap to way - a line target with `.nodes` + edge = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, end.id); + + if (edge) { + loc = edge.loc; + } + } + } + + context.replace(actionMoveNode(entity.id, loc)); // Below here: validations + + var isInvalid = false; // Check if this connection to `target` could cause relations to break.. + + if (target) { + isInvalid = hasRelationConflict(entity, target, edge, context.graph()); + } // Check if this drag causes the geometry to break.. + + + if (!isInvalid) { + isInvalid = hasInvalidGeometry(entity, context.graph()); + } + + var nope = context.surface().classed('nope'); + + if (isInvalid === 'relation' || isInvalid === 'restriction') { + if (!nope) { + // about to nope - show hint + context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('operations.connect.' + isInvalid, { + relation: _mainPresetIndex.item('type/restriction').name() + }))(); + } + } else if (isInvalid) { + var errorID = isInvalid === 'line' ? 'lines' : 'areas'; + context.ui().flash.duration(3000).iconName('#iD-icon-no').label(_t('self_intersection.error.' + errorID))(); + } else { + if (nope) { + // about to un-nope, remove hint + context.ui().flash.duration(1).label('')(); + } + } + + var nopeDisabled = context.surface().classed('nope-disabled'); + + if (nopeDisabled) { + context.surface().classed('nope', false).classed('nope-suppressed', isInvalid); + } else { + context.surface().classed('nope', isInvalid).classed('nope-suppressed', false); + } + + _lastLoc = loc; + } // Uses `actionConnect.disabled()` to know whether this connection is ok.. + + + function hasRelationConflict(entity, target, edge, graph) { + var testGraph = graph.update(); // copy + // if snapping to way - add midpoint there and consider that the target.. + + if (edge) { + var midpoint = osmNode(); + var action = actionAddMidpoint({ + loc: edge.loc, + edge: [target.nodes[edge.index - 1], target.nodes[edge.index]] + }, midpoint); + testGraph = action(testGraph); + target = midpoint; + } // can we connect to it? + + + var ids = [entity.id, target.id]; + return actionConnect(ids).disabled(testGraph); + } + + function hasInvalidGeometry(entity, graph) { + var parents = graph.parentWays(entity); + var i, j, k; + + for (i = 0; i < parents.length; i++) { + var parent = parents[i]; + var nodes = []; + var activeIndex = null; // which multipolygon ring contains node being dragged + // test any parent multipolygons for valid geometry + + var relations = graph.parentRelations(parent); + + for (j = 0; j < relations.length; j++) { + if (!relations[j].isMultipolygon()) continue; + var rings = osmJoinWays(relations[j].members, graph); // find active ring and test it for self intersections + + for (k = 0; k < rings.length; k++) { + nodes = rings[k].nodes; + + if (nodes.find(function (n) { + return n.id === entity.id; + })) { + activeIndex = k; + + if (geoHasSelfIntersections(nodes, entity.id)) { + return 'multipolygonMember'; + } + } + + rings[k].coords = nodes.map(function (n) { + return n.loc; + }); + } // test active ring for intersections with other rings in the multipolygon + + + for (k = 0; k < rings.length; k++) { + if (k === activeIndex) continue; // make sure active ring doesn't cross passive rings + + if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) { + return 'multipolygonRing'; + } + } + } // If we still haven't tested this node's parent way for self-intersections. + // (because it's not a member of a multipolygon), test it now. + + + if (activeIndex === null) { + nodes = parent.nodes.map(function (nodeID) { + return graph.entity(nodeID); + }); + + if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) { + return parent.geometry(graph); + } + } + } + + return false; + } + + function move(d3_event, entity, point) { + if (_isCancelled) return; + d3_event.stopPropagation(); + context.surface().classed('nope-disabled', d3_event.altKey); + _lastLoc = context.projection.invert(point); + doMove(d3_event, entity); + var nudge = geoViewportEdge(point, context.map().dimensions()); + + if (nudge) { + startNudge(d3_event, entity, nudge); + } else { + stopNudge(); + } + } + + function end(d3_event, entity) { + if (_isCancelled) return; + var wasPoint = entity.geometry(context.graph()) === 'point'; + var d = datum(d3_event); + var nope = d && d.properties && d.properties.nope || context.surface().classed('nope'); + var target = d && d.properties && d.properties.entity; // entity to snap to + + if (nope) { + // bounce back + context.perform(_actionBounceBack(entity.id, _startLoc)); + } else if (target && target.type === 'way') { + var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id); + context.replace(actionAddMidpoint({ + loc: choice.loc, + edge: [target.nodes[choice.index - 1], target.nodes[choice.index]] + }, entity), connectAnnotation(entity, target)); + } else if (target && target.type === 'node' && shouldSnapToNode(target)) { + context.replace(actionConnect([target.id, entity.id]), connectAnnotation(entity, target)); + } else if (_wasMidpoint) { + context.replace(actionNoop(), _t('operations.add.annotation.vertex')); + } else { + context.replace(actionNoop(), moveAnnotation(entity)); + } + + if (wasPoint) { + context.enter(modeSelect(context, [entity.id])); + } else { + var reselection = _restoreSelectedIDs.filter(function (id) { + return context.graph().hasEntity(id); + }); + + if (reselection.length) { + context.enter(modeSelect(context, reselection)); + } else { + context.enter(modeBrowse(context)); + } + } + } + + function _actionBounceBack(nodeID, toLoc) { + var moveNode = actionMoveNode(nodeID, toLoc); + + var action = function action(graph, t) { + // last time through, pop off the bounceback perform. + // it will then overwrite the initial perform with a moveNode that does nothing + if (t === 1) context.pop(); + return moveNode(graph, t); + }; + + action.transitionable = true; + return action; + } + + function cancel() { + drag.cancel(); + context.enter(modeBrowse(context)); + } + + var drag = behaviorDrag().selector('.layer-touch.points .target').surface(context.container().select('.main-map').node()).origin(origin).on('start', start).on('move', move).on('end', end); + + mode.enter = function () { + context.install(hover); + context.install(edit); + select(window).on('keydown.dragNode', keydown).on('keyup.dragNode', keyup); + context.history().on('undone.drag-node', cancel); + }; + + mode.exit = function () { + context.ui().sidebar.hover.cancel(); + context.uninstall(hover); + context.uninstall(edit); + select(window).on('keydown.dragNode', null).on('keyup.dragNode', null); + context.history().on('undone.drag-node', null); + _activeEntity = null; + context.surface().classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false).selectAll('.active').classed('active', false); + stopNudge(); + }; + + mode.selectedIDs = function () { + if (!arguments.length) return _activeEntity ? [_activeEntity.id] : []; // no assign + + return mode; + }; + + mode.activeID = function () { + if (!arguments.length) return _activeEntity && _activeEntity.id; // no assign + + return mode; + }; + + mode.restoreSelectedIDs = function (_) { + if (!arguments.length) return _restoreSelectedIDs; + _restoreSelectedIDs = _; + return mode; + }; + + mode.behavior = drag; + return mode; + } + + var $$4 = _export; + var NativePromise = nativePromiseConstructor; + var fails$1 = fails$N; + var getBuiltIn = getBuiltIn$9; + var speciesConstructor = speciesConstructor$8; + var promiseResolve = promiseResolve$2; + var redefine = redefine$g.exports; + + // Safari bug https://bugs.webkit.org/show_bug.cgi?id=200829 + var NON_GENERIC = !!NativePromise && fails$1(function () { + NativePromise.prototype['finally'].call({ then: function () { /* empty */ } }, function () { /* empty */ }); + }); + + // `Promise.prototype.finally` method + // https://tc39.es/ecma262/#sec-promise.prototype.finally + $$4({ target: 'Promise', proto: true, real: true, forced: NON_GENERIC }, { + 'finally': function (onFinally) { + var C = speciesConstructor(this, getBuiltIn('Promise')); + var isFunction = typeof onFinally == 'function'; + return this.then( + isFunction ? function (x) { + return promiseResolve(C, onFinally()).then(function () { return x; }); + } : onFinally, + isFunction ? function (e) { + return promiseResolve(C, onFinally()).then(function () { throw e; }); + } : onFinally + ); + } + }); + + // makes sure that native promise-based APIs `Promise#finally` properly works with patched `Promise#then` + if (typeof NativePromise == 'function') { + var method = getBuiltIn('Promise').prototype['finally']; + if (NativePromise.prototype['finally'] !== method) { + redefine(NativePromise.prototype, 'finally', method, { unsafe: true }); + } + } + + function quickselect(arr, k, left, right, compare) { + quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare); + } + + function quickselectStep(arr, k, left, right, compare) { + while (right > left) { + if (right - left > 600) { + var n = right - left + 1; + var m = k - left + 1; + var z = Math.log(n); + var s = 0.5 * Math.exp(2 * z / 3); + var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); + var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); + var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); + quickselectStep(arr, k, newLeft, newRight, compare); + } + + var t = arr[k]; + var i = left; + var j = right; + swap(arr, left, k); + if (compare(arr[right], t) > 0) swap(arr, left, right); + + while (i < j) { + swap(arr, i, j); + i++; + j--; + + while (compare(arr[i], t) < 0) { + i++; + } + + while (compare(arr[j], t) > 0) { + j--; + } + } + + if (compare(arr[left], t) === 0) swap(arr, left, j);else { + j++; + swap(arr, j, right); + } + if (j <= k) left = j + 1; + if (k <= j) right = j - 1; + } + } + + function swap(arr, i, j) { + var tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + function defaultCompare(a, b) { + return a < b ? -1 : a > b ? 1 : 0; + } + + var RBush = /*#__PURE__*/function () { + function RBush() { + var maxEntries = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 9; + + _classCallCheck$1(this, RBush); + + // max entries in a node is 9 by default; min node fill is 40% for best performance + this._maxEntries = Math.max(4, maxEntries); + this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4)); + this.clear(); + } + + _createClass$1(RBush, [{ + key: "all", + value: function all() { + return this._all(this.data, []); + } + }, { + key: "search", + value: function search(bbox) { + var node = this.data; + var result = []; + if (!intersects(bbox, node)) return result; + var toBBox = this.toBBox; + var nodesToSearch = []; + + while (node) { + for (var i = 0; i < node.children.length; i++) { + var child = node.children[i]; + var childBBox = node.leaf ? toBBox(child) : child; + + if (intersects(bbox, childBBox)) { + if (node.leaf) result.push(child);else if (contains(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child); + } + } + + node = nodesToSearch.pop(); + } + + return result; + } + }, { + key: "collides", + value: function collides(bbox) { + var node = this.data; + if (!intersects(bbox, node)) return false; + var nodesToSearch = []; + + while (node) { + for (var i = 0; i < node.children.length; i++) { + var child = node.children[i]; + var childBBox = node.leaf ? this.toBBox(child) : child; + + if (intersects(bbox, childBBox)) { + if (node.leaf || contains(bbox, childBBox)) return true; + nodesToSearch.push(child); + } + } + + node = nodesToSearch.pop(); + } + + return false; + } + }, { + key: "load", + value: function load(data) { + if (!(data && data.length)) return this; + + if (data.length < this._minEntries) { + for (var i = 0; i < data.length; i++) { + this.insert(data[i]); + } + + return this; + } // recursively build the tree with the given data from scratch using OMT algorithm + + + var node = this._build(data.slice(), 0, data.length - 1, 0); + + if (!this.data.children.length) { + // save as is if tree is empty + this.data = node; + } else if (this.data.height === node.height) { + // split root if trees have the same height + this._splitRoot(this.data, node); + } else { + if (this.data.height < node.height) { + // swap trees if inserted one is bigger + var tmpNode = this.data; + this.data = node; + node = tmpNode; + } // insert the small tree into the large tree at appropriate level + + + this._insert(node, this.data.height - node.height - 1, true); + } + + return this; + } + }, { + key: "insert", + value: function insert(item) { + if (item) this._insert(item, this.data.height - 1); + return this; + } + }, { + key: "clear", + value: function clear() { + this.data = createNode([]); + return this; + } + }, { + key: "remove", + value: function remove(item, equalsFn) { + if (!item) return this; + var node = this.data; + var bbox = this.toBBox(item); + var path = []; + var indexes = []; + var i, parent, goingUp; // depth-first iterative tree traversal + + while (node || path.length) { + if (!node) { + // go up + node = path.pop(); + parent = path[path.length - 1]; + i = indexes.pop(); + goingUp = true; + } + + if (node.leaf) { + // check current node + var index = findItem(item, node.children, equalsFn); + + if (index !== -1) { + // item found, remove the item and condense tree upwards + node.children.splice(index, 1); + path.push(node); + + this._condense(path); + + return this; + } + } + + if (!goingUp && !node.leaf && contains(node, bbox)) { + // go down + path.push(node); + indexes.push(i); + i = 0; + parent = node; + node = node.children[0]; + } else if (parent) { + // go right + i++; + node = parent.children[i]; + goingUp = false; + } else node = null; // nothing found + + } + + return this; + } + }, { + key: "toBBox", + value: function toBBox(item) { + return item; + } + }, { + key: "compareMinX", + value: function compareMinX(a, b) { + return a.minX - b.minX; + } + }, { + key: "compareMinY", + value: function compareMinY(a, b) { + return a.minY - b.minY; + } + }, { + key: "toJSON", + value: function toJSON() { + return this.data; + } + }, { + key: "fromJSON", + value: function fromJSON(data) { + this.data = data; + return this; + } + }, { + key: "_all", + value: function _all(node, result) { + var nodesToSearch = []; + + while (node) { + if (node.leaf) result.push.apply(result, _toConsumableArray(node.children));else nodesToSearch.push.apply(nodesToSearch, _toConsumableArray(node.children)); + node = nodesToSearch.pop(); + } + + return result; + } + }, { + key: "_build", + value: function _build(items, left, right, height) { + var N = right - left + 1; + var M = this._maxEntries; + var node; + + if (N <= M) { + // reached leaf level; return leaf + node = createNode(items.slice(left, right + 1)); + calcBBox(node, this.toBBox); + return node; + } + + if (!height) { + // target height of the bulk-loaded tree + height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization + + M = Math.ceil(N / Math.pow(M, height - 1)); + } + + node = createNode([]); + node.leaf = false; + node.height = height; // split the items into M mostly square tiles + + var N2 = Math.ceil(N / M); + var N1 = N2 * Math.ceil(Math.sqrt(M)); + multiSelect(items, left, right, N1, this.compareMinX); + + for (var i = left; i <= right; i += N1) { + var right2 = Math.min(i + N1 - 1, right); + multiSelect(items, i, right2, N2, this.compareMinY); + + for (var j = i; j <= right2; j += N2) { + var right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively + + node.children.push(this._build(items, j, right3, height - 1)); + } + } + + calcBBox(node, this.toBBox); + return node; + } + }, { + key: "_chooseSubtree", + value: function _chooseSubtree(bbox, node, level, path) { + while (true) { + path.push(node); + if (node.leaf || path.length - 1 === level) break; + var minArea = Infinity; + var minEnlargement = Infinity; + var targetNode = void 0; + + for (var i = 0; i < node.children.length; i++) { + var child = node.children[i]; + var area = bboxArea(child); + var enlargement = enlargedArea(bbox, child) - area; // choose entry with the least area enlargement + + if (enlargement < minEnlargement) { + minEnlargement = enlargement; + minArea = area < minArea ? area : minArea; + targetNode = child; + } else if (enlargement === minEnlargement) { + // otherwise choose one with the smallest area + if (area < minArea) { + minArea = area; + targetNode = child; + } + } + } + + node = targetNode || node.children[0]; + } + + return node; + } + }, { + key: "_insert", + value: function _insert(item, level, isNode) { + var bbox = isNode ? item : this.toBBox(item); + var insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too + + var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node + + + node.children.push(item); + extend$1(node, bbox); // split on node overflow; propagate upwards if necessary + + while (level >= 0) { + if (insertPath[level].children.length > this._maxEntries) { + this._split(insertPath, level); + + level--; + } else break; + } // adjust bboxes along the insertion path + + + this._adjustParentBBoxes(bbox, insertPath, level); + } // split overflowed node into two + + }, { + key: "_split", + value: function _split(insertPath, level) { + var node = insertPath[level]; + var M = node.children.length; + var m = this._minEntries; + + this._chooseSplitAxis(node, m, M); + + var splitIndex = this._chooseSplitIndex(node, m, M); + + var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex)); + newNode.height = node.height; + newNode.leaf = node.leaf; + calcBBox(node, this.toBBox); + calcBBox(newNode, this.toBBox); + if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode); + } + }, { + key: "_splitRoot", + value: function _splitRoot(node, newNode) { + // split root node + this.data = createNode([node, newNode]); + this.data.height = node.height + 1; + this.data.leaf = false; + calcBBox(this.data, this.toBBox); + } + }, { + key: "_chooseSplitIndex", + value: function _chooseSplitIndex(node, m, M) { + var index; + var minOverlap = Infinity; + var minArea = Infinity; + + for (var i = m; i <= M - m; i++) { + var bbox1 = distBBox(node, 0, i, this.toBBox); + var bbox2 = distBBox(node, i, M, this.toBBox); + var overlap = intersectionArea(bbox1, bbox2); + var area = bboxArea(bbox1) + bboxArea(bbox2); // choose distribution with minimum overlap + + if (overlap < minOverlap) { + minOverlap = overlap; + index = i; + minArea = area < minArea ? area : minArea; + } else if (overlap === minOverlap) { + // otherwise choose distribution with minimum area + if (area < minArea) { + minArea = area; + index = i; + } + } + } + + return index || M - m; + } // sorts node children by the best axis for split + + }, { + key: "_chooseSplitAxis", + value: function _chooseSplitAxis(node, m, M) { + var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX; + var compareMinY = node.leaf ? this.compareMinY : compareNodeMinY; + + var xMargin = this._allDistMargin(node, m, M, compareMinX); + + var yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX, + // otherwise it's already sorted by minY + + + if (xMargin < yMargin) node.children.sort(compareMinX); + } // total margin of all possible split distributions where each node is at least m full + + }, { + key: "_allDistMargin", + value: function _allDistMargin(node, m, M, compare) { + node.children.sort(compare); + var toBBox = this.toBBox; + var leftBBox = distBBox(node, 0, m, toBBox); + var rightBBox = distBBox(node, M - m, M, toBBox); + var margin = bboxMargin(leftBBox) + bboxMargin(rightBBox); + + for (var i = m; i < M - m; i++) { + var child = node.children[i]; + extend$1(leftBBox, node.leaf ? toBBox(child) : child); + margin += bboxMargin(leftBBox); + } + + for (var _i = M - m - 1; _i >= m; _i--) { + var _child = node.children[_i]; + extend$1(rightBBox, node.leaf ? toBBox(_child) : _child); + margin += bboxMargin(rightBBox); + } + + return margin; + } + }, { + key: "_adjustParentBBoxes", + value: function _adjustParentBBoxes(bbox, path, level) { + // adjust bboxes along the given tree path + for (var i = level; i >= 0; i--) { + extend$1(path[i], bbox); + } + } + }, { + key: "_condense", + value: function _condense(path) { + // go through the path, removing empty nodes and updating bboxes + for (var i = path.length - 1, siblings; i >= 0; i--) { + if (path[i].children.length === 0) { + if (i > 0) { + siblings = path[i - 1].children; + siblings.splice(siblings.indexOf(path[i]), 1); + } else this.clear(); + } else calcBBox(path[i], this.toBBox); + } + } + }]); + + return RBush; + }(); + + function findItem(item, items, equalsFn) { + if (!equalsFn) return items.indexOf(item); + + for (var i = 0; i < items.length; i++) { + if (equalsFn(item, items[i])) return i; + } + + return -1; + } // calculate node's bbox from bboxes of its children + + + function calcBBox(node, toBBox) { + distBBox(node, 0, node.children.length, toBBox, node); + } // min bounding rectangle of node children from k to p-1 + + + function distBBox(node, k, p, toBBox, destNode) { + if (!destNode) destNode = createNode(null); + destNode.minX = Infinity; + destNode.minY = Infinity; + destNode.maxX = -Infinity; + destNode.maxY = -Infinity; + + for (var i = k; i < p; i++) { + var child = node.children[i]; + extend$1(destNode, node.leaf ? toBBox(child) : child); + } + + return destNode; + } + + function extend$1(a, b) { + a.minX = Math.min(a.minX, b.minX); + a.minY = Math.min(a.minY, b.minY); + a.maxX = Math.max(a.maxX, b.maxX); + a.maxY = Math.max(a.maxY, b.maxY); + return a; + } + + function compareNodeMinX(a, b) { + return a.minX - b.minX; + } + + function compareNodeMinY(a, b) { + return a.minY - b.minY; + } + + function bboxArea(a) { + return (a.maxX - a.minX) * (a.maxY - a.minY); + } + + function bboxMargin(a) { + return a.maxX - a.minX + (a.maxY - a.minY); + } + + function enlargedArea(a, b) { + return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY)); + } + + function intersectionArea(a, b) { + var minX = Math.max(a.minX, b.minX); + var minY = Math.max(a.minY, b.minY); + var maxX = Math.min(a.maxX, b.maxX); + var maxY = Math.min(a.maxY, b.maxY); + return Math.max(0, maxX - minX) * Math.max(0, maxY - minY); + } + + function contains(a, b) { + return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY; + } + + function intersects(a, b) { + return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY; + } + + function createNode(children) { + return { + children: children, + height: 1, + leaf: true, + minX: Infinity, + minY: Infinity, + maxX: -Infinity, + maxY: -Infinity + }; + } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other; + // combines selection algorithm with binary divide & conquer approach + + + function multiSelect(arr, left, right, n, compare) { + var stack = [left, right]; + + while (stack.length) { + right = stack.pop(); + left = stack.pop(); + if (right - left <= n) continue; + var mid = left + Math.ceil((right - left) / n / 2) * n; + quickselect(arr, mid, left, right, compare); + stack.push(left, mid, mid, right); + } + } + + function responseText(response) { + if (!response.ok) throw new Error(response.status + " " + response.statusText); + return response.text(); + } + + function d3_text (input, init) { + return fetch(input, init).then(responseText); + } + + function responseJson(response) { + if (!response.ok) throw new Error(response.status + " " + response.statusText); + if (response.status === 204 || response.status === 205) return; + return response.json(); + } + + function d3_json (input, init) { + return fetch(input, init).then(responseJson); + } + + function parser(type) { + return function (input, init) { + return d3_text(input, init).then(function (text) { + return new DOMParser().parseFromString(text, type); + }); + }; + } + + var d3_xml = parser("application/xml"); + var svg = parser("image/svg+xml"); + + var tiler$6 = utilTiler(); + var dispatch$7 = dispatch$8('loaded'); + var _tileZoom$3 = 14; + var _krUrlRoot = 'https://www.keepright.at'; + var _krData = { + errorTypes: {}, + localizeStrings: {} + }; // This gets reassigned if reset + + var _cache$2; + + var _krRuleset = [// no 20 - multiple node on same spot - these are mostly boundaries overlapping roads + 30, 40, 50, 60, 70, 90, 100, 110, 120, 130, 150, 160, 170, 180, 190, 191, 192, 193, 194, 195, 196, 197, 198, 200, 201, 202, 203, 204, 205, 206, 207, 208, 210, 220, 230, 231, 232, 270, 280, 281, 282, 283, 284, 285, 290, 291, 292, 293, 294, 295, 296, 297, 298, 300, 310, 311, 312, 313, 320, 350, 360, 370, 380, 390, 400, 401, 402, 410, 411, 412, 413]; + + function abortRequest$6(controller) { + if (controller) { + controller.abort(); + } + } + + function abortUnwantedRequests$3(cache, tiles) { + Object.keys(cache.inflightTile).forEach(function (k) { + var wanted = tiles.find(function (tile) { + return k === tile.id; + }); + + if (!wanted) { + abortRequest$6(cache.inflightTile[k]); + delete cache.inflightTile[k]; + } + }); + } + + function encodeIssueRtree$2(d) { + return { + minX: d.loc[0], + minY: d.loc[1], + maxX: d.loc[0], + maxY: d.loc[1], + data: d + }; + } // Replace or remove QAItem from rtree + + + function updateRtree$3(item, replace) { + _cache$2.rtree.remove(item, function (a, b) { + return a.data.id === b.data.id; + }); + + if (replace) { + _cache$2.rtree.insert(item); + } + } + + function tokenReplacements(d) { + if (!(d instanceof QAItem)) return; + var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/); + var replacements = {}; + var issueTemplate = _krData.errorTypes[d.whichType]; + + if (!issueTemplate) { + /* eslint-disable no-console */ + console.log('No Template: ', d.whichType); + console.log(' ', d.description); + /* eslint-enable no-console */ + + return; + } // some descriptions are just fixed text + + + if (!issueTemplate.regex) return; // regex pattern should match description with variable details captured + + var errorRegex = new RegExp(issueTemplate.regex, 'i'); + var errorMatch = errorRegex.exec(d.description); + + if (!errorMatch) { + /* eslint-disable no-console */ + console.log('Unmatched: ', d.whichType); + console.log(' ', d.description); + console.log(' ', errorRegex); + /* eslint-enable no-console */ + + return; + } + + for (var i = 1; i < errorMatch.length; i++) { + // skip first + var capture = errorMatch[i]; + var idType = void 0; + idType = 'IDs' in issueTemplate ? issueTemplate.IDs[i - 1] : ''; + + if (idType && capture) { + // link IDs if present in the capture + capture = parseError(capture, idType); + } else if (htmlRegex.test(capture)) { + // escape any html in non-IDs + capture = '\\' + capture + '\\'; + } else { + var compare = capture.toLowerCase(); + + if (_krData.localizeStrings[compare]) { + // some replacement strings can be localized + capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]); + } + } + + replacements['var' + i] = capture; + } + + return replacements; + } + + function parseError(capture, idType) { + var compare = capture.toLowerCase(); + + if (_krData.localizeStrings[compare]) { + // some replacement strings can be localized + capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]); + } + + switch (idType) { + // link a string like "this node" + case 'this': + capture = linkErrorObject(capture); + break; + + case 'url': + capture = linkURL(capture); + break; + // link an entity ID + + case 'n': + case 'w': + case 'r': + capture = linkEntity(idType + capture); + break; + // some errors have more complex ID lists/variance + + case '20': + capture = parse20(capture); + break; + + case '211': + capture = parse211(capture); + break; + + case '231': + capture = parse231(capture); + break; + + case '294': + capture = parse294(capture); + break; + + case '370': + capture = parse370(capture); + break; + } + + return capture; + + function linkErrorObject(d) { + return "".concat(d, ""); + } + + function linkEntity(d) { + return "".concat(d, ""); + } + + function linkURL(d) { + return "").concat(d, ""); + } // arbitrary node list of form: #ID, #ID, #ID... + + + function parse211(capture) { + var newList = []; + var items = capture.split(', '); + items.forEach(function (item) { + // ID has # at the front + var id = linkEntity('n' + item.slice(1)); + newList.push(id); + }); + return newList.join(', '); + } // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)... + + + function parse231(capture) { + var newList = []; // unfortunately 'layer' can itself contain commas, so we split on '),' + + var items = capture.split('),'); + items.forEach(function (item) { + var match = item.match(/\#(\d+)\((.+)\)?/); + + if (match !== null && match.length > 2) { + newList.push(linkEntity('w' + match[1]) + ' ' + _t('QA.keepRight.errorTypes.231.layer', { + layer: match[2] + })); + } + }); + return newList.join(', '); + } // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID... + + + function parse294(capture) { + var newList = []; + var items = capture.split(','); + items.forEach(function (item) { + // item of form "from/to node/relation #ID" + item = item.split(' '); // to/from role is more clear in quotes + + var role = "\"".concat(item[0], "\""); // first letter of node/relation provides the type + + var idType = item[1].slice(0, 1); // ID has # at the front + + var id = item[2].slice(1); + id = linkEntity(idType + id); + newList.push("".concat(role, " ").concat(item[1], " ").concat(id)); + }); + return newList.join(', '); + } // may or may not include the string "(including the name 'name')" + + + function parse370(capture) { + if (!capture) return ''; + var match = capture.match(/\(including the name (\'.+\')\)/); + + if (match && match.length) { + return _t('QA.keepRight.errorTypes.370.including_the_name', { + name: match[1] + }); + } + + return ''; + } // arbitrary node list of form: #ID,#ID,#ID... + + + function parse20(capture) { + var newList = []; + var items = capture.split(','); + items.forEach(function (item) { + // ID has # at the front + var id = linkEntity('n' + item.slice(1)); + newList.push(id); + }); + return newList.join(', '); + } + } + + var serviceKeepRight = { + title: 'keepRight', + init: function init() { + _mainFileFetcher.get('keepRight').then(function (d) { + return _krData = d; + }); + + if (!_cache$2) { + this.reset(); + } + + this.event = utilRebind(this, dispatch$7, 'on'); + }, + reset: function reset() { + if (_cache$2) { + Object.values(_cache$2.inflightTile).forEach(abortRequest$6); + } + + _cache$2 = { + data: {}, + loadedTile: {}, + inflightTile: {}, + inflightPost: {}, + closed: {}, + rtree: new RBush() + }; + }, + // KeepRight API: http://osm.mueschelsoft.de/keepright/interfacing.php + loadIssues: function loadIssues(projection) { + var _this = this; + + var options = { + format: 'geojson', + ch: _krRuleset + }; // determine the needed tiles to cover the view + + var tiles = tiler$6.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection); // abort inflight requests that are no longer needed + + abortUnwantedRequests$3(_cache$2, tiles); // issue new requests.. + + tiles.forEach(function (tile) { + if (_cache$2.loadedTile[tile.id] || _cache$2.inflightTile[tile.id]) return; + + var _tile$extent$rectangl = tile.extent.rectangle(), + _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4), + left = _tile$extent$rectangl2[0], + top = _tile$extent$rectangl2[1], + right = _tile$extent$rectangl2[2], + bottom = _tile$extent$rectangl2[3]; + + var params = Object.assign({}, options, { + left: left, + bottom: bottom, + right: right, + top: top + }); + var url = "".concat(_krUrlRoot, "/export.php?") + utilQsString(params); + var controller = new AbortController(); + _cache$2.inflightTile[tile.id] = controller; + d3_json(url, { + signal: controller.signal + }).then(function (data) { + delete _cache$2.inflightTile[tile.id]; + _cache$2.loadedTile[tile.id] = true; + + if (!data || !data.features || !data.features.length) { + throw new Error('No Data'); + } + + data.features.forEach(function (feature) { + var _feature$properties = feature.properties, + itemType = _feature$properties.error_type, + id = _feature$properties.error_id, + _feature$properties$c = _feature$properties.comment, + comment = _feature$properties$c === void 0 ? null : _feature$properties$c, + objectId = _feature$properties.object_id, + objectType = _feature$properties.object_type, + schema = _feature$properties.schema, + title = _feature$properties.title; + var loc = feature.geometry.coordinates, + _feature$properties$d = feature.properties.description, + description = _feature$properties$d === void 0 ? '' : _feature$properties$d; // if there is a parent, save its error type e.g.: + // Error 191 = "highway-highway" + // Error 190 = "intersections without junctions" (parent) + + var issueTemplate = _krData.errorTypes[itemType]; + var parentIssueType = (Math.floor(itemType / 10) * 10).toString(); // try to handle error type directly, fallback to parent error type. + + var whichType = issueTemplate ? itemType : parentIssueType; + var whichTemplate = _krData.errorTypes[whichType]; // Rewrite a few of the errors at this point.. + // This is done to make them easier to linkify and translate. + + switch (whichType) { + case '170': + description = "This feature has a FIXME tag: ".concat(description); + break; + + case '292': + case '293': + description = description.replace('A turn-', 'This turn-'); + break; + + case '294': + case '295': + case '296': + case '297': + case '298': + description = "This turn-restriction~".concat(description); + break; + + case '300': + description = 'This highway is missing a maxspeed tag'; + break; + + case '411': + case '412': + case '413': + description = "This feature~".concat(description); + break; + } // move markers slightly so it doesn't obscure the geometry, + // then move markers away from other coincident markers + + + var coincident = false; + + do { + // first time, move marker up. after that, move marker right. + var delta = coincident ? [0.00001, 0] : [0, 0.00001]; + loc = geoVecAdd(loc, delta); + var bbox = geoExtent(loc).bbox(); + coincident = _cache$2.rtree.search(bbox).length; + } while (coincident); + + var d = new QAItem(loc, _this, itemType, id, { + comment: comment, + description: description, + whichType: whichType, + parentIssueType: parentIssueType, + severity: whichTemplate.severity || 'error', + objectId: objectId, + objectType: objectType, + schema: schema, + title: title + }); + d.replacements = tokenReplacements(d); + _cache$2.data[id] = d; + + _cache$2.rtree.insert(encodeIssueRtree$2(d)); + }); + dispatch$7.call('loaded'); + })["catch"](function () { + delete _cache$2.inflightTile[tile.id]; + _cache$2.loadedTile[tile.id] = true; + }); + }); + }, + postUpdate: function postUpdate(d, callback) { + var _this2 = this; + + if (_cache$2.inflightPost[d.id]) { + return callback({ + message: 'Error update already inflight', + status: -2 + }, d); + } + + var params = { + schema: d.schema, + id: d.id + }; + + if (d.newStatus) { + params.st = d.newStatus; + } + + if (d.newComment !== undefined) { + params.co = d.newComment; + } // NOTE: This throws a CORS err, but it seems successful. + // We don't care too much about the response, so this is fine. + + + var url = "".concat(_krUrlRoot, "/comment.php?") + utilQsString(params); + var controller = new AbortController(); + _cache$2.inflightPost[d.id] = controller; // Since this is expected to throw an error just continue as if it worked + // (worst case scenario the request truly fails and issue will show up if iD restarts) + + d3_json(url, { + signal: controller.signal + })["finally"](function () { + delete _cache$2.inflightPost[d.id]; + + if (d.newStatus === 'ignore') { + // ignore permanently (false positive) + _this2.removeItem(d); + } else if (d.newStatus === 'ignore_t') { + // ignore temporarily (error fixed) + _this2.removeItem(d); + + _cache$2.closed["".concat(d.schema, ":").concat(d.id)] = true; + } else { + d = _this2.replaceItem(d.update({ + comment: d.newComment, + newComment: undefined, + newState: undefined + })); + } + + if (callback) callback(null, d); + }); + }, + // Get all cached QAItems covering the viewport + getItems: function getItems(projection) { + var viewport = projection.clipExtent(); + var min = [viewport[0][0], viewport[1][1]]; + var max = [viewport[1][0], viewport[0][1]]; + var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); + return _cache$2.rtree.search(bbox).map(function (d) { + return d.data; + }); + }, + // Get a QAItem from cache + // NOTE: Don't change method name until UI v3 is merged + getError: function getError(id) { + return _cache$2.data[id]; + }, + // Replace a single QAItem in the cache + replaceItem: function replaceItem(item) { + if (!(item instanceof QAItem) || !item.id) return; + _cache$2.data[item.id] = item; + updateRtree$3(encodeIssueRtree$2(item), true); // true = replace + + return item; + }, + // Remove a single QAItem from the cache + removeItem: function removeItem(item) { + if (!(item instanceof QAItem) || !item.id) return; + delete _cache$2.data[item.id]; + updateRtree$3(encodeIssueRtree$2(item), false); // false = remove + }, + issueURL: function issueURL(item) { + return "".concat(_krUrlRoot, "/report_map.php?schema=").concat(item.schema, "&error=").concat(item.id); + }, + // Get an array of issues closed during this session. + // Used to populate `closed:keepright` changeset tag + getClosedIDs: function getClosedIDs() { + return Object.keys(_cache$2.closed).sort(); + } + }; + + var tiler$5 = utilTiler(); + var dispatch$6 = dispatch$8('loaded'); + var _tileZoom$2 = 14; + var _impOsmUrls = { + ow: 'https://grab.community.improve-osm.org/directionOfFlowService', + mr: 'https://grab.community.improve-osm.org/missingGeoService', + tr: 'https://grab.community.improve-osm.org/turnRestrictionService' + }; + var _impOsmData = { + icons: {} + }; // This gets reassigned if reset + + var _cache$1; + + function abortRequest$5(i) { + Object.values(i).forEach(function (controller) { + if (controller) { + controller.abort(); + } + }); + } + + function abortUnwantedRequests$2(cache, tiles) { + Object.keys(cache.inflightTile).forEach(function (k) { + var wanted = tiles.find(function (tile) { + return k === tile.id; + }); + + if (!wanted) { + abortRequest$5(cache.inflightTile[k]); + delete cache.inflightTile[k]; + } + }); + } + + function encodeIssueRtree$1(d) { + return { + minX: d.loc[0], + minY: d.loc[1], + maxX: d.loc[0], + maxY: d.loc[1], + data: d + }; + } // Replace or remove QAItem from rtree + + + function updateRtree$2(item, replace) { + _cache$1.rtree.remove(item, function (a, b) { + return a.data.id === b.data.id; + }); + + if (replace) { + _cache$1.rtree.insert(item); + } + } + + function linkErrorObject(d) { + return "".concat(d, ""); + } + + function linkEntity(d) { + return "".concat(d, ""); + } + + function pointAverage(points) { + if (points.length) { + var sum = points.reduce(function (acc, point) { + return geoVecAdd(acc, [point.lon, point.lat]); + }, [0, 0]); + return geoVecScale(sum, 1 / points.length); + } else { + return [0, 0]; + } + } + + function relativeBearing(p1, p2) { + var angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat); + + if (angle < 0) { + angle += 2 * Math.PI; + } // Return degrees + + + return angle * 180 / Math.PI; + } // Assuming range [0,360) + + + function cardinalDirection(bearing) { + var dir = 45 * Math.round(bearing / 45); + var compass = { + 0: 'north', + 45: 'northeast', + 90: 'east', + 135: 'southeast', + 180: 'south', + 225: 'southwest', + 270: 'west', + 315: 'northwest', + 360: 'north' + }; + return _t("QA.improveOSM.directions.".concat(compass[dir])); + } // Errors shouldn't obscure each other + + + function preventCoincident$1(loc, bumpUp) { + var coincident = false; + + do { + // first time, move marker up. after that, move marker right. + var delta = coincident ? [0.00001, 0] : bumpUp ? [0, 0.00001] : [0, 0]; + loc = geoVecAdd(loc, delta); + var bbox = geoExtent(loc).bbox(); + coincident = _cache$1.rtree.search(bbox).length; + } while (coincident); + + return loc; + } + + var serviceImproveOSM = { + title: 'improveOSM', + init: function init() { + _mainFileFetcher.get('qa_data').then(function (d) { + return _impOsmData = d.improveOSM; + }); + + if (!_cache$1) { + this.reset(); + } + + this.event = utilRebind(this, dispatch$6, 'on'); + }, + reset: function reset() { + if (_cache$1) { + Object.values(_cache$1.inflightTile).forEach(abortRequest$5); + } + + _cache$1 = { + data: {}, + loadedTile: {}, + inflightTile: {}, + inflightPost: {}, + closed: {}, + rtree: new RBush() + }; + }, + loadIssues: function loadIssues(projection) { + var _this = this; + + var options = { + client: 'iD', + status: 'OPEN', + zoom: '19' // Use a high zoom so that clusters aren't returned + + }; // determine the needed tiles to cover the view + + var tiles = tiler$5.zoomExtent([_tileZoom$2, _tileZoom$2]).getTiles(projection); // abort inflight requests that are no longer needed + + abortUnwantedRequests$2(_cache$1, tiles); // issue new requests.. + + tiles.forEach(function (tile) { + if (_cache$1.loadedTile[tile.id] || _cache$1.inflightTile[tile.id]) return; + + var _tile$extent$rectangl = tile.extent.rectangle(), + _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4), + east = _tile$extent$rectangl2[0], + north = _tile$extent$rectangl2[1], + west = _tile$extent$rectangl2[2], + south = _tile$extent$rectangl2[3]; + + var params = Object.assign({}, options, { + east: east, + south: south, + west: west, + north: north + }); // 3 separate requests to store for each tile + + var requests = {}; + Object.keys(_impOsmUrls).forEach(function (k) { + // We exclude WATER from missing geometry as it doesn't seem useful + // We use most confident one-way and turn restrictions only, still have false positives + var kParams = Object.assign({}, params, k === 'mr' ? { + type: 'PARKING,ROAD,BOTH,PATH' + } : { + confidenceLevel: 'C1' + }); + var url = "".concat(_impOsmUrls[k], "/search?") + utilQsString(kParams); + var controller = new AbortController(); + requests[k] = controller; + d3_json(url, { + signal: controller.signal + }).then(function (data) { + delete _cache$1.inflightTile[tile.id][k]; + + if (!Object.keys(_cache$1.inflightTile[tile.id]).length) { + delete _cache$1.inflightTile[tile.id]; + _cache$1.loadedTile[tile.id] = true; + } // Road segments at high zoom == oneways + + + if (data.roadSegments) { + data.roadSegments.forEach(function (feature) { + // Position error at the approximate middle of the segment + var points = feature.points, + wayId = feature.wayId, + fromNodeId = feature.fromNodeId, + toNodeId = feature.toNodeId; + var itemId = "".concat(wayId).concat(fromNodeId).concat(toNodeId); + var mid = points.length / 2; + var loc; // Even number of points, find midpoint of the middle two + // Odd number of points, use position of very middle point + + if (mid % 1 === 0) { + loc = pointAverage([points[mid - 1], points[mid]]); + } else { + mid = points[Math.floor(mid)]; + loc = [mid.lon, mid.lat]; + } // One-ways can land on same segment in opposite direction + + + loc = preventCoincident$1(loc, false); + var d = new QAItem(loc, _this, k, itemId, { + issueKey: k, + // used as a category + identifier: { + // used to post changes + wayId: wayId, + fromNodeId: fromNodeId, + toNodeId: toNodeId + }, + objectId: wayId, + objectType: 'way' + }); // Variables used in the description + + d.replacements = { + percentage: feature.percentOfTrips, + num_trips: feature.numberOfTrips, + highway: linkErrorObject(_t('QA.keepRight.error_parts.highway')), + from_node: linkEntity('n' + feature.fromNodeId), + to_node: linkEntity('n' + feature.toNodeId) + }; + _cache$1.data[d.id] = d; + + _cache$1.rtree.insert(encodeIssueRtree$1(d)); + }); + } // Tiles at high zoom == missing roads + + + if (data.tiles) { + data.tiles.forEach(function (feature) { + var type = feature.type, + x = feature.x, + y = feature.y, + numberOfTrips = feature.numberOfTrips; + var geoType = type.toLowerCase(); + var itemId = "".concat(geoType).concat(x).concat(y).concat(numberOfTrips); // Average of recorded points should land on the missing geometry + // Missing geometry could happen to land on another error + + var loc = pointAverage(feature.points); + loc = preventCoincident$1(loc, false); + var d = new QAItem(loc, _this, "".concat(k, "-").concat(geoType), itemId, { + issueKey: k, + identifier: { + x: x, + y: y + } + }); + d.replacements = { + num_trips: numberOfTrips, + geometry_type: _t("QA.improveOSM.geometry_types.".concat(geoType)) + }; // -1 trips indicates data came from a 3rd party + + if (numberOfTrips === -1) { + d.desc = _t('QA.improveOSM.error_types.mr.description_alt', d.replacements); + } + + _cache$1.data[d.id] = d; + + _cache$1.rtree.insert(encodeIssueRtree$1(d)); + }); + } // Entities at high zoom == turn restrictions + + + if (data.entities) { + data.entities.forEach(function (feature) { + var point = feature.point, + id = feature.id, + segments = feature.segments, + numberOfPasses = feature.numberOfPasses, + turnType = feature.turnType; + var itemId = "".concat(id.replace(/[,:+#]/g, '_')); // Turn restrictions could be missing at same junction + // We also want to bump the error up so node is accessible + + var loc = preventCoincident$1([point.lon, point.lat], true); // Elements are presented in a strange way + + var ids = id.split(','); + var from_way = ids[0]; + var via_node = ids[3]; + var to_way = ids[2].split(':')[1]; + var d = new QAItem(loc, _this, k, itemId, { + issueKey: k, + identifier: id, + objectId: via_node, + objectType: 'node' + }); // Travel direction along from_way clarifies the turn restriction + + var _segments$0$points = _slicedToArray(segments[0].points, 2), + p1 = _segments$0$points[0], + p2 = _segments$0$points[1]; + + var dir_of_travel = cardinalDirection(relativeBearing(p1, p2)); // Variables used in the description + + d.replacements = { + num_passed: numberOfPasses, + num_trips: segments[0].numberOfTrips, + turn_restriction: turnType.toLowerCase(), + from_way: linkEntity('w' + from_way), + to_way: linkEntity('w' + to_way), + travel_direction: dir_of_travel, + junction: linkErrorObject(_t('QA.keepRight.error_parts.this_node')) + }; + _cache$1.data[d.id] = d; + + _cache$1.rtree.insert(encodeIssueRtree$1(d)); + + dispatch$6.call('loaded'); + }); + } + })["catch"](function () { + delete _cache$1.inflightTile[tile.id][k]; + + if (!Object.keys(_cache$1.inflightTile[tile.id]).length) { + delete _cache$1.inflightTile[tile.id]; + _cache$1.loadedTile[tile.id] = true; + } + }); + }); + _cache$1.inflightTile[tile.id] = requests; + }); + }, + getComments: function getComments(item) { + var _this2 = this; + + // If comments already retrieved no need to do so again + if (item.comments) { + return Promise.resolve(item); + } + + var key = item.issueKey; + var qParams = {}; + + if (key === 'ow') { + qParams = item.identifier; + } else if (key === 'mr') { + qParams.tileX = item.identifier.x; + qParams.tileY = item.identifier.y; + } else if (key === 'tr') { + qParams.targetId = item.identifier; + } + + var url = "".concat(_impOsmUrls[key], "/retrieveComments?") + utilQsString(qParams); + + var cacheComments = function cacheComments(data) { + // Assign directly for immediate use afterwards + // comments are served newest to oldest + item.comments = data.comments ? data.comments.reverse() : []; + + _this2.replaceItem(item); + }; + + return d3_json(url).then(cacheComments).then(function () { + return item; + }); + }, + postUpdate: function postUpdate(d, callback) { + if (!serviceOsm.authenticated()) { + // Username required in payload + return callback({ + message: 'Not Authenticated', + status: -3 + }, d); + } + + if (_cache$1.inflightPost[d.id]) { + return callback({ + message: 'Error update already inflight', + status: -2 + }, d); + } // Payload can only be sent once username is established + + + serviceOsm.userDetails(sendPayload.bind(this)); + + function sendPayload(err, user) { + var _this3 = this; + + if (err) { + return callback(err, d); + } + + var key = d.issueKey; + var url = "".concat(_impOsmUrls[key], "/comment"); + var payload = { + username: user.display_name, + targetIds: [d.identifier] + }; + + if (d.newStatus) { + payload.status = d.newStatus; + payload.text = 'status changed'; + } // Comment take place of default text + + + if (d.newComment) { + payload.text = d.newComment; + } + + var controller = new AbortController(); + _cache$1.inflightPost[d.id] = controller; + var options = { + method: 'POST', + signal: controller.signal, + body: JSON.stringify(payload) + }; + d3_json(url, options).then(function () { + delete _cache$1.inflightPost[d.id]; // Just a comment, update error in cache + + if (!d.newStatus) { + var now = new Date(); + var comments = d.comments ? d.comments : []; + comments.push({ + username: payload.username, + text: payload.text, + timestamp: now.getTime() / 1000 + }); + + _this3.replaceItem(d.update({ + comments: comments, + newComment: undefined + })); + } else { + _this3.removeItem(d); + + if (d.newStatus === 'SOLVED') { + // Keep track of the number of issues closed per type to tag the changeset + if (!(d.issueKey in _cache$1.closed)) { + _cache$1.closed[d.issueKey] = 0; + } + + _cache$1.closed[d.issueKey] += 1; + } + } + + if (callback) callback(null, d); + })["catch"](function (err) { + delete _cache$1.inflightPost[d.id]; + if (callback) callback(err.message); + }); + } + }, + // Get all cached QAItems covering the viewport + getItems: function getItems(projection) { + var viewport = projection.clipExtent(); + var min = [viewport[0][0], viewport[1][1]]; + var max = [viewport[1][0], viewport[0][1]]; + var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); + return _cache$1.rtree.search(bbox).map(function (d) { + return d.data; + }); + }, + // Get a QAItem from cache + // NOTE: Don't change method name until UI v3 is merged + getError: function getError(id) { + return _cache$1.data[id]; + }, + // get the name of the icon to display for this item + getIcon: function getIcon(itemType) { + return _impOsmData.icons[itemType]; + }, + // Replace a single QAItem in the cache + replaceItem: function replaceItem(issue) { + if (!(issue instanceof QAItem) || !issue.id) return; + _cache$1.data[issue.id] = issue; + updateRtree$2(encodeIssueRtree$1(issue), true); // true = replace + + return issue; + }, + // Remove a single QAItem from the cache + removeItem: function removeItem(issue) { + if (!(issue instanceof QAItem) || !issue.id) return; + delete _cache$1.data[issue.id]; + updateRtree$2(encodeIssueRtree$1(issue), false); // false = remove + }, + // Used to populate `closed:improveosm:*` changeset tags + getClosedCounts: function getClosedCounts() { + return _cache$1.closed; + } + }; + + var defaults$5 = {exports: {}}; + + function getDefaults$1() { + return { + baseUrl: null, + breaks: false, + gfm: true, + headerIds: true, + headerPrefix: '', + highlight: null, + langPrefix: 'language-', + mangle: true, + pedantic: false, + renderer: null, + sanitize: false, + sanitizer: null, + silent: false, + smartLists: false, + smartypants: false, + tokenizer: null, + walkTokens: null, + xhtml: false + }; + } + + function changeDefaults$1(newDefaults) { + defaults$5.exports.defaults = newDefaults; + } + + defaults$5.exports = { + defaults: getDefaults$1(), + getDefaults: getDefaults$1, + changeDefaults: changeDefaults$1 + }; + + var escapeTest = /[&<>"']/; + var escapeReplace = /[&<>"']/g; + var escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/; + var escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g; + var escapeReplacements = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + + var getEscapeReplacement = function getEscapeReplacement(ch) { + return escapeReplacements[ch]; + }; + + function escape$3(html, encode) { + if (encode) { + if (escapeTest.test(html)) { + return html.replace(escapeReplace, getEscapeReplacement); + } + } else { + if (escapeTestNoEncode.test(html)) { + return html.replace(escapeReplaceNoEncode, getEscapeReplacement); + } + } + + return html; + } + + var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig; + + function unescape$2(html) { + // explicitly match decimal, hex, and named HTML entities + return html.replace(unescapeTest, function (_, n) { + n = n.toLowerCase(); + if (n === 'colon') return ':'; + + if (n.charAt(0) === '#') { + return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1)); + } + + return ''; + }); + } + + var caret = /(^|[^\[])\^/g; + + function edit$1(regex, opt) { + regex = regex.source || regex; + opt = opt || ''; + var obj = { + replace: function replace(name, val) { + val = val.source || val; + val = val.replace(caret, '$1'); + regex = regex.replace(name, val); + return obj; + }, + getRegex: function getRegex() { + return new RegExp(regex, opt); + } + }; + return obj; + } + + var nonWordAndColonTest = /[^\w:]/g; + var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; + + function cleanUrl$1(sanitize, base, href) { + if (sanitize) { + var prot; + + try { + prot = decodeURIComponent(unescape$2(href)).replace(nonWordAndColonTest, '').toLowerCase(); + } catch (e) { + return null; + } + + if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) { + return null; + } + } + + if (base && !originIndependentUrl.test(href)) { + href = resolveUrl$2(base, href); + } + + try { + href = encodeURI(href).replace(/%25/g, '%'); + } catch (e) { + return null; + } + + return href; + } + + var baseUrls = {}; + var justDomain = /^[^:]+:\/*[^/]*$/; + var protocol = /^([^:]+:)[\s\S]*$/; + var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/; + + function resolveUrl$2(base, href) { + if (!baseUrls[' ' + base]) { + // we can ignore everything in base after the last slash of its path component, + // but we might need to add _that_ + // https://tools.ietf.org/html/rfc3986#section-3 + if (justDomain.test(base)) { + baseUrls[' ' + base] = base + '/'; + } else { + baseUrls[' ' + base] = rtrim$1(base, '/', true); + } + } + + base = baseUrls[' ' + base]; + var relativeBase = base.indexOf(':') === -1; + + if (href.substring(0, 2) === '//') { + if (relativeBase) { + return href; + } + + return base.replace(protocol, '$1') + href; + } else if (href.charAt(0) === '/') { + if (relativeBase) { + return href; + } + + return base.replace(domain, '$1') + href; + } else { + return base + href; + } + } + + var noopTest$1 = { + exec: function noopTest() {} + }; + + function merge$2(obj) { + var i = 1, + target, + key; + + for (; i < arguments.length; i++) { + target = arguments[i]; + + for (key in target) { + if (Object.prototype.hasOwnProperty.call(target, key)) { + obj[key] = target[key]; + } + } + } + + return obj; + } + + function splitCells$1(tableRow, count) { + // ensure that every cell-delimiting pipe has a space + // before it to distinguish it from an escaped pipe + var row = tableRow.replace(/\|/g, function (match, offset, str) { + var escaped = false, + curr = offset; + + while (--curr >= 0 && str[curr] === '\\') { + escaped = !escaped; + } + + if (escaped) { + // odd number of slashes means | is escaped + // so we leave it alone + return '|'; + } else { + // add space before unescaped | + return ' |'; + } + }), + cells = row.split(/ \|/); + var i = 0; + + if (cells.length > count) { + cells.splice(count); + } else { + while (cells.length < count) { + cells.push(''); + } + } + + for (; i < cells.length; i++) { + // leading or trailing whitespace is ignored per the gfm spec + cells[i] = cells[i].trim().replace(/\\\|/g, '|'); + } + + return cells; + } // Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). + // /c*$/ is vulnerable to REDOS. + // invert: Remove suffix of non-c chars instead. Default falsey. + + + function rtrim$1(str, c, invert) { + var l = str.length; + + if (l === 0) { + return ''; + } // Length of suffix matching the invert condition. + + + var suffLen = 0; // Step left until we fail to match the invert condition. + + while (suffLen < l) { + var currChar = str.charAt(l - suffLen - 1); + + if (currChar === c && !invert) { + suffLen++; + } else if (currChar !== c && invert) { + suffLen++; + } else { + break; + } + } + + return str.substr(0, l - suffLen); + } + + function findClosingBracket$1(str, b) { + if (str.indexOf(b[1]) === -1) { + return -1; + } + + var l = str.length; + var level = 0, + i = 0; + + for (; i < l; i++) { + if (str[i] === '\\') { + i++; + } else if (str[i] === b[0]) { + level++; + } else if (str[i] === b[1]) { + level--; + + if (level < 0) { + return i; + } + } + } + + return -1; + } + + function checkSanitizeDeprecation$1(opt) { + if (opt && opt.sanitize && !opt.silent) { + console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); + } + } // copied from https://stackoverflow.com/a/5450113/806777 + + + function repeatString$1(pattern, count) { + if (count < 1) { + return ''; + } + + var result = ''; + + while (count > 1) { + if (count & 1) { + result += pattern; + } + + count >>= 1; + pattern += pattern; + } + + return result + pattern; + } + + var helpers = { + escape: escape$3, + unescape: unescape$2, + edit: edit$1, + cleanUrl: cleanUrl$1, + resolveUrl: resolveUrl$2, + noopTest: noopTest$1, + merge: merge$2, + splitCells: splitCells$1, + rtrim: rtrim$1, + findClosingBracket: findClosingBracket$1, + checkSanitizeDeprecation: checkSanitizeDeprecation$1, + repeatString: repeatString$1 + }; + + var defaults$4 = defaults$5.exports.defaults; + var rtrim = helpers.rtrim, + splitCells = helpers.splitCells, + _escape = helpers.escape, + findClosingBracket = helpers.findClosingBracket; + + function outputLink(cap, link, raw) { + var href = link.href; + var title = link.title ? _escape(link.title) : null; + var text = cap[1].replace(/\\([\[\]])/g, '$1'); + + if (cap[0].charAt(0) !== '!') { + return { + type: 'link', + raw: raw, + href: href, + title: title, + text: text + }; + } else { + return { + type: 'image', + raw: raw, + href: href, + title: title, + text: _escape(text) + }; + } + } + + function indentCodeCompensation(raw, text) { + var matchIndentToCode = raw.match(/^(\s+)(?:```)/); + + if (matchIndentToCode === null) { + return text; + } + + var indentToCode = matchIndentToCode[1]; + return text.split('\n').map(function (node) { + var matchIndentInNode = node.match(/^\s+/); + + if (matchIndentInNode === null) { + return node; + } + + var _matchIndentInNode = _slicedToArray(matchIndentInNode, 1), + indentInNode = _matchIndentInNode[0]; + + if (indentInNode.length >= indentToCode.length) { + return node.slice(indentToCode.length); + } + + return node; + }).join('\n'); + } + /** + * Tokenizer + */ + + + var Tokenizer_1 = /*#__PURE__*/function () { + function Tokenizer(options) { + _classCallCheck$1(this, Tokenizer); + + this.options = options || defaults$4; + } + + _createClass$1(Tokenizer, [{ + key: "space", + value: function space(src) { + var cap = this.rules.block.newline.exec(src); + + if (cap) { + if (cap[0].length > 1) { + return { + type: 'space', + raw: cap[0] + }; + } + + return { + raw: '\n' + }; + } + } + }, { + key: "code", + value: function code(src) { + var cap = this.rules.block.code.exec(src); + + if (cap) { + var text = cap[0].replace(/^ {1,4}/gm, ''); + return { + type: 'code', + raw: cap[0], + codeBlockStyle: 'indented', + text: !this.options.pedantic ? rtrim(text, '\n') : text + }; + } + } + }, { + key: "fences", + value: function fences(src) { + var cap = this.rules.block.fences.exec(src); + + if (cap) { + var raw = cap[0]; + var text = indentCodeCompensation(raw, cap[3] || ''); + return { + type: 'code', + raw: raw, + lang: cap[2] ? cap[2].trim() : cap[2], + text: text + }; + } + } + }, { + key: "heading", + value: function heading(src) { + var cap = this.rules.block.heading.exec(src); + + if (cap) { + var text = cap[2].trim(); // remove trailing #s + + if (/#$/.test(text)) { + var trimmed = rtrim(text, '#'); + + if (this.options.pedantic) { + text = trimmed.trim(); + } else if (!trimmed || / $/.test(trimmed)) { + // CommonMark requires space before trailing #s + text = trimmed.trim(); + } + } + + return { + type: 'heading', + raw: cap[0], + depth: cap[1].length, + text: text + }; + } + } + }, { + key: "nptable", + value: function nptable(src) { + var cap = this.rules.block.nptable.exec(src); + + if (cap) { + var item = { + type: 'table', + header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), + cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], + raw: cap[0] + }; + + if (item.header.length === item.align.length) { + var l = item.align.length; + var i; + + for (i = 0; i < l; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } + + l = item.cells.length; + + for (i = 0; i < l; i++) { + item.cells[i] = splitCells(item.cells[i], item.header.length); + } + + return item; + } + } + } + }, { + key: "hr", + value: function hr(src) { + var cap = this.rules.block.hr.exec(src); + + if (cap) { + return { + type: 'hr', + raw: cap[0] + }; + } + } + }, { + key: "blockquote", + value: function blockquote(src) { + var cap = this.rules.block.blockquote.exec(src); + + if (cap) { + var text = cap[0].replace(/^ *> ?/gm, ''); + return { + type: 'blockquote', + raw: cap[0], + text: text + }; + } + } + }, { + key: "list", + value: function list(src) { + var cap = this.rules.block.list.exec(src); + + if (cap) { + var raw = cap[0]; + var bull = cap[2]; + var isordered = bull.length > 1; + var list = { + type: 'list', + raw: raw, + ordered: isordered, + start: isordered ? +bull.slice(0, -1) : '', + loose: false, + items: [] + }; // Get each top-level item. + + var itemMatch = cap[0].match(this.rules.block.item); + var next = false, + item, + space, + bcurr, + bnext, + addBack, + loose, + istask, + ischecked, + endMatch; + var l = itemMatch.length; + bcurr = this.rules.block.listItemStart.exec(itemMatch[0]); + + for (var i = 0; i < l; i++) { + item = itemMatch[i]; + raw = item; + + if (!this.options.pedantic) { + // Determine if current item contains the end of the list + endMatch = item.match(new RegExp('\\n\\s*\\n {0,' + (bcurr[0].length - 1) + '}\\S')); + + if (endMatch) { + addBack = item.length - endMatch.index + itemMatch.slice(i + 1).join('\n').length; + list.raw = list.raw.substring(0, list.raw.length - addBack); + item = item.substring(0, endMatch.index); + raw = item; + l = i + 1; + } + } // Determine whether the next list item belongs here. + // Backpedal if it does not belong in this list. + + + if (i !== l - 1) { + bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]); + + if (!this.options.pedantic ? bnext[1].length >= bcurr[0].length || bnext[1].length > 3 : bnext[1].length > bcurr[1].length) { + // nested list or continuation + itemMatch.splice(i, 2, itemMatch[i] + (!this.options.pedantic && bnext[1].length < bcurr[0].length && !itemMatch[i].match(/\n$/) ? '' : '\n') + itemMatch[i + 1]); + i--; + l--; + continue; + } else if ( // different bullet style + !this.options.pedantic || this.options.smartLists ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] : isordered === (bnext[2].length === 1)) { + addBack = itemMatch.slice(i + 1).join('\n').length; + list.raw = list.raw.substring(0, list.raw.length - addBack); + i = l - 1; + } + + bcurr = bnext; + } // Remove the list item's bullet + // so it is seen as the next token. + + + space = item.length; + item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the + // list item contains. Hacky. + + if (~item.indexOf('\n ')) { + space -= item.length; + item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, ''); + } // trim item newlines at end + + + item = rtrim(item, '\n'); + + if (i !== l - 1) { + raw = raw + '\n'; + } // Determine whether item is loose or not. + // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ + // for discount behavior. + + + loose = next || /\n\n(?!\s*$)/.test(raw); + + if (i !== l - 1) { + next = raw.slice(-2) === '\n\n'; + if (!loose) loose = next; + } + + if (loose) { + list.loose = true; + } // Check for task list items + + + if (this.options.gfm) { + istask = /^\[[ xX]\] /.test(item); + ischecked = undefined; + + if (istask) { + ischecked = item[1] !== ' '; + item = item.replace(/^\[[ xX]\] +/, ''); + } + } + + list.items.push({ + type: 'list_item', + raw: raw, + task: istask, + checked: ischecked, + loose: loose, + text: item + }); + } + + return list; + } + } + }, { + key: "html", + value: function html(src) { + var cap = this.rules.block.html.exec(src); + + if (cap) { + return { + type: this.options.sanitize ? 'paragraph' : 'html', + raw: cap[0], + pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), + text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0] + }; + } + } + }, { + key: "def", + value: function def(src) { + var cap = this.rules.block.def.exec(src); + + if (cap) { + if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1); + var tag = cap[1].toLowerCase().replace(/\s+/g, ' '); + return { + type: 'def', + tag: tag, + raw: cap[0], + href: cap[2], + title: cap[3] + }; + } + } + }, { + key: "table", + value: function table(src) { + var cap = this.rules.block.table.exec(src); + + if (cap) { + var item = { + type: 'table', + header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), + cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] + }; + + if (item.header.length === item.align.length) { + item.raw = cap[0]; + var l = item.align.length; + var i; + + for (i = 0; i < l; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } + + l = item.cells.length; + + for (i = 0; i < l; i++) { + item.cells[i] = splitCells(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length); + } + + return item; + } + } + } + }, { + key: "lheading", + value: function lheading(src) { + var cap = this.rules.block.lheading.exec(src); + + if (cap) { + return { + type: 'heading', + raw: cap[0], + depth: cap[2].charAt(0) === '=' ? 1 : 2, + text: cap[1] + }; + } + } + }, { + key: "paragraph", + value: function paragraph(src) { + var cap = this.rules.block.paragraph.exec(src); + + if (cap) { + return { + type: 'paragraph', + raw: cap[0], + text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1] + }; + } + } + }, { + key: "text", + value: function text(src) { + var cap = this.rules.block.text.exec(src); + + if (cap) { + return { + type: 'text', + raw: cap[0], + text: cap[0] + }; + } + } + }, { + key: "escape", + value: function escape(src) { + var cap = this.rules.inline.escape.exec(src); + + if (cap) { + return { + type: 'escape', + raw: cap[0], + text: _escape(cap[1]) + }; + } + } + }, { + key: "tag", + value: function tag(src, inLink, inRawBlock) { + var cap = this.rules.inline.tag.exec(src); + + if (cap) { + if (!inLink && /^/i.test(cap[0])) { + inLink = false; + } + + if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + inRawBlock = true; + } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + inRawBlock = false; + } + + return { + type: this.options.sanitize ? 'text' : 'html', + raw: cap[0], + inLink: inLink, + inRawBlock: inRawBlock, + text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0] + }; + } + } + }, { + key: "link", + value: function link(src) { + var cap = this.rules.inline.link.exec(src); + + if (cap) { + var trimmedUrl = cap[2].trim(); + + if (!this.options.pedantic && /^$/.test(trimmedUrl)) { + return; + } // ending angle bracket cannot be escaped + + + var rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\'); + + if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) { + return; + } + } else { + // find closing parenthesis + var lastParenIndex = findClosingBracket(cap[2], '()'); + + if (lastParenIndex > -1) { + var start = cap[0].indexOf('!') === 0 ? 5 : 4; + var linkLen = start + cap[1].length + lastParenIndex; + cap[2] = cap[2].substring(0, lastParenIndex); + cap[0] = cap[0].substring(0, linkLen).trim(); + cap[3] = ''; + } + } + + var href = cap[2]; + var title = ''; + + if (this.options.pedantic) { + // split pedantic href and title + var link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href); + + if (link) { + href = link[1]; + title = link[3]; + } + } else { + title = cap[3] ? cap[3].slice(1, -1) : ''; + } + + href = href.trim(); + + if (/^$/.test(trimmedUrl)) { + // pedantic allows starting angle bracket without ending angle bracket + href = href.slice(1); + } else { + href = href.slice(1, -1); + } + } + + return outputLink(cap, { + href: href ? href.replace(this.rules.inline._escapes, '$1') : href, + title: title ? title.replace(this.rules.inline._escapes, '$1') : title + }, cap[0]); + } + } + }, { + key: "reflink", + value: function reflink(src, links) { + var cap; + + if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) { + var link = (cap[2] || cap[1]).replace(/\s+/g, ' '); + link = links[link.toLowerCase()]; + + if (!link || !link.href) { + var text = cap[0].charAt(0); + return { + type: 'text', + raw: text, + text: text + }; + } + + return outputLink(cap, link, cap[0]); + } + } + }, { + key: "emStrong", + value: function emStrong(src, maskedSrc) { + var prevChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; + var match = this.rules.inline.emStrong.lDelim.exec(src); + if (!match) return; // _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well + + 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; + var nextChar = match[1] || match[2] || ''; + + if (!nextChar || nextChar && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar))) { + var lLength = match[0].length - 1; + var rDelim, + rLength, + delimTotal = lLength, + midDelimTotal = 0; + var endReg = match[0][0] === '*' ? this.rules.inline.emStrong.rDelimAst : this.rules.inline.emStrong.rDelimUnd; + endReg.lastIndex = 0; // Clip maskedSrc to same section of string as src (move to lexer?) + + maskedSrc = maskedSrc.slice(-1 * src.length + lLength); + + while ((match = endReg.exec(maskedSrc)) != null) { + rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6]; + if (!rDelim) continue; // skip single * in __abc*abc__ + + rLength = rDelim.length; + + if (match[3] || match[4]) { + // found another Left Delim + delimTotal += rLength; + continue; + } else if (match[5] || match[6]) { + // either Left or Right Delim + if (lLength % 3 && !((lLength + rLength) % 3)) { + midDelimTotal += rLength; + continue; // CommonMark Emphasis Rules 9-10 + } + } + + delimTotal -= rLength; + if (delimTotal > 0) continue; // Haven't found enough closing delimiters + // Remove extra characters. *a*** -> *a* + + rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal); // Create `em` if smallest delimiter has odd char count. *a*** + + if (Math.min(lLength, rLength) % 2) { + return { + type: 'em', + raw: src.slice(0, lLength + match.index + rLength + 1), + text: src.slice(1, lLength + match.index + rLength) + }; + } // Create 'strong' if smallest delimiter has even char count. **a*** + + + return { + type: 'strong', + raw: src.slice(0, lLength + match.index + rLength + 1), + text: src.slice(2, lLength + match.index + rLength - 1) + }; + } + } + } + }, { + key: "codespan", + value: function codespan(src) { + var cap = this.rules.inline.code.exec(src); + + if (cap) { + var text = cap[2].replace(/\n/g, ' '); + var hasNonSpaceChars = /[^ ]/.test(text); + var hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text); + + if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) { + text = text.substring(1, text.length - 1); + } + + text = _escape(text, true); + return { + type: 'codespan', + raw: cap[0], + text: text + }; + } + } + }, { + key: "br", + value: function br(src) { + var cap = this.rules.inline.br.exec(src); + + if (cap) { + return { + type: 'br', + raw: cap[0] + }; + } + } + }, { + key: "del", + value: function del(src) { + var cap = this.rules.inline.del.exec(src); + + if (cap) { + return { + type: 'del', + raw: cap[0], + text: cap[2] + }; + } + } + }, { + key: "autolink", + value: function autolink(src, mangle) { + var cap = this.rules.inline.autolink.exec(src); + + if (cap) { + var text, href; + + if (cap[2] === '@') { + text = _escape(this.options.mangle ? mangle(cap[1]) : cap[1]); + href = 'mailto:' + text; + } else { + text = _escape(cap[1]); + href = text; + } + + return { + type: 'link', + raw: cap[0], + text: text, + href: href, + tokens: [{ + type: 'text', + raw: text, + text: text + }] + }; + } + } + }, { + key: "url", + value: function url(src, mangle) { + var cap; + + if (cap = this.rules.inline.url.exec(src)) { + var text, href; + + if (cap[2] === '@') { + text = _escape(this.options.mangle ? mangle(cap[0]) : cap[0]); + href = 'mailto:' + text; + } else { + // do extended autolink path validation + var prevCapZero; + + do { + prevCapZero = cap[0]; + cap[0] = this.rules.inline._backpedal.exec(cap[0])[0]; + } while (prevCapZero !== cap[0]); + + text = _escape(cap[0]); + + if (cap[1] === 'www.') { + href = 'http://' + text; + } else { + href = text; + } + } + + return { + type: 'link', + raw: cap[0], + text: text, + href: href, + tokens: [{ + type: 'text', + raw: text, + text: text + }] + }; + } + } + }, { + key: "inlineText", + value: function inlineText(src, inRawBlock, smartypants) { + var cap = this.rules.inline.text.exec(src); + + if (cap) { + var text; + + if (inRawBlock) { + text = this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]; + } else { + text = _escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]); + } + + return { + type: 'text', + raw: cap[0], + text: text + }; + } + } + }]); + + return Tokenizer; + }(); + + var noopTest = helpers.noopTest, + edit = helpers.edit, + merge$1 = helpers.merge; + /** + * Block-Level Grammar + */ + + var block$1 = { + newline: /^(?: *(?:\n|$))+/, + code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/, + fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, + hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, + heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, + blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, + list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/, + html: '^ {0,3}(?:' // optional indentation + + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + + '|comment[^\\n]*(\\n+|$)' // (2) + + '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3) + + '|\\n*|$)' // (4) + + '|\\n*|$)' // (5) + + '|)[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (6) + + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) open tag + + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag + + ')', + def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, + nptable: noopTest, + table: noopTest, + lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/, + // regex template, placeholders will be replaced according to different paragraph + // interruption rules of commonmark and the original markdown spec: + _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html| +\n)[^\n]+)*)/, + text: /^[^\n]+/ + }; + block$1._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; + block$1._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; + block$1.def = edit(block$1.def).replace('label', block$1._label).replace('title', block$1._title).getRegex(); + block$1.bullet = /(?:[*+-]|\d{1,9}[.)])/; + block$1.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/; + block$1.item = edit(block$1.item, 'gm').replace(/bull/g, block$1.bullet).getRegex(); + block$1.listItemStart = edit(/^( *)(bull) */).replace('bull', block$1.bullet).getRegex(); + 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(); + 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'; + block$1._comment = /|$)/; + 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(); + 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 + .replace('blockquote', ' {0,3}>').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // pars can be interrupted by type (6) html blocks + .getRegex(); + block$1.blockquote = edit(block$1.blockquote).replace('paragraph', block$1.paragraph).getRegex(); + /** + * Normal Block Grammar + */ + + block$1.normal = merge$1({}, block$1); + /** + * GFM Block Grammar + */ + + block$1.gfm = merge$1({}, block$1.normal, { + nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header + + ' {0,3}([-:]+ *\\|[-| :]*)' // Align + + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', + // Cells + table: '^ *\\|(.+)\\n' // Header + + ' {0,3}\\|?( *[-:]+[-| :]*)' // Align + + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells + + }); + 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 + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + .getRegex(); + 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 + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks + .getRegex(); + /** + * Pedantic grammar (original John Gruber's loose markdown specification) + */ + + block$1.pedantic = merge$1({}, block$1.normal, { + html: edit('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag + + '|\\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(), + def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, + heading: /^(#{1,6})(.*)(?:\n+|$)/, + fences: noopTest, + // fences not supported + 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() + }); + /** + * Inline-Level Grammar + */ + + var inline$1 = { + escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, + autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, + url: noopTest, + tag: '^comment' + '|^' // self-closing tag + + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag + + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. + + '|^' // declaration, e.g. + + '|^', + // CDATA section + link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/, + reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/, + nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/, + reflinkSearch: 'reflink|nolink(?!\\()', + emStrong: { + lDelim: /^(?:\*+(?:([punct_])|[^\s*]))|^_+(?:([punct*])|([^\s_]))/, + // (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. + // () Skip other delimiter (1) #*** (2) a***#, a*** (3) #***a, ***a (4) ***# (5) #***# (6) a***a + rDelimAst: /\_\_[^_*]*?\*[^_*]*?\_\_|[punct_](\*+)(?=[\s]|$)|[^punct*_\s](\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|[^punct*_\s](\*+)(?=[^punct*_\s])/, + rDelimUnd: /\*\*[^_*]*?\_[^_*]*?\*\*|[punct*](\_+)(?=[\s]|$)|[^punct*_\s](\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/ // ^- Not allowed for _ + + }, + code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, + br: /^( {2,}|\\)\n(?!\s*$)/, + del: noopTest, + text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~'; + inline$1.punctuation = edit(inline$1.punctuation).replace(/punctuation/g, inline$1._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, + + inline$1.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g; + inline$1.escapedEmSt = /\\\*|\\_/g; + inline$1._comment = edit(block$1._comment).replace('(?:-->|$)', '-->').getRegex(); + inline$1.emStrong.lDelim = edit(inline$1.emStrong.lDelim).replace(/punct/g, inline$1._punctuation).getRegex(); + inline$1.emStrong.rDelimAst = edit(inline$1.emStrong.rDelimAst, 'g').replace(/punct/g, inline$1._punctuation).getRegex(); + inline$1.emStrong.rDelimUnd = edit(inline$1.emStrong.rDelimUnd, 'g').replace(/punct/g, inline$1._punctuation).getRegex(); + inline$1._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; + inline$1._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; + 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])?)+(?![-_])/; + inline$1.autolink = edit(inline$1.autolink).replace('scheme', inline$1._scheme).replace('email', inline$1._email).getRegex(); + inline$1._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; + inline$1.tag = edit(inline$1.tag).replace('comment', inline$1._comment).replace('attribute', inline$1._attribute).getRegex(); + inline$1._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; + inline$1._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/; + inline$1._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; + inline$1.link = edit(inline$1.link).replace('label', inline$1._label).replace('href', inline$1._href).replace('title', inline$1._title).getRegex(); + inline$1.reflink = edit(inline$1.reflink).replace('label', inline$1._label).getRegex(); + inline$1.reflinkSearch = edit(inline$1.reflinkSearch, 'g').replace('reflink', inline$1.reflink).replace('nolink', inline$1.nolink).getRegex(); + /** + * Normal Inline Grammar + */ + + inline$1.normal = merge$1({}, inline$1); + /** + * Pedantic Inline Grammar + */ + + inline$1.pedantic = merge$1({}, inline$1.normal, { + strong: { + start: /^__|\*\*/, + middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, + endAst: /\*\*(?!\*)/g, + endUnd: /__(?!_)/g + }, + em: { + start: /^_|\*/, + middle: /^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/, + endAst: /\*(?!\*)/g, + endUnd: /_(?!_)/g + }, + link: edit(/^!?\[(label)\]\((.*?)\)/).replace('label', inline$1._label).getRegex(), + reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline$1._label).getRegex() + }); + /** + * GFM Inline Grammar + */ + + inline$1.gfm = merge$1({}, inline$1.normal, { + escape: edit(inline$1.escape).replace('])', '~|])').getRegex(), + _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/, + url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, + _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, + del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/, + text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\ 0.5) { + ch = 'x' + ch.toString(16); + } + + out += '&#' + ch + ';'; + } + + return out; + } + /** + * Block Lexer + */ + + + var Lexer_1 = /*#__PURE__*/function () { + function Lexer(options) { + _classCallCheck$1(this, Lexer); + + this.tokens = []; + this.tokens.links = Object.create(null); + this.options = options || defaults$3; + this.options.tokenizer = this.options.tokenizer || new Tokenizer$1(); + this.tokenizer = this.options.tokenizer; + this.tokenizer.options = this.options; + var rules = { + block: block.normal, + inline: inline.normal + }; + + if (this.options.pedantic) { + rules.block = block.pedantic; + rules.inline = inline.pedantic; + } else if (this.options.gfm) { + rules.block = block.gfm; + + if (this.options.breaks) { + rules.inline = inline.breaks; + } else { + rules.inline = inline.gfm; + } + } + + this.tokenizer.rules = rules; + } + /** + * Expose Rules + */ + + + _createClass$1(Lexer, [{ + key: "lex", + value: + /** + * Preprocessing + */ + function lex(src) { + src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, ' '); + this.blockTokens(src, this.tokens, true); + this.inline(this.tokens); + return this.tokens; + } + /** + * Lexing + */ + + }, { + key: "blockTokens", + value: function blockTokens(src) { + var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; + var top = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; + + if (this.options.pedantic) { + src = src.replace(/^ +$/gm, ''); + } + + var token, i, l, lastToken; + + while (src) { + // newline + if (token = this.tokenizer.space(src)) { + src = src.substring(token.raw.length); + + if (token.type) { + tokens.push(token); + } + + continue; + } // code + + + if (token = this.tokenizer.code(src)) { + src = src.substring(token.raw.length); + lastToken = tokens[tokens.length - 1]; // An indented code block cannot interrupt a paragraph. + + if (lastToken && lastToken.type === 'paragraph') { + lastToken.raw += '\n' + token.raw; + lastToken.text += '\n' + token.text; + } else { + tokens.push(token); + } + + continue; + } // fences + + + if (token = this.tokenizer.fences(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // heading + + + if (token = this.tokenizer.heading(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // table no leading pipe (gfm) + + + if (token = this.tokenizer.nptable(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // hr + + + if (token = this.tokenizer.hr(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // blockquote + + + if (token = this.tokenizer.blockquote(src)) { + src = src.substring(token.raw.length); + token.tokens = this.blockTokens(token.text, [], top); + tokens.push(token); + continue; + } // list + + + if (token = this.tokenizer.list(src)) { + src = src.substring(token.raw.length); + l = token.items.length; + + for (i = 0; i < l; i++) { + token.items[i].tokens = this.blockTokens(token.items[i].text, [], false); + } + + tokens.push(token); + continue; + } // html + + + if (token = this.tokenizer.html(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // def + + + if (top && (token = this.tokenizer.def(src))) { + src = src.substring(token.raw.length); + + if (!this.tokens.links[token.tag]) { + this.tokens.links[token.tag] = { + href: token.href, + title: token.title + }; + } + + continue; + } // table (gfm) + + + if (token = this.tokenizer.table(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // lheading + + + if (token = this.tokenizer.lheading(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // top-level paragraph + + + if (top && (token = this.tokenizer.paragraph(src))) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // text + + + if (token = this.tokenizer.text(src)) { + src = src.substring(token.raw.length); + lastToken = tokens[tokens.length - 1]; + + if (lastToken && lastToken.type === 'text') { + lastToken.raw += '\n' + token.raw; + lastToken.text += '\n' + token.text; + } else { + tokens.push(token); + } + + continue; + } + + if (src) { + var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0); + + if (this.options.silent) { + console.error(errMsg); + break; + } else { + throw new Error(errMsg); + } + } + } + + return tokens; + } + }, { + key: "inline", + value: function inline(tokens) { + var i, j, k, l2, row, token; + var l = tokens.length; + + for (i = 0; i < l; i++) { + token = tokens[i]; + + switch (token.type) { + case 'paragraph': + case 'text': + case 'heading': + { + token.tokens = []; + this.inlineTokens(token.text, token.tokens); + break; + } + + case 'table': + { + token.tokens = { + header: [], + cells: [] + }; // header + + l2 = token.header.length; + + for (j = 0; j < l2; j++) { + token.tokens.header[j] = []; + this.inlineTokens(token.header[j], token.tokens.header[j]); + } // cells + + + l2 = token.cells.length; + + for (j = 0; j < l2; j++) { + row = token.cells[j]; + token.tokens.cells[j] = []; + + for (k = 0; k < row.length; k++) { + token.tokens.cells[j][k] = []; + this.inlineTokens(row[k], token.tokens.cells[j][k]); + } + } + + break; + } + + case 'blockquote': + { + this.inline(token.tokens); + break; + } + + case 'list': + { + l2 = token.items.length; + + for (j = 0; j < l2; j++) { + this.inline(token.items[j].tokens); + } + + break; + } + } + } + + return tokens; + } + /** + * Lexing/Compiling + */ + + }, { + key: "inlineTokens", + value: function inlineTokens(src) { + var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; + var inLink = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + var inRawBlock = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; + var token, lastToken; // String with links masked to avoid interference with em and strong + + var maskedSrc = src; + var match; + var keepPrevChar, prevChar; // Mask out reflinks + + if (this.tokens.links) { + var links = Object.keys(this.tokens.links); + + if (links.length > 0) { + while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) { + if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) { + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); + } + } + } + } // Mask out other blocks + + + while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) { + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); + } // Mask out escaped em & strong delimiters + + + while ((match = this.tokenizer.rules.inline.escapedEmSt.exec(maskedSrc)) != null) { + maskedSrc = maskedSrc.slice(0, match.index) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.escapedEmSt.lastIndex); + } + + while (src) { + if (!keepPrevChar) { + prevChar = ''; + } + + keepPrevChar = false; // escape + + if (token = this.tokenizer.escape(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // tag + + + if (token = this.tokenizer.tag(src, inLink, inRawBlock)) { + src = src.substring(token.raw.length); + inLink = token.inLink; + inRawBlock = token.inRawBlock; + var _lastToken = tokens[tokens.length - 1]; + + if (_lastToken && token.type === 'text' && _lastToken.type === 'text') { + _lastToken.raw += token.raw; + _lastToken.text += token.text; + } else { + tokens.push(token); + } + + continue; + } // link + + + if (token = this.tokenizer.link(src)) { + src = src.substring(token.raw.length); + + if (token.type === 'link') { + token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); + } + + tokens.push(token); + continue; + } // reflink, nolink + + + if (token = this.tokenizer.reflink(src, this.tokens.links)) { + src = src.substring(token.raw.length); + var _lastToken2 = tokens[tokens.length - 1]; + + if (token.type === 'link') { + token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); + tokens.push(token); + } else if (_lastToken2 && token.type === 'text' && _lastToken2.type === 'text') { + _lastToken2.raw += token.raw; + _lastToken2.text += token.text; + } else { + tokens.push(token); + } + + continue; + } // em & strong + + + if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) { + src = src.substring(token.raw.length); + token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); + tokens.push(token); + continue; + } // code + + + if (token = this.tokenizer.codespan(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // br + + + if (token = this.tokenizer.br(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // del (gfm) + + + if (token = this.tokenizer.del(src)) { + src = src.substring(token.raw.length); + token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); + tokens.push(token); + continue; + } // autolink + + + if (token = this.tokenizer.autolink(src, mangle)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // url (gfm) + + + if (!inLink && (token = this.tokenizer.url(src, mangle))) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // text + + + if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) { + src = src.substring(token.raw.length); + + if (token.raw.slice(-1) !== '_') { + // Track prevChar before string of ____ started + prevChar = token.raw.slice(-1); + } + + keepPrevChar = true; + lastToken = tokens[tokens.length - 1]; + + if (lastToken && lastToken.type === 'text') { + lastToken.raw += token.raw; + lastToken.text += token.text; + } else { + tokens.push(token); + } + + continue; + } + + if (src) { + var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0); + + if (this.options.silent) { + console.error(errMsg); + break; + } else { + throw new Error(errMsg); + } + } + } + + return tokens; + } + }], [{ + key: "rules", + get: function get() { + return { + block: block, + inline: inline + }; + } + /** + * Static Lex Method + */ + + }, { + key: "lex", + value: function lex(src, options) { + var lexer = new Lexer(options); + return lexer.lex(src); + } + /** + * Static Lex Inline Method + */ + + }, { + key: "lexInline", + value: function lexInline(src, options) { + var lexer = new Lexer(options); + return lexer.inlineTokens(src); + } + }]); + + return Lexer; + }(); + + var defaults$2 = defaults$5.exports.defaults; + var cleanUrl = helpers.cleanUrl, + escape$2 = helpers.escape; + /** + * Renderer + */ + + var Renderer_1 = /*#__PURE__*/function () { + function Renderer(options) { + _classCallCheck$1(this, Renderer); + + this.options = options || defaults$2; + } + + _createClass$1(Renderer, [{ + key: "code", + value: function code(_code, infostring, escaped) { + var lang = (infostring || '').match(/\S*/)[0]; + + if (this.options.highlight) { + var out = this.options.highlight(_code, lang); + + if (out != null && out !== _code) { + escaped = true; + _code = out; + } + } + + _code = _code.replace(/\n$/, '') + '\n'; + + if (!lang) { + return '
' + (escaped ? _code : escape$2(_code, true)) + '
\n'; + } + + return '
' + (escaped ? _code : escape$2(_code, true)) + '
\n'; + } + }, { + key: "blockquote", + value: function blockquote(quote) { + return '
\n' + quote + '
\n'; + } + }, { + key: "html", + value: function html(_html) { + return _html; + } + }, { + key: "heading", + value: function heading(text, level, raw, slugger) { + if (this.options.headerIds) { + return '' + text + '\n'; + } // ignore IDs + + + return '' + text + '\n'; + } + }, { + key: "hr", + value: function hr() { + return this.options.xhtml ? '
\n' : '
\n'; + } + }, { + key: "list", + value: function list(body, ordered, start) { + var type = ordered ? 'ol' : 'ul', + startatt = ordered && start !== 1 ? ' start="' + start + '"' : ''; + return '<' + type + startatt + '>\n' + body + '\n'; + } + }, { + key: "listitem", + value: function listitem(text) { + return '
  • ' + text + '
  • \n'; + } + }, { + key: "checkbox", + value: function checkbox(checked) { + return ' '; + } + }, { + key: "paragraph", + value: function paragraph(text) { + return '

    ' + text + '

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

    An error occurred:

    ' + escape$1(e.message + '', true) + '
    '; + } + + throw e; + } + } + /** + * Options + */ + + + marked.options = marked.setOptions = function (opt) { + merge(marked.defaults, opt); + changeDefaults(marked.defaults); + return marked; + }; + + marked.getDefaults = getDefaults; + marked.defaults = defaults; + /** + * Use Extension + */ + + marked.use = function (extension) { + var opts = merge({}, extension); + + if (extension.renderer) { + (function () { + var renderer = marked.defaults.renderer || new Renderer(); + + var _loop = function _loop(prop) { + var prevRenderer = renderer[prop]; + + renderer[prop] = function () { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var ret = extension.renderer[prop].apply(renderer, args); + + if (ret === false) { + ret = prevRenderer.apply(renderer, args); + } + + return ret; + }; + }; + + for (var prop in extension.renderer) { + _loop(prop); + } + + opts.renderer = renderer; + })(); + } + + if (extension.tokenizer) { + (function () { + var tokenizer = marked.defaults.tokenizer || new Tokenizer(); + + var _loop2 = function _loop2(prop) { + var prevTokenizer = tokenizer[prop]; + + tokenizer[prop] = function () { + for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + var ret = extension.tokenizer[prop].apply(tokenizer, args); + + if (ret === false) { + ret = prevTokenizer.apply(tokenizer, args); + } + + return ret; + }; + }; + + for (var prop in extension.tokenizer) { + _loop2(prop); + } + + opts.tokenizer = tokenizer; + })(); + } + + if (extension.walkTokens) { + var walkTokens = marked.defaults.walkTokens; + + opts.walkTokens = function (token) { + extension.walkTokens(token); + + if (walkTokens) { + walkTokens(token); + } + }; + } + + marked.setOptions(opts); + }; + /** + * Run callback for every token + */ + + + marked.walkTokens = function (tokens, callback) { + var _iterator = _createForOfIteratorHelper(tokens), + _step; + + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var token = _step.value; + callback(token); + + switch (token.type) { + case 'table': + { + var _iterator2 = _createForOfIteratorHelper(token.tokens.header), + _step2; + + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + var cell = _step2.value; + marked.walkTokens(cell, callback); + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } + + var _iterator3 = _createForOfIteratorHelper(token.tokens.cells), + _step3; + + try { + for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { + var row = _step3.value; + + var _iterator4 = _createForOfIteratorHelper(row), + _step4; + + try { + for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) { + var _cell = _step4.value; + marked.walkTokens(_cell, callback); + } + } catch (err) { + _iterator4.e(err); + } finally { + _iterator4.f(); + } + } + } catch (err) { + _iterator3.e(err); + } finally { + _iterator3.f(); + } + + break; + } + + case 'list': + { + marked.walkTokens(token.items, callback); + break; + } + + default: + { + if (token.tokens) { + marked.walkTokens(token.tokens, callback); + } + } + } + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + }; + /** + * Parse Inline + */ + + + marked.parseInline = function (src, opt) { + // throw error in case of non string input + if (typeof src === 'undefined' || src === null) { + throw new Error('marked.parseInline(): input parameter is undefined or null'); + } + + if (typeof src !== 'string') { + throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected'); + } + + opt = merge({}, marked.defaults, opt || {}); + checkSanitizeDeprecation(opt); + + try { + var tokens = Lexer.lexInline(src, opt); + + if (opt.walkTokens) { + marked.walkTokens(tokens, opt.walkTokens); + } + + return Parser.parseInline(tokens, opt); + } catch (e) { + e.message += '\nPlease report this to https://github.com/markedjs/marked.'; + + if (opt.silent) { + return '

    An error occurred:

    ' + escape$1(e.message + '', true) + '
    '; + } + + throw e; + } + }; + /** + * Expose + */ + + + marked.Parser = Parser; + marked.parser = Parser.parse; + marked.Renderer = Renderer; + marked.TextRenderer = TextRenderer; + marked.Lexer = Lexer; + marked.lexer = Lexer.lex; + marked.Tokenizer = Tokenizer; + marked.Slugger = Slugger; + marked.parse = marked; + var marked_1 = marked; + + var tiler$4 = utilTiler(); + var dispatch$5 = dispatch$8('loaded'); + var _tileZoom$1 = 14; + var _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3'; + var _osmoseData = { + icons: {}, + items: [] + }; // This gets reassigned if reset + + var _cache; + + function abortRequest$4(controller) { + if (controller) { + controller.abort(); + } + } + + function abortUnwantedRequests$1(cache, tiles) { + Object.keys(cache.inflightTile).forEach(function (k) { + var wanted = tiles.find(function (tile) { + return k === tile.id; + }); + + if (!wanted) { + abortRequest$4(cache.inflightTile[k]); + delete cache.inflightTile[k]; + } + }); + } + + function encodeIssueRtree(d) { + return { + minX: d.loc[0], + minY: d.loc[1], + maxX: d.loc[0], + maxY: d.loc[1], + data: d + }; + } // Replace or remove QAItem from rtree + + + function updateRtree$1(item, replace) { + _cache.rtree.remove(item, function (a, b) { + return a.data.id === b.data.id; + }); + + if (replace) { + _cache.rtree.insert(item); + } + } // Issues shouldn't obscure each other + + + function preventCoincident(loc) { + var coincident = false; + + do { + // first time, move marker up. after that, move marker right. + var delta = coincident ? [0.00001, 0] : [0, 0.00001]; + loc = geoVecAdd(loc, delta); + var bbox = geoExtent(loc).bbox(); + coincident = _cache.rtree.search(bbox).length; + } while (coincident); + + return loc; + } + + var serviceOsmose = { + title: 'osmose', + init: function init() { + _mainFileFetcher.get('qa_data').then(function (d) { + _osmoseData = d.osmose; + _osmoseData.items = Object.keys(d.osmose.icons).map(function (s) { + return s.split('-')[0]; + }).reduce(function (unique, item) { + return unique.indexOf(item) !== -1 ? unique : [].concat(_toConsumableArray(unique), [item]); + }, []); + }); + + if (!_cache) { + this.reset(); + } + + this.event = utilRebind(this, dispatch$5, 'on'); + }, + reset: function reset() { + var _strings = {}; + var _colors = {}; + + if (_cache) { + Object.values(_cache.inflightTile).forEach(abortRequest$4); // Strings and colors are static and should not be re-populated + + _strings = _cache.strings; + _colors = _cache.colors; + } + + _cache = { + data: {}, + loadedTile: {}, + inflightTile: {}, + inflightPost: {}, + closed: {}, + rtree: new RBush(), + strings: _strings, + colors: _colors + }; + }, + loadIssues: function loadIssues(projection) { + var _this = this; + + var params = { + // Tiles return a maximum # of issues + // So we want to filter our request for only types iD supports + item: _osmoseData.items + }; // determine the needed tiles to cover the view + + var tiles = tiler$4.zoomExtent([_tileZoom$1, _tileZoom$1]).getTiles(projection); // abort inflight requests that are no longer needed + + abortUnwantedRequests$1(_cache, tiles); // issue new requests.. + + tiles.forEach(function (tile) { + if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return; + + var _tile$xyz = _slicedToArray(tile.xyz, 3), + x = _tile$xyz[0], + y = _tile$xyz[1], + z = _tile$xyz[2]; + + var url = "".concat(_osmoseUrlRoot, "/issues/").concat(z, "/").concat(x, "/").concat(y, ".json?") + utilQsString(params); + var controller = new AbortController(); + _cache.inflightTile[tile.id] = controller; + d3_json(url, { + signal: controller.signal + }).then(function (data) { + delete _cache.inflightTile[tile.id]; + _cache.loadedTile[tile.id] = true; + + if (data.features) { + data.features.forEach(function (issue) { + var _issue$properties = issue.properties, + item = _issue$properties.item, + cl = _issue$properties["class"], + id = _issue$properties.uuid; + /* Osmose issues are uniquely identified by a unique + `item` and `class` combination (both integer values) */ + + var itemType = "".concat(item, "-").concat(cl); // Filter out unsupported issue types (some are too specific or advanced) + + if (itemType in _osmoseData.icons) { + var loc = issue.geometry.coordinates; // lon, lat + + loc = preventCoincident(loc); + var d = new QAItem(loc, _this, itemType, id, { + item: item + }); // Setting elems here prevents UI detail requests + + if (item === 8300 || item === 8360) { + d.elems = []; + } + + _cache.data[d.id] = d; + + _cache.rtree.insert(encodeIssueRtree(d)); + } + }); + } + + dispatch$5.call('loaded'); + })["catch"](function () { + delete _cache.inflightTile[tile.id]; + _cache.loadedTile[tile.id] = true; + }); + }); + }, + loadIssueDetail: function loadIssueDetail(issue) { + var _this2 = this; + + // Issue details only need to be fetched once + if (issue.elems !== undefined) { + return Promise.resolve(issue); + } + + var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "?langs=").concat(_mainLocalizer.localeCode()); + + var cacheDetails = function cacheDetails(data) { + // Associated elements used for highlighting + // Assign directly for immediate use in the callback + issue.elems = data.elems.map(function (e) { + return e.type.substring(0, 1) + e.id; + }); // Some issues have instance specific detail in a subtitle + + issue.detail = data.subtitle ? marked_1(data.subtitle.auto) : ''; + + _this2.replaceItem(issue); + }; + + return d3_json(url).then(cacheDetails).then(function () { + return issue; + }); + }, + loadStrings: function loadStrings() { + var locale = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _mainLocalizer.localeCode(); + var items = Object.keys(_osmoseData.icons); + + if (locale in _cache.strings && Object.keys(_cache.strings[locale]).length === items.length) { + return Promise.resolve(_cache.strings[locale]); + } // May be partially populated already if some requests were successful + + + if (!(locale in _cache.strings)) { + _cache.strings[locale] = {}; + } // Only need to cache strings for supported issue types + // Using multiple individual item + class requests to reduce fetched data size + + + var allRequests = items.map(function (itemType) { + // No need to request data we already have + if (itemType in _cache.strings[locale]) return null; + + var cacheData = function cacheData(data) { + // Bunch of nested single value arrays of objects + var _data$categories = _slicedToArray(data.categories, 1), + _data$categories$ = _data$categories[0], + cat = _data$categories$ === void 0 ? { + items: [] + } : _data$categories$; + + var _cat$items = _slicedToArray(cat.items, 1), + _cat$items$ = _cat$items[0], + item = _cat$items$ === void 0 ? { + "class": [] + } : _cat$items$; + + var _item$class = _slicedToArray(item["class"], 1), + _item$class$ = _item$class[0], + cl = _item$class$ === void 0 ? null : _item$class$; // If null default value is reached, data wasn't as expected (or was empty) + + + if (!cl) { + /* eslint-disable no-console */ + console.log("Osmose strings request (".concat(itemType, ") had unexpected data")); + /* eslint-enable no-console */ + + return; + } // Cache served item colors to automatically style issue markers later + + + var itemInt = item.item, + color = item.color; + + if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) { + _cache.colors[itemInt] = color; + } // Value of root key will be null if no string exists + // If string exists, value is an object with key 'auto' for string + + + var title = cl.title, + detail = cl.detail, + fix = cl.fix, + trap = cl.trap; // Osmose titles shouldn't contain markdown + + var issueStrings = {}; + if (title) issueStrings.title = title.auto; + if (detail) issueStrings.detail = marked_1(detail.auto); + if (trap) issueStrings.trap = marked_1(trap.auto); + if (fix) issueStrings.fix = marked_1(fix.auto); + _cache.strings[locale][itemType] = issueStrings; + }; + + var _itemType$split = itemType.split('-'), + _itemType$split2 = _slicedToArray(_itemType$split, 2), + item = _itemType$split2[0], + cl = _itemType$split2[1]; // Osmose API falls back to English strings where untranslated or if locale doesn't exist + + + var url = "".concat(_osmoseUrlRoot, "/items/").concat(item, "/class/").concat(cl, "?langs=").concat(locale); + return d3_json(url).then(cacheData); + }).filter(Boolean); + return Promise.all(allRequests).then(function () { + return _cache.strings[locale]; + }); + }, + getStrings: function getStrings(itemType) { + var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _mainLocalizer.localeCode(); + // No need to fallback to English, Osmose API handles this for us + return locale in _cache.strings ? _cache.strings[locale][itemType] : {}; + }, + getColor: function getColor(itemType) { + return itemType in _cache.colors ? _cache.colors[itemType] : '#FFFFFF'; + }, + postUpdate: function postUpdate(issue, callback) { + var _this3 = this; + + if (_cache.inflightPost[issue.id]) { + return callback({ + message: 'Issue update already inflight', + status: -2 + }, issue); + } // UI sets the status to either 'done' or 'false' + + + var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "/").concat(issue.newStatus); + var controller = new AbortController(); + + var after = function after() { + delete _cache.inflightPost[issue.id]; + + _this3.removeItem(issue); + + if (issue.newStatus === 'done') { + // Keep track of the number of issues closed per `item` to tag the changeset + if (!(issue.item in _cache.closed)) { + _cache.closed[issue.item] = 0; + } + + _cache.closed[issue.item] += 1; + } + + if (callback) callback(null, issue); + }; + + _cache.inflightPost[issue.id] = controller; + fetch(url, { + signal: controller.signal + }).then(after)["catch"](function (err) { + delete _cache.inflightPost[issue.id]; + if (callback) callback(err.message); + }); + }, + // Get all cached QAItems covering the viewport + getItems: function getItems(projection) { + var viewport = projection.clipExtent(); + var min = [viewport[0][0], viewport[1][1]]; + var max = [viewport[1][0], viewport[0][1]]; + var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); + return _cache.rtree.search(bbox).map(function (d) { + return d.data; + }); + }, + // Get a QAItem from cache + // NOTE: Don't change method name until UI v3 is merged + getError: function getError(id) { + return _cache.data[id]; + }, + // get the name of the icon to display for this item + getIcon: function getIcon(itemType) { + return _osmoseData.icons[itemType]; + }, + // Replace a single QAItem in the cache + replaceItem: function replaceItem(item) { + if (!(item instanceof QAItem) || !item.id) return; + _cache.data[item.id] = item; + updateRtree$1(encodeIssueRtree(item), true); // true = replace + + return item; + }, + // Remove a single QAItem from the cache + removeItem: function removeItem(item) { + if (!(item instanceof QAItem) || !item.id) return; + delete _cache.data[item.id]; + updateRtree$1(encodeIssueRtree(item), false); // false = remove + }, + // Used to populate `closed:osmose:*` changeset tags + getClosedCounts: function getClosedCounts() { + return _cache.closed; + }, + itemURL: function itemURL(item) { + return "https://osmose.openstreetmap.fr/en/error/".concat(item.id); + } + }; + + var ieee754$1 = {}; + + /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ + + ieee754$1.read = function (buffer, offset, isLE, mLen, nBytes) { + var e, m; + var eLen = nBytes * 8 - mLen - 1; + var eMax = (1 << eLen) - 1; + var eBias = eMax >> 1; + var nBits = -7; + var i = isLE ? nBytes - 1 : 0; + var d = isLE ? -1 : 1; + var s = buffer[offset + i]; + i += d; + e = s & (1 << -nBits) - 1; + s >>= -nBits; + nBits += eLen; + + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + m = e & (1 << -nBits) - 1; + e >>= -nBits; + nBits += mLen; + + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + if (e === 0) { + e = 1 - eBias; + } else if (e === eMax) { + return m ? NaN : (s ? -1 : 1) * Infinity; + } else { + m = m + Math.pow(2, mLen); + e = e - eBias; + } + + return (s ? -1 : 1) * m * Math.pow(2, e - mLen); + }; + + ieee754$1.write = function (buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c; + var eLen = nBytes * 8 - mLen - 1; + var eMax = (1 << eLen) - 1; + var eBias = eMax >> 1; + var rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0; + var i = isLE ? 0 : nBytes - 1; + var d = isLE ? 1 : -1; + var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0; + value = Math.abs(value); + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0; + e = eMax; + } else { + e = Math.floor(Math.log(value) / Math.LN2); + + if (value * (c = Math.pow(2, -e)) < 1) { + e--; + c *= 2; + } + + if (e + eBias >= 1) { + value += rt / c; + } else { + value += rt * Math.pow(2, 1 - eBias); + } + + if (value * c >= 2) { + e++; + c /= 2; + } + + if (e + eBias >= eMax) { + m = 0; + e = eMax; + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen); + e = e + eBias; + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); + e = 0; + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + + e = e << mLen | m; + eLen += mLen; + + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + + buffer[offset + i - d] |= s * 128; + }; + + var pbf = Pbf; + var ieee754 = ieee754$1; + + function Pbf(buf) { + this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0); + this.pos = 0; + this.type = 0; + this.length = this.buf.length; + } + + Pbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum + + Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64 + + Pbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields + + Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32 + + var SHIFT_LEFT_32 = (1 << 16) * (1 << 16), + SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32; // Threshold chosen based on both benchmarking and knowledge about browser string + // data structures (which currently switch structure types at 12 bytes or more) + + var TEXT_DECODER_MIN_LENGTH = 12; + var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8'); + Pbf.prototype = { + destroy: function destroy() { + this.buf = null; + }, + // === READING ================================================================= + readFields: function readFields(readField, result, end) { + end = end || this.length; + + while (this.pos < end) { + var val = this.readVarint(), + tag = val >> 3, + startPos = this.pos; + this.type = val & 0x7; + readField(tag, result, this); + if (this.pos === startPos) this.skip(val); + } + + return result; + }, + readMessage: function readMessage(readField, result) { + return this.readFields(readField, result, this.readVarint() + this.pos); + }, + readFixed32: function readFixed32() { + var val = readUInt32(this.buf, this.pos); + this.pos += 4; + return val; + }, + readSFixed32: function readSFixed32() { + var val = readInt32(this.buf, this.pos); + this.pos += 4; + return val; + }, + // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed) + readFixed64: function readFixed64() { + var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32; + this.pos += 8; + return val; + }, + readSFixed64: function readSFixed64() { + var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32; + this.pos += 8; + return val; + }, + readFloat: function readFloat() { + var val = ieee754.read(this.buf, this.pos, true, 23, 4); + this.pos += 4; + return val; + }, + readDouble: function readDouble() { + var val = ieee754.read(this.buf, this.pos, true, 52, 8); + this.pos += 8; + return val; + }, + readVarint: function readVarint(isSigned) { + var buf = this.buf, + val, + b; + b = buf[this.pos++]; + val = b & 0x7f; + if (b < 0x80) return val; + b = buf[this.pos++]; + val |= (b & 0x7f) << 7; + if (b < 0x80) return val; + b = buf[this.pos++]; + val |= (b & 0x7f) << 14; + if (b < 0x80) return val; + b = buf[this.pos++]; + val |= (b & 0x7f) << 21; + if (b < 0x80) return val; + b = buf[this.pos]; + val |= (b & 0x0f) << 28; + return readVarintRemainder(val, isSigned, this); + }, + readVarint64: function readVarint64() { + // for compatibility with v2.0.1 + return this.readVarint(true); + }, + readSVarint: function readSVarint() { + var num = this.readVarint(); + return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding + }, + readBoolean: function readBoolean() { + return Boolean(this.readVarint()); + }, + readString: function readString() { + var end = this.readVarint() + this.pos; + var pos = this.pos; + this.pos = end; + + if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) { + // longer strings are fast with the built-in browser TextDecoder API + return readUtf8TextDecoder(this.buf, pos, end); + } // short strings are fast with our custom implementation + + + return readUtf8(this.buf, pos, end); + }, + readBytes: function readBytes() { + var end = this.readVarint() + this.pos, + buffer = this.buf.subarray(this.pos, end); + this.pos = end; + return buffer; + }, + // verbose for performance reasons; doesn't affect gzipped size + readPackedVarint: function readPackedVarint(arr, isSigned) { + if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned)); + var end = readPackedEnd(this); + arr = arr || []; + + while (this.pos < end) { + arr.push(this.readVarint(isSigned)); + } + + return arr; + }, + readPackedSVarint: function readPackedSVarint(arr) { + if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint()); + var end = readPackedEnd(this); + arr = arr || []; + + while (this.pos < end) { + arr.push(this.readSVarint()); + } + + return arr; + }, + readPackedBoolean: function readPackedBoolean(arr) { + if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean()); + var end = readPackedEnd(this); + arr = arr || []; + + while (this.pos < end) { + arr.push(this.readBoolean()); + } + + return arr; + }, + readPackedFloat: function readPackedFloat(arr) { + if (this.type !== Pbf.Bytes) return arr.push(this.readFloat()); + var end = readPackedEnd(this); + arr = arr || []; + + while (this.pos < end) { + arr.push(this.readFloat()); + } + + return arr; + }, + readPackedDouble: function readPackedDouble(arr) { + if (this.type !== Pbf.Bytes) return arr.push(this.readDouble()); + var end = readPackedEnd(this); + arr = arr || []; + + while (this.pos < end) { + arr.push(this.readDouble()); + } + + return arr; + }, + readPackedFixed32: function readPackedFixed32(arr) { + if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32()); + var end = readPackedEnd(this); + arr = arr || []; + + while (this.pos < end) { + arr.push(this.readFixed32()); + } + + return arr; + }, + readPackedSFixed32: function readPackedSFixed32(arr) { + if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32()); + var end = readPackedEnd(this); + arr = arr || []; + + while (this.pos < end) { + arr.push(this.readSFixed32()); + } + + return arr; + }, + readPackedFixed64: function readPackedFixed64(arr) { + if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64()); + var end = readPackedEnd(this); + arr = arr || []; + + while (this.pos < end) { + arr.push(this.readFixed64()); + } + + return arr; + }, + readPackedSFixed64: function readPackedSFixed64(arr) { + if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64()); + var end = readPackedEnd(this); + arr = arr || []; + + while (this.pos < end) { + arr.push(this.readSFixed64()); + } + + return arr; + }, + skip: function skip(val) { + var type = val & 0x7; + if (type === Pbf.Varint) while (this.buf[this.pos++] > 0x7f) {} else if (type === Pbf.Bytes) this.pos = this.readVarint() + this.pos;else if (type === Pbf.Fixed32) this.pos += 4;else if (type === Pbf.Fixed64) this.pos += 8;else throw new Error('Unimplemented type: ' + type); + }, + // === WRITING ================================================================= + writeTag: function writeTag(tag, type) { + this.writeVarint(tag << 3 | type); + }, + realloc: function realloc(min) { + var length = this.length || 16; + + while (length < this.pos + min) { + length *= 2; + } + + if (length !== this.length) { + var buf = new Uint8Array(length); + buf.set(this.buf); + this.buf = buf; + this.length = length; + } + }, + finish: function finish() { + this.length = this.pos; + this.pos = 0; + return this.buf.subarray(0, this.length); + }, + writeFixed32: function writeFixed32(val) { + this.realloc(4); + writeInt32(this.buf, val, this.pos); + this.pos += 4; + }, + writeSFixed32: function writeSFixed32(val) { + this.realloc(4); + writeInt32(this.buf, val, this.pos); + this.pos += 4; + }, + writeFixed64: function writeFixed64(val) { + this.realloc(8); + writeInt32(this.buf, val & -1, this.pos); + writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4); + this.pos += 8; + }, + writeSFixed64: function writeSFixed64(val) { + this.realloc(8); + writeInt32(this.buf, val & -1, this.pos); + writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4); + this.pos += 8; + }, + writeVarint: function writeVarint(val) { + val = +val || 0; + + if (val > 0xfffffff || val < 0) { + writeBigVarint(val, this); + return; + } + + this.realloc(4); + this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0); + if (val <= 0x7f) return; + this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0); + if (val <= 0x7f) return; + this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0); + if (val <= 0x7f) return; + this.buf[this.pos++] = val >>> 7 & 0x7f; + }, + writeSVarint: function writeSVarint(val) { + this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2); + }, + writeBoolean: function writeBoolean(val) { + this.writeVarint(Boolean(val)); + }, + writeString: function writeString(str) { + str = String(str); + this.realloc(str.length * 4); + this.pos++; // reserve 1 byte for short string length + + var startPos = this.pos; // write the string directly to the buffer and see how much was written + + this.pos = writeUtf8(this.buf, str, this.pos); + var len = this.pos - startPos; + if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position + + this.pos = startPos - 1; + this.writeVarint(len); + this.pos += len; + }, + writeFloat: function writeFloat(val) { + this.realloc(4); + ieee754.write(this.buf, val, this.pos, true, 23, 4); + this.pos += 4; + }, + writeDouble: function writeDouble(val) { + this.realloc(8); + ieee754.write(this.buf, val, this.pos, true, 52, 8); + this.pos += 8; + }, + writeBytes: function writeBytes(buffer) { + var len = buffer.length; + this.writeVarint(len); + this.realloc(len); + + for (var i = 0; i < len; i++) { + this.buf[this.pos++] = buffer[i]; + } + }, + writeRawMessage: function writeRawMessage(fn, obj) { + this.pos++; // reserve 1 byte for short message length + // write the message directly to the buffer and see how much was written + + var startPos = this.pos; + fn(obj, this); + var len = this.pos - startPos; + if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position + + this.pos = startPos - 1; + this.writeVarint(len); + this.pos += len; + }, + writeMessage: function writeMessage(tag, fn, obj) { + this.writeTag(tag, Pbf.Bytes); + this.writeRawMessage(fn, obj); + }, + writePackedVarint: function writePackedVarint(tag, arr) { + if (arr.length) this.writeMessage(tag, _writePackedVarint, arr); + }, + writePackedSVarint: function writePackedSVarint(tag, arr) { + if (arr.length) this.writeMessage(tag, _writePackedSVarint, arr); + }, + writePackedBoolean: function writePackedBoolean(tag, arr) { + if (arr.length) this.writeMessage(tag, _writePackedBoolean, arr); + }, + writePackedFloat: function writePackedFloat(tag, arr) { + if (arr.length) this.writeMessage(tag, _writePackedFloat, arr); + }, + writePackedDouble: function writePackedDouble(tag, arr) { + if (arr.length) this.writeMessage(tag, _writePackedDouble, arr); + }, + writePackedFixed32: function writePackedFixed32(tag, arr) { + if (arr.length) this.writeMessage(tag, _writePackedFixed, arr); + }, + writePackedSFixed32: function writePackedSFixed32(tag, arr) { + if (arr.length) this.writeMessage(tag, _writePackedSFixed, arr); + }, + writePackedFixed64: function writePackedFixed64(tag, arr) { + if (arr.length) this.writeMessage(tag, _writePackedFixed2, arr); + }, + writePackedSFixed64: function writePackedSFixed64(tag, arr) { + if (arr.length) this.writeMessage(tag, _writePackedSFixed2, arr); + }, + writeBytesField: function writeBytesField(tag, buffer) { + this.writeTag(tag, Pbf.Bytes); + this.writeBytes(buffer); + }, + writeFixed32Field: function writeFixed32Field(tag, val) { + this.writeTag(tag, Pbf.Fixed32); + this.writeFixed32(val); + }, + writeSFixed32Field: function writeSFixed32Field(tag, val) { + this.writeTag(tag, Pbf.Fixed32); + this.writeSFixed32(val); + }, + writeFixed64Field: function writeFixed64Field(tag, val) { + this.writeTag(tag, Pbf.Fixed64); + this.writeFixed64(val); + }, + writeSFixed64Field: function writeSFixed64Field(tag, val) { + this.writeTag(tag, Pbf.Fixed64); + this.writeSFixed64(val); + }, + writeVarintField: function writeVarintField(tag, val) { + this.writeTag(tag, Pbf.Varint); + this.writeVarint(val); + }, + writeSVarintField: function writeSVarintField(tag, val) { + this.writeTag(tag, Pbf.Varint); + this.writeSVarint(val); + }, + writeStringField: function writeStringField(tag, str) { + this.writeTag(tag, Pbf.Bytes); + this.writeString(str); + }, + writeFloatField: function writeFloatField(tag, val) { + this.writeTag(tag, Pbf.Fixed32); + this.writeFloat(val); + }, + writeDoubleField: function writeDoubleField(tag, val) { + this.writeTag(tag, Pbf.Fixed64); + this.writeDouble(val); + }, + writeBooleanField: function writeBooleanField(tag, val) { + this.writeVarintField(tag, Boolean(val)); + } + }; + + function readVarintRemainder(l, s, p) { + var buf = p.buf, + h, + b; + b = buf[p.pos++]; + h = (b & 0x70) >> 4; + if (b < 0x80) return toNum(l, h, s); + b = buf[p.pos++]; + h |= (b & 0x7f) << 3; + if (b < 0x80) return toNum(l, h, s); + b = buf[p.pos++]; + h |= (b & 0x7f) << 10; + if (b < 0x80) return toNum(l, h, s); + b = buf[p.pos++]; + h |= (b & 0x7f) << 17; + if (b < 0x80) return toNum(l, h, s); + b = buf[p.pos++]; + h |= (b & 0x7f) << 24; + if (b < 0x80) return toNum(l, h, s); + b = buf[p.pos++]; + h |= (b & 0x01) << 31; + if (b < 0x80) return toNum(l, h, s); + throw new Error('Expected varint not more than 10 bytes'); + } + + function readPackedEnd(pbf) { + return pbf.type === Pbf.Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1; + } + + function toNum(low, high, isSigned) { + if (isSigned) { + return high * 0x100000000 + (low >>> 0); + } + + return (high >>> 0) * 0x100000000 + (low >>> 0); + } + + function writeBigVarint(val, pbf) { + var low, high; + + if (val >= 0) { + low = val % 0x100000000 | 0; + high = val / 0x100000000 | 0; + } else { + low = ~(-val % 0x100000000); + high = ~(-val / 0x100000000); + + if (low ^ 0xffffffff) { + low = low + 1 | 0; + } else { + low = 0; + high = high + 1 | 0; + } + } + + if (val >= 0x10000000000000000 || val < -0x10000000000000000) { + throw new Error('Given varint doesn\'t fit into 10 bytes'); + } + + pbf.realloc(10); + writeBigVarintLow(low, high, pbf); + writeBigVarintHigh(high, pbf); + } + + function writeBigVarintLow(low, high, pbf) { + pbf.buf[pbf.pos++] = low & 0x7f | 0x80; + low >>>= 7; + pbf.buf[pbf.pos++] = low & 0x7f | 0x80; + low >>>= 7; + pbf.buf[pbf.pos++] = low & 0x7f | 0x80; + low >>>= 7; + pbf.buf[pbf.pos++] = low & 0x7f | 0x80; + low >>>= 7; + pbf.buf[pbf.pos] = low & 0x7f; + } + + function writeBigVarintHigh(high, pbf) { + var lsb = (high & 0x07) << 4; + pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0); + if (!high) return; + pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); + if (!high) return; + pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); + if (!high) return; + pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); + if (!high) return; + pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); + if (!high) return; + pbf.buf[pbf.pos++] = high & 0x7f; + } + + function makeRoomForExtraLength(startPos, len, pbf) { + var extraLen = len <= 0x3fff ? 1 : len <= 0x1fffff ? 2 : len <= 0xfffffff ? 3 : Math.floor(Math.log(len) / (Math.LN2 * 7)); // if 1 byte isn't enough for encoding message length, shift the data to the right + + pbf.realloc(extraLen); + + for (var i = pbf.pos - 1; i >= startPos; i--) { + pbf.buf[i + extraLen] = pbf.buf[i]; + } + } + + function _writePackedVarint(arr, pbf) { + for (var i = 0; i < arr.length; i++) { + pbf.writeVarint(arr[i]); + } + } + + function _writePackedSVarint(arr, pbf) { + for (var i = 0; i < arr.length; i++) { + pbf.writeSVarint(arr[i]); + } + } + + function _writePackedFloat(arr, pbf) { + for (var i = 0; i < arr.length; i++) { + pbf.writeFloat(arr[i]); + } + } + + function _writePackedDouble(arr, pbf) { + for (var i = 0; i < arr.length; i++) { + pbf.writeDouble(arr[i]); + } + } + + function _writePackedBoolean(arr, pbf) { + for (var i = 0; i < arr.length; i++) { + pbf.writeBoolean(arr[i]); + } + } + + function _writePackedFixed(arr, pbf) { + for (var i = 0; i < arr.length; i++) { + pbf.writeFixed32(arr[i]); + } + } + + function _writePackedSFixed(arr, pbf) { + for (var i = 0; i < arr.length; i++) { + pbf.writeSFixed32(arr[i]); + } + } + + function _writePackedFixed2(arr, pbf) { + for (var i = 0; i < arr.length; i++) { + pbf.writeFixed64(arr[i]); + } + } + + function _writePackedSFixed2(arr, pbf) { + for (var i = 0; i < arr.length; i++) { + pbf.writeSFixed64(arr[i]); + } + } // Buffer code below from https://github.com/feross/buffer, MIT-licensed + + + function readUInt32(buf, pos) { + return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + buf[pos + 3] * 0x1000000; + } + + function writeInt32(buf, val, pos) { + buf[pos] = val; + buf[pos + 1] = val >>> 8; + buf[pos + 2] = val >>> 16; + buf[pos + 3] = val >>> 24; + } + + function readInt32(buf, pos) { + return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + (buf[pos + 3] << 24); + } + + function readUtf8(buf, pos, end) { + var str = ''; + var i = pos; + + while (i < end) { + var b0 = buf[i]; + var c = null; // codepoint + + var bytesPerSequence = b0 > 0xEF ? 4 : b0 > 0xDF ? 3 : b0 > 0xBF ? 2 : 1; + if (i + bytesPerSequence > end) break; + var b1, b2, b3; + + if (bytesPerSequence === 1) { + if (b0 < 0x80) { + c = b0; + } + } else if (bytesPerSequence === 2) { + b1 = buf[i + 1]; + + if ((b1 & 0xC0) === 0x80) { + c = (b0 & 0x1F) << 0x6 | b1 & 0x3F; + + if (c <= 0x7F) { + c = null; + } + } + } else if (bytesPerSequence === 3) { + b1 = buf[i + 1]; + b2 = buf[i + 2]; + + if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) { + c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | b2 & 0x3F; + + if (c <= 0x7FF || c >= 0xD800 && c <= 0xDFFF) { + c = null; + } + } + } else if (bytesPerSequence === 4) { + b1 = buf[i + 1]; + b2 = buf[i + 2]; + b3 = buf[i + 3]; + + if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) { + c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | b3 & 0x3F; + + if (c <= 0xFFFF || c >= 0x110000) { + c = null; + } + } + } + + if (c === null) { + c = 0xFFFD; + bytesPerSequence = 1; + } else if (c > 0xFFFF) { + c -= 0x10000; + str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800); + c = 0xDC00 | c & 0x3FF; + } + + str += String.fromCharCode(c); + i += bytesPerSequence; + } + + return str; + } + + function readUtf8TextDecoder(buf, pos, end) { + return utf8TextDecoder.decode(buf.subarray(pos, end)); + } + + function writeUtf8(buf, str, pos) { + for (var i = 0, c, lead; i < str.length; i++) { + c = str.charCodeAt(i); // code point + + if (c > 0xD7FF && c < 0xE000) { + if (lead) { + if (c < 0xDC00) { + buf[pos++] = 0xEF; + buf[pos++] = 0xBF; + buf[pos++] = 0xBD; + lead = c; + continue; + } else { + c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000; + lead = null; + } + } else { + if (c > 0xDBFF || i + 1 === str.length) { + buf[pos++] = 0xEF; + buf[pos++] = 0xBF; + buf[pos++] = 0xBD; + } else { + lead = c; + } + + continue; + } + } else if (lead) { + buf[pos++] = 0xEF; + buf[pos++] = 0xBF; + buf[pos++] = 0xBD; + lead = null; + } + + if (c < 0x80) { + buf[pos++] = c; + } else { + if (c < 0x800) { + buf[pos++] = c >> 0x6 | 0xC0; + } else { + if (c < 0x10000) { + buf[pos++] = c >> 0xC | 0xE0; + } else { + buf[pos++] = c >> 0x12 | 0xF0; + buf[pos++] = c >> 0xC & 0x3F | 0x80; + } + + buf[pos++] = c >> 0x6 & 0x3F | 0x80; + } + + buf[pos++] = c & 0x3F | 0x80; + } + } + + return pos; + } + + var vectorTile = {}; + + var pointGeometry = Point$1; + /** + * A standalone point geometry with useful accessor, comparison, and + * modification methods. + * + * @class Point + * @param {Number} x the x-coordinate. this could be longitude or screen + * pixels, or any other sort of unit. + * @param {Number} y the y-coordinate. this could be latitude or screen + * pixels, or any other sort of unit. + * @example + * var point = new Point(-77, 38); + */ + + function Point$1(x, y) { + this.x = x; + this.y = y; + } + + Point$1.prototype = { + /** + * Clone this point, returning a new point that can be modified + * without affecting the old one. + * @return {Point} the clone + */ + clone: function clone() { + return new Point$1(this.x, this.y); + }, + + /** + * Add this point's x & y coordinates to another point, + * yielding a new point. + * @param {Point} p the other point + * @return {Point} output point + */ + add: function add(p) { + return this.clone()._add(p); + }, + + /** + * Subtract this point's x & y coordinates to from point, + * yielding a new point. + * @param {Point} p the other point + * @return {Point} output point + */ + sub: function sub(p) { + return this.clone()._sub(p); + }, + + /** + * Multiply this point's x & y coordinates by point, + * yielding a new point. + * @param {Point} p the other point + * @return {Point} output point + */ + multByPoint: function multByPoint(p) { + return this.clone()._multByPoint(p); + }, + + /** + * Divide this point's x & y coordinates by point, + * yielding a new point. + * @param {Point} p the other point + * @return {Point} output point + */ + divByPoint: function divByPoint(p) { + return this.clone()._divByPoint(p); + }, + + /** + * Multiply this point's x & y coordinates by a factor, + * yielding a new point. + * @param {Point} k factor + * @return {Point} output point + */ + mult: function mult(k) { + return this.clone()._mult(k); + }, + + /** + * Divide this point's x & y coordinates by a factor, + * yielding a new point. + * @param {Point} k factor + * @return {Point} output point + */ + div: function div(k) { + return this.clone()._div(k); + }, + + /** + * Rotate this point around the 0, 0 origin by an angle a, + * given in radians + * @param {Number} a angle to rotate around, in radians + * @return {Point} output point + */ + rotate: function rotate(a) { + return this.clone()._rotate(a); + }, + + /** + * Rotate this point around p point by an angle a, + * given in radians + * @param {Number} a angle to rotate around, in radians + * @param {Point} p Point to rotate around + * @return {Point} output point + */ + rotateAround: function rotateAround(a, p) { + return this.clone()._rotateAround(a, p); + }, + + /** + * Multiply this point by a 4x1 transformation matrix + * @param {Array} m transformation matrix + * @return {Point} output point + */ + matMult: function matMult(m) { + return this.clone()._matMult(m); + }, + + /** + * Calculate this point but as a unit vector from 0, 0, meaning + * that the distance from the resulting point to the 0, 0 + * coordinate will be equal to 1 and the angle from the resulting + * point to the 0, 0 coordinate will be the same as before. + * @return {Point} unit vector point + */ + unit: function unit() { + return this.clone()._unit(); + }, + + /** + * Compute a perpendicular point, where the new y coordinate + * is the old x coordinate and the new x coordinate is the old y + * coordinate multiplied by -1 + * @return {Point} perpendicular point + */ + perp: function perp() { + return this.clone()._perp(); + }, + + /** + * Return a version of this point with the x & y coordinates + * rounded to integers. + * @return {Point} rounded point + */ + round: function round() { + return this.clone()._round(); + }, + + /** + * Return the magitude of this point: this is the Euclidean + * distance from the 0, 0 coordinate to this point's x and y + * coordinates. + * @return {Number} magnitude + */ + mag: function mag() { + return Math.sqrt(this.x * this.x + this.y * this.y); + }, + + /** + * Judge whether this point is equal to another point, returning + * true or false. + * @param {Point} other the other point + * @return {boolean} whether the points are equal + */ + equals: function equals(other) { + return this.x === other.x && this.y === other.y; + }, + + /** + * Calculate the distance from this point to another point + * @param {Point} p the other point + * @return {Number} distance + */ + dist: function dist(p) { + return Math.sqrt(this.distSqr(p)); + }, + + /** + * Calculate the distance from this point to another point, + * without the square root step. Useful if you're comparing + * relative distances. + * @param {Point} p the other point + * @return {Number} distance + */ + distSqr: function distSqr(p) { + var dx = p.x - this.x, + dy = p.y - this.y; + return dx * dx + dy * dy; + }, + + /** + * Get the angle from the 0, 0 coordinate to this point, in radians + * coordinates. + * @return {Number} angle + */ + angle: function angle() { + return Math.atan2(this.y, this.x); + }, + + /** + * Get the angle from this point to another point, in radians + * @param {Point} b the other point + * @return {Number} angle + */ + angleTo: function angleTo(b) { + return Math.atan2(this.y - b.y, this.x - b.x); + }, + + /** + * Get the angle between this point and another point, in radians + * @param {Point} b the other point + * @return {Number} angle + */ + angleWith: function angleWith(b) { + return this.angleWithSep(b.x, b.y); + }, + + /* + * Find the angle of the two vectors, solving the formula for + * the cross product a x b = |a||b|sin(θ) for θ. + * @param {Number} x the x-coordinate + * @param {Number} y the y-coordinate + * @return {Number} the angle in radians + */ + angleWithSep: function angleWithSep(x, y) { + return Math.atan2(this.x * y - this.y * x, this.x * x + this.y * y); + }, + _matMult: function _matMult(m) { + var x = m[0] * this.x + m[1] * this.y, + y = m[2] * this.x + m[3] * this.y; + this.x = x; + this.y = y; + return this; + }, + _add: function _add(p) { + this.x += p.x; + this.y += p.y; + return this; + }, + _sub: function _sub(p) { + this.x -= p.x; + this.y -= p.y; + return this; + }, + _mult: function _mult(k) { + this.x *= k; + this.y *= k; + return this; + }, + _div: function _div(k) { + this.x /= k; + this.y /= k; + return this; + }, + _multByPoint: function _multByPoint(p) { + this.x *= p.x; + this.y *= p.y; + return this; + }, + _divByPoint: function _divByPoint(p) { + this.x /= p.x; + this.y /= p.y; + return this; + }, + _unit: function _unit() { + this._div(this.mag()); + + return this; + }, + _perp: function _perp() { + var y = this.y; + this.y = this.x; + this.x = -y; + return this; + }, + _rotate: function _rotate(angle) { + var cos = Math.cos(angle), + sin = Math.sin(angle), + x = cos * this.x - sin * this.y, + y = sin * this.x + cos * this.y; + this.x = x; + this.y = y; + return this; + }, + _rotateAround: function _rotateAround(angle, p) { + var cos = Math.cos(angle), + sin = Math.sin(angle), + x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y), + y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y); + this.x = x; + this.y = y; + return this; + }, + _round: function _round() { + this.x = Math.round(this.x); + this.y = Math.round(this.y); + return this; + } + }; + /** + * Construct a point from an array if necessary, otherwise if the input + * is already a Point, or an unknown type, return it unchanged + * @param {Array|Point|*} a any kind of input value + * @return {Point} constructed point, or passed-through value. + * @example + * // this + * var point = Point.convert([0, 1]); + * // is equivalent to + * var point = new Point(0, 1); + */ + + Point$1.convert = function (a) { + if (a instanceof Point$1) { + return a; + } + + if (Array.isArray(a)) { + return new Point$1(a[0], a[1]); + } + + return a; + }; + + var Point = pointGeometry; + var vectortilefeature = VectorTileFeature$1; + + function VectorTileFeature$1(pbf, end, extent, keys, values) { + // Public + this.properties = {}; + this.extent = extent; + this.type = 0; // Private + + this._pbf = pbf; + this._geometry = -1; + this._keys = keys; + this._values = values; + pbf.readFields(readFeature, this, end); + } + + function readFeature(tag, feature, pbf) { + if (tag == 1) feature.id = pbf.readVarint();else if (tag == 2) readTag(pbf, feature);else if (tag == 3) feature.type = pbf.readVarint();else if (tag == 4) feature._geometry = pbf.pos; + } + + function readTag(pbf, feature) { + var end = pbf.readVarint() + pbf.pos; + + while (pbf.pos < end) { + var key = feature._keys[pbf.readVarint()], + value = feature._values[pbf.readVarint()]; + + feature.properties[key] = value; + } + } + + VectorTileFeature$1.types = ['Unknown', 'Point', 'LineString', 'Polygon']; + + VectorTileFeature$1.prototype.loadGeometry = function () { + var pbf = this._pbf; + pbf.pos = this._geometry; + var end = pbf.readVarint() + pbf.pos, + cmd = 1, + length = 0, + x = 0, + y = 0, + lines = [], + line; + + while (pbf.pos < end) { + if (length <= 0) { + var cmdLen = pbf.readVarint(); + cmd = cmdLen & 0x7; + length = cmdLen >> 3; + } + + length--; + + if (cmd === 1 || cmd === 2) { + x += pbf.readSVarint(); + y += pbf.readSVarint(); + + if (cmd === 1) { + // moveTo + if (line) lines.push(line); + line = []; + } + + line.push(new Point(x, y)); + } else if (cmd === 7) { + // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90 + if (line) { + line.push(line[0].clone()); // closePolygon + } + } else { + throw new Error('unknown command ' + cmd); + } + } + + if (line) lines.push(line); + return lines; + }; + + VectorTileFeature$1.prototype.bbox = function () { + var pbf = this._pbf; + pbf.pos = this._geometry; + var end = pbf.readVarint() + pbf.pos, + cmd = 1, + length = 0, + x = 0, + y = 0, + x1 = Infinity, + x2 = -Infinity, + y1 = Infinity, + y2 = -Infinity; + + while (pbf.pos < end) { + if (length <= 0) { + var cmdLen = pbf.readVarint(); + cmd = cmdLen & 0x7; + length = cmdLen >> 3; + } + + length--; + + if (cmd === 1 || cmd === 2) { + x += pbf.readSVarint(); + y += pbf.readSVarint(); + if (x < x1) x1 = x; + if (x > x2) x2 = x; + if (y < y1) y1 = y; + if (y > y2) y2 = y; + } else if (cmd !== 7) { + throw new Error('unknown command ' + cmd); + } + } + + return [x1, y1, x2, y2]; + }; + + VectorTileFeature$1.prototype.toGeoJSON = function (x, y, z) { + var size = this.extent * Math.pow(2, z), + x0 = this.extent * x, + y0 = this.extent * y, + coords = this.loadGeometry(), + type = VectorTileFeature$1.types[this.type], + i, + j; + + function project(line) { + for (var j = 0; j < line.length; j++) { + var p = line[j], + y2 = 180 - (p.y + y0) * 360 / size; + line[j] = [(p.x + x0) * 360 / size - 180, 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90]; + } + } + + switch (this.type) { + case 1: + var points = []; + + for (i = 0; i < coords.length; i++) { + points[i] = coords[i][0]; + } + + coords = points; + project(coords); + break; + + case 2: + for (i = 0; i < coords.length; i++) { + project(coords[i]); + } + + break; + + case 3: + coords = classifyRings(coords); + + for (i = 0; i < coords.length; i++) { + for (j = 0; j < coords[i].length; j++) { + project(coords[i][j]); + } + } + + break; + } + + if (coords.length === 1) { + coords = coords[0]; + } else { + type = 'Multi' + type; + } + + var result = { + type: "Feature", + geometry: { + type: type, + coordinates: coords + }, + properties: this.properties + }; + + if ('id' in this) { + result.id = this.id; + } + + return result; + }; // classifies an array of rings into polygons with outer rings and holes + + + function classifyRings(rings) { + var len = rings.length; + if (len <= 1) return [rings]; + var polygons = [], + polygon, + ccw; + + for (var i = 0; i < len; i++) { + var area = signedArea(rings[i]); + if (area === 0) continue; + if (ccw === undefined) ccw = area < 0; + + if (ccw === area < 0) { + if (polygon) polygons.push(polygon); + polygon = [rings[i]]; + } else { + polygon.push(rings[i]); + } + } + + if (polygon) polygons.push(polygon); + return polygons; + } + + function signedArea(ring) { + var sum = 0; + + for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) { + p1 = ring[i]; + p2 = ring[j]; + sum += (p2.x - p1.x) * (p1.y + p2.y); + } + + return sum; + } + + var VectorTileFeature = vectortilefeature; + var vectortilelayer = VectorTileLayer$1; + + function VectorTileLayer$1(pbf, end) { + // Public + this.version = 1; + this.name = null; + this.extent = 4096; + this.length = 0; // Private + + this._pbf = pbf; + this._keys = []; + this._values = []; + this._features = []; + pbf.readFields(readLayer, this, end); + this.length = this._features.length; + } + + function readLayer(tag, layer, pbf) { + if (tag === 15) layer.version = pbf.readVarint();else if (tag === 1) layer.name = pbf.readString();else if (tag === 5) layer.extent = pbf.readVarint();else if (tag === 2) layer._features.push(pbf.pos);else if (tag === 3) layer._keys.push(pbf.readString());else if (tag === 4) layer._values.push(readValueMessage(pbf)); + } + + function readValueMessage(pbf) { + var value = null, + end = pbf.readVarint() + pbf.pos; + + while (pbf.pos < end) { + var tag = pbf.readVarint() >> 3; + value = tag === 1 ? pbf.readString() : tag === 2 ? pbf.readFloat() : tag === 3 ? pbf.readDouble() : tag === 4 ? pbf.readVarint64() : tag === 5 ? pbf.readVarint() : tag === 6 ? pbf.readSVarint() : tag === 7 ? pbf.readBoolean() : null; + } + + return value; + } // return feature `i` from this layer as a `VectorTileFeature` + + + VectorTileLayer$1.prototype.feature = function (i) { + if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds'); + this._pbf.pos = this._features[i]; + + var end = this._pbf.readVarint() + this._pbf.pos; + + return new VectorTileFeature(this._pbf, end, this.extent, this._keys, this._values); + }; + + var VectorTileLayer = vectortilelayer; + var vectortile = VectorTile$1; + + function VectorTile$1(pbf, end) { + this.layers = pbf.readFields(readTile, {}, end); + } + + function readTile(tag, layers, pbf) { + if (tag === 3) { + var layer = new VectorTileLayer(pbf, pbf.readVarint() + pbf.pos); + if (layer.length) layers[layer.name] = layer; + } + } + + var VectorTile = vectorTile.VectorTile = vectortile; + vectorTile.VectorTileFeature = vectortilefeature; + vectorTile.VectorTileLayer = vectortilelayer; + + var accessToken = 'MLY|4100327730013843|5bb78b81720791946a9a7b956c57b7cf'; + var apiUrl = 'https://graph.mapillary.com/'; + var baseTileUrl = 'https://tiles.mapillary.com/maps/vtp'; + var mapFeatureTileUrl = "".concat(baseTileUrl, "/mly_map_feature_point/2/{z}/{x}/{y}?access_token=").concat(accessToken); + var tileUrl = "".concat(baseTileUrl, "/mly1_public/2/{z}/{x}/{y}?access_token=").concat(accessToken); + var trafficSignTileUrl = "".concat(baseTileUrl, "/mly_map_feature_traffic_sign/2/{z}/{x}/{y}?access_token=").concat(accessToken); + var viewercss = 'mapillary-js/mapillary.css'; + var viewerjs = 'mapillary-js/mapillary.js'; + var minZoom$1 = 14; + var dispatch$4 = dispatch$8('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'imageChanged'); + + var _loadViewerPromise$2; + + var _mlyActiveImage; + + var _mlyCache; + + var _mlyFallback = false; + + var _mlyHighlightedDetection; + + var _mlyShowFeatureDetections = false; + var _mlyShowSignDetections = false; + + var _mlyViewer; + + var _mlyViewerFilter = ['all']; // Load all data for the specified type from Mapillary vector tiles + + function loadTiles$2(which, url, maxZoom, projection) { + var tiler = utilTiler().zoomExtent([minZoom$1, maxZoom]).skipNullIsland(true); + var tiles = tiler.getTiles(projection); + tiles.forEach(function (tile) { + loadTile$1(which, url, tile); + }); + } // Load all data for the specified type from one vector tile + + + function loadTile$1(which, url, tile) { + var cache = _mlyCache.requests; + var tileId = "".concat(tile.id, "-").concat(which); + if (cache.loaded[tileId] || cache.inflight[tileId]) return; + var controller = new AbortController(); + cache.inflight[tileId] = controller; + var requestUrl = url.replace('{x}', tile.xyz[0]).replace('{y}', tile.xyz[1]).replace('{z}', tile.xyz[2]); + fetch(requestUrl, { + signal: controller.signal + }).then(function (response) { + if (!response.ok) { + throw new Error(response.status + ' ' + response.statusText); + } + + cache.loaded[tileId] = true; + delete cache.inflight[tileId]; + return response.arrayBuffer(); + }).then(function (data) { + if (!data) { + throw new Error('No Data'); + } + + loadTileDataToCache(data, tile, which); + + if (which === 'images') { + dispatch$4.call('loadedImages'); + } else if (which === 'signs') { + dispatch$4.call('loadedSigns'); + } else if (which === 'points') { + dispatch$4.call('loadedMapFeatures'); + } + })["catch"](function () { + cache.loaded[tileId] = true; + delete cache.inflight[tileId]; + }); + } // Load the data from the vector tile into cache + + + function loadTileDataToCache(data, tile, which) { + var vectorTile = new VectorTile(new pbf(data)); + var features, cache, layer, i, feature, loc, d; + + if (vectorTile.layers.hasOwnProperty('image')) { + features = []; + cache = _mlyCache.images; + layer = vectorTile.layers.image; + + for (i = 0; i < layer.length; i++) { + feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]); + loc = feature.geometry.coordinates; + d = { + loc: loc, + captured_at: feature.properties.captured_at, + ca: feature.properties.compass_angle, + id: feature.properties.id, + is_pano: feature.properties.is_pano, + sequence_id: feature.properties.sequence_id + }; + cache.forImageId[d.id] = d; + features.push({ + minX: loc[0], + minY: loc[1], + maxX: loc[0], + maxY: loc[1], + data: d + }); + } + + if (cache.rtree) { + cache.rtree.load(features); + } + } + + if (vectorTile.layers.hasOwnProperty('sequence')) { + features = []; + cache = _mlyCache.sequences; + layer = vectorTile.layers.sequence; + + for (i = 0; i < layer.length; i++) { + feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]); + + if (cache.lineString[feature.properties.id]) { + cache.lineString[feature.properties.id].push(feature); + } else { + cache.lineString[feature.properties.id] = [feature]; + } + } + } + + if (vectorTile.layers.hasOwnProperty('point')) { + features = []; + cache = _mlyCache[which]; + layer = vectorTile.layers.point; + + for (i = 0; i < layer.length; i++) { + feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]); + loc = feature.geometry.coordinates; + d = { + loc: loc, + id: feature.properties.id, + first_seen_at: feature.properties.first_seen_at, + last_seen_at: feature.properties.last_seen_at, + value: feature.properties.value + }; + features.push({ + minX: loc[0], + minY: loc[1], + maxX: loc[0], + maxY: loc[1], + data: d + }); + } + + if (cache.rtree) { + cache.rtree.load(features); + } + } + + if (vectorTile.layers.hasOwnProperty('traffic_sign')) { + features = []; + cache = _mlyCache[which]; + layer = vectorTile.layers.traffic_sign; + + for (i = 0; i < layer.length; i++) { + feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]); + loc = feature.geometry.coordinates; + d = { + loc: loc, + id: feature.properties.id, + first_seen_at: feature.properties.first_seen_at, + last_seen_at: feature.properties.last_seen_at, + value: feature.properties.value + }; + features.push({ + minX: loc[0], + minY: loc[1], + maxX: loc[0], + maxY: loc[1], + data: d + }); + } + + if (cache.rtree) { + cache.rtree.load(features); + } + } + } // Get data from the API + + + function loadData(url) { + return fetch(url).then(function (response) { + if (!response.ok) { + throw new Error(response.status + ' ' + response.statusText); + } + + return response.json(); + }).then(function (result) { + if (!result) { + return []; + } + + return result.data || []; + }); + } // Partition viewport into higher zoom tiles + + + function partitionViewport$2(projection) { + var z = geoScaleToZoom(projection.scale()); + var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5 + + var tiler = utilTiler().zoomExtent([z2, z2]); + return tiler.getTiles(projection).map(function (tile) { + return tile.extent; + }); + } // Return no more than `limit` results per partition. + + + function searchLimited$2(limit, projection, rtree) { + limit = limit || 5; + return partitionViewport$2(projection).reduce(function (result, extent) { + var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) { + return d.data; + }); + return found.length ? result.concat(found) : result; + }, []); + } + + var serviceMapillary = { + // Initialize Mapillary + init: function init() { + if (!_mlyCache) { + this.reset(); + } + + this.event = utilRebind(this, dispatch$4, 'on'); + }, + // Reset cache and state + reset: function reset() { + if (_mlyCache) { + Object.values(_mlyCache.requests.inflight).forEach(function (request) { + request.abort(); + }); + } + + _mlyCache = { + images: { + rtree: new RBush(), + forImageId: {} + }, + image_detections: { + forImageId: {} + }, + signs: { + rtree: new RBush() + }, + points: { + rtree: new RBush() + }, + sequences: { + rtree: new RBush(), + lineString: {} + }, + requests: { + loaded: {}, + inflight: {} + } + }; + _mlyActiveImage = null; + }, + // Get visible images + images: function images(projection) { + var limit = 5; + return searchLimited$2(limit, projection, _mlyCache.images.rtree); + }, + // Get visible traffic signs + signs: function signs(projection) { + var limit = 5; + return searchLimited$2(limit, projection, _mlyCache.signs.rtree); + }, + // Get visible map (point) features + mapFeatures: function mapFeatures(projection) { + var limit = 5; + return searchLimited$2(limit, projection, _mlyCache.points.rtree); + }, + // Get cached image by id + cachedImage: function cachedImage(imageId) { + return _mlyCache.images.forImageId[imageId]; + }, + // Get visible sequences + sequences: function sequences(projection) { + var viewport = projection.clipExtent(); + var min = [viewport[0][0], viewport[1][1]]; + var max = [viewport[1][0], viewport[0][1]]; + var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); + var sequenceIds = {}; + var lineStrings = []; + + _mlyCache.images.rtree.search(bbox).forEach(function (d) { + if (d.data.sequence_id) { + sequenceIds[d.data.sequence_id] = true; + } + }); + + Object.keys(sequenceIds).forEach(function (sequenceId) { + if (_mlyCache.sequences.lineString[sequenceId]) { + lineStrings = lineStrings.concat(_mlyCache.sequences.lineString[sequenceId]); + } + }); + return lineStrings; + }, + // Load images in the visible area + loadImages: function loadImages(projection) { + loadTiles$2('images', tileUrl, 14, projection); + }, + // Load traffic signs in the visible area + loadSigns: function loadSigns(projection) { + loadTiles$2('signs', trafficSignTileUrl, 14, projection); + }, + // Load map (point) features in the visible area + loadMapFeatures: function loadMapFeatures(projection) { + loadTiles$2('points', mapFeatureTileUrl, 14, projection); + }, + // Return a promise that resolves when the image viewer (Mapillary JS) library has finished loading + ensureViewerLoaded: function ensureViewerLoaded(context) { + if (_loadViewerPromise$2) return _loadViewerPromise$2; // add mly-wrapper + + var wrap = context.container().select('.photoviewer').selectAll('.mly-wrapper').data([0]); + wrap.enter().append('div').attr('id', 'ideditor-mly').attr('class', 'photo-wrapper mly-wrapper').classed('hide', true); + var that = this; + _loadViewerPromise$2 = new Promise(function (resolve, reject) { + var loadedCount = 0; + + function loaded() { + loadedCount += 1; // wait until both files are loaded + + if (loadedCount === 2) resolve(); + } + + var head = select('head'); // load mapillary-viewercss + + 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 () { + reject(); + }); // load mapillary-viewerjs + + 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 () { + reject(); + }); + })["catch"](function () { + _loadViewerPromise$2 = null; + }).then(function () { + that.initViewer(context); + }); + return _loadViewerPromise$2; + }, + // Load traffic sign image sprites + loadSignResources: function loadSignResources(context) { + context.ui().svgDefs.addSprites(['mapillary-sprite'], false + /* don't override colors */ + ); + return this; + }, + // Load map (point) feature image sprites + loadObjectResources: function loadObjectResources(context) { + context.ui().svgDefs.addSprites(['mapillary-object-sprite'], false + /* don't override colors */ + ); + return this; + }, + // Remove previous detections in image viewer + resetTags: function resetTags() { + if (_mlyViewer && !_mlyFallback) { + _mlyViewer.getComponent('tag').removeAll(); + } + }, + // Show map feature detections in image viewer + showFeatureDetections: function showFeatureDetections(value) { + _mlyShowFeatureDetections = value; + + if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) { + this.resetTags(); + } + }, + // Show traffic sign detections in image viewer + showSignDetections: function showSignDetections(value) { + _mlyShowSignDetections = value; + + if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) { + this.resetTags(); + } + }, + // Apply filter to image viewer + filterViewer: function filterViewer(context) { + var showsPano = context.photos().showsPanoramic(); + var showsFlat = context.photos().showsFlat(); + var fromDate = context.photos().fromDate(); + var toDate = context.photos().toDate(); + var filter = ['all']; + if (!showsPano) filter.push(['!=', 'cameraType', 'spherical']); + if (!showsFlat && showsPano) filter.push(['==', 'pano', true]); + + if (fromDate) { + filter.push(['>=', 'capturedAt', new Date(fromDate).getTime()]); + } + + if (toDate) { + filter.push(['>=', 'capturedAt', new Date(toDate).getTime()]); + } + + if (_mlyViewer) { + _mlyViewer.setFilter(filter); + } + + _mlyViewerFilter = filter; + return filter; + }, + // Make the image viewer visible + showViewer: function showViewer(context) { + var wrap = context.container().select('.photoviewer').classed('hide', false); + var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size(); + + if (isHidden && _mlyViewer) { + wrap.selectAll('.photo-wrapper:not(.mly-wrapper)').classed('hide', true); + wrap.selectAll('.photo-wrapper.mly-wrapper').classed('hide', false); + + _mlyViewer.resize(); + } + + return this; + }, + // Hide the image viewer and resets map markers + hideViewer: function hideViewer(context) { + _mlyActiveImage = null; + + if (!_mlyFallback && _mlyViewer) { + _mlyViewer.getComponent('sequence').stop(); + } + + var viewer = context.container().select('.photoviewer'); + if (!viewer.empty()) viewer.datum(null); + viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true); + this.updateUrlImage(null); + dispatch$4.call('imageChanged'); + dispatch$4.call('loadedMapFeatures'); + dispatch$4.call('loadedSigns'); + return this.setStyles(context, null); + }, + // Update the URL with current image id + updateUrlImage: function updateUrlImage(imageId) { + if (!window.mocha) { + var hash = utilStringQs(window.location.hash); + + if (imageId) { + hash.photo = 'mapillary/' + imageId; + } else { + delete hash.photo; + } + + window.location.replace('#' + utilQsString(hash, true)); + } + }, + // Highlight the detection in the viewer that is related to the clicked map feature + highlightDetection: function highlightDetection(detection) { + if (detection) { + _mlyHighlightedDetection = detection.id; + } + + return this; + }, + // Initialize image viewer (Mapillar JS) + initViewer: function initViewer(context) { + var that = this; + if (!window.mapillary) return; + var opts = { + accessToken: accessToken, + component: { + cover: false, + keyboard: false, + tag: true + }, + container: 'ideditor-mly' + }; // Disable components requiring WebGL support + + if (!mapillary.isSupported() && mapillary.isFallbackSupported()) { + _mlyFallback = true; + opts.component = { + cover: false, + direction: false, + imagePlane: false, + keyboard: false, + mouse: false, + sequence: false, + tag: false, + image: true, + // fallback + navigation: true // fallback + + }; + } + + _mlyViewer = new mapillary.Viewer(opts); + + _mlyViewer.on('image', imageChanged); + + _mlyViewer.on('bearing', bearingChanged); + + if (_mlyViewerFilter) { + _mlyViewer.setFilter(_mlyViewerFilter); + } // Register viewer resize handler + + + context.ui().photoviewer.on('resize.mapillary', function () { + if (_mlyViewer) _mlyViewer.resize(); + }); // imageChanged: called after the viewer has changed images and is ready. + + function imageChanged(node) { + that.resetTags(); + var image = node.image; + that.setActiveImage(image); + that.setStyles(context, null); + var loc = [image.originalLngLat.lng, image.originalLngLat.lat]; + context.map().centerEase(loc); + that.updateUrlImage(image.id); + + if (_mlyShowFeatureDetections || _mlyShowSignDetections) { + that.updateDetections(image.id, "".concat(apiUrl, "/").concat(image.id, "/detections?access_token=").concat(accessToken, "&fields=id,image,geometry,value")); + } + + dispatch$4.call('imageChanged'); + } // bearingChanged: called when the bearing changes in the image viewer. + + + function bearingChanged(e) { + dispatch$4.call('bearingChanged', undefined, e); + } + }, + // Move to an image + selectImage: function selectImage(context, imageId) { + if (_mlyViewer && imageId) { + _mlyViewer.moveTo(imageId)["catch"](function (e) { + console.error('mly3', e); // eslint-disable-line no-console + }); + } + + return this; + }, + // Return the currently displayed image + getActiveImage: function getActiveImage() { + return _mlyActiveImage; + }, + // Return a list of detection objects for the given id + getDetections: function getDetections(id) { + return loadData("".concat(apiUrl, "/").concat(id, "/detections?access_token=").concat(accessToken, "&fields=id,value,image")); + }, + // Set the currently visible image + setActiveImage: function setActiveImage(image) { + if (image) { + _mlyActiveImage = { + ca: image.originalCompassAngle, + id: image.id, + loc: [image.originalLngLat.lng, image.originalLngLat.lat], + is_pano: image.cameraType === 'spherical', + sequence_id: image.sequenceId + }; + } else { + _mlyActiveImage = null; + } + }, + // Update the currently highlighted sequence and selected bubble. + setStyles: function setStyles(context, hovered) { + var hoveredImageId = hovered && hovered.id; + var hoveredSequenceId = hovered && hovered.sequence_id; + var selectedSequenceId = _mlyActiveImage && _mlyActiveImage.sequence_id; + context.container().selectAll('.layer-mapillary .viewfield-group').classed('highlighted', function (d) { + return d.sequence_id === selectedSequenceId || d.id === hoveredImageId; + }).classed('hovered', function (d) { + return d.id === hoveredImageId; + }); + context.container().selectAll('.layer-mapillary .sequence').classed('highlighted', function (d) { + return d.properties.id === hoveredSequenceId; + }).classed('currentView', function (d) { + return d.properties.id === selectedSequenceId; + }); + return this; + }, + // Get detections for the current image and shows them in the image viewer + updateDetections: function updateDetections(imageId, url) { + if (!_mlyViewer || _mlyFallback) return; + if (!imageId) return; + var cache = _mlyCache.image_detections; + + if (cache.forImageId[imageId]) { + showDetections(_mlyCache.image_detections.forImageId[imageId]); + } else { + loadData(url).then(function (detections) { + detections.forEach(function (detection) { + if (!cache.forImageId[imageId]) { + cache.forImageId[imageId] = []; + } + + cache.forImageId[imageId].push({ + geometry: detection.geometry, + id: detection.id, + image_id: imageId, + value: detection.value + }); + }); + showDetections(_mlyCache.image_detections.forImageId[imageId] || []); + }); + } // Create a tag for each detection and shows it in the image viewer + + + function showDetections(detections) { + var tagComponent = _mlyViewer.getComponent('tag'); + + detections.forEach(function (data) { + var tag = makeTag(data); + + if (tag) { + tagComponent.add([tag]); + } + }); + } // Create a Mapillary JS tag object + + + function makeTag(data) { + var valueParts = data.value.split('--'); + if (!valueParts.length) return; + var tag; + var text; + var color = 0xffffff; + + if (_mlyHighlightedDetection === data.id) { + color = 0xffff00; + text = valueParts[1]; + + if (text === 'flat' || text === 'discrete' || text === 'sign') { + text = valueParts[2]; + } + + text = text.replace(/-/g, ' '); + text = text.charAt(0).toUpperCase() + text.slice(1); + _mlyHighlightedDetection = null; + } + + var decodedGeometry = window.atob(data.geometry); + var uintArray = new Uint8Array(decodedGeometry.length); + + for (var i = 0; i < decodedGeometry.length; i++) { + uintArray[i] = decodedGeometry.charCodeAt(i); + } + + var tile = new VectorTile(new pbf(uintArray.buffer)); + var layer = tile.layers['mpy-or']; + var geometries = layer.feature(0).loadGeometry(); + var polygon = geometries.map(function (ring) { + return ring.map(function (point) { + return [point.x / layer.extent, point.y / layer.extent]; + }); + }); + tag = new mapillary.OutlineTag(data.id, new mapillary.PolygonGeometry(polygon[0]), { + text: text, + textColor: color, + lineColor: color, + lineWidth: 2, + fillColor: color, + fillOpacity: 0.3 + }); + return tag; + } + }, + // Return the current cache + cache: function cache() { + return _mlyCache; + } + }; + + function validationIssue(attrs) { + this.type = attrs.type; // required - name of rule that created the issue (e.g. 'missing_tag') + + this.subtype = attrs.subtype; // optional - category of the issue within the type (e.g. 'relation_type' under 'missing_tag') + + this.severity = attrs.severity; // required - 'warning' or 'error' + + this.message = attrs.message; // required - function returning localized string + + this.reference = attrs.reference; // optional - function(selection) to render reference information + + this.entityIds = attrs.entityIds; // optional - array of IDs of entities involved in the issue + + this.loc = attrs.loc; // optional - [lon, lat] to zoom in on to see the issue + + this.data = attrs.data; // optional - object containing extra data for the fixes + + this.dynamicFixes = attrs.dynamicFixes; // optional - function(context) returning fixes + + this.hash = attrs.hash; // optional - string to further differentiate the issue + + this.id = generateID.apply(this); // generated - see below + + this.key = generateKey.apply(this); // generated - see below (call after generating this.id) + + this.autoFix = null; // generated - if autofix exists, will be set below + // A unique, deterministic string hash. + // Issues with identical id values are considered identical. + + function generateID() { + var parts = [this.type]; + + if (this.hash) { + // subclasses can pass in their own differentiator + parts.push(this.hash); + } + + if (this.subtype) { + parts.push(this.subtype); + } // include the entities this issue is for + // (sort them so the id is deterministic) + + + if (this.entityIds) { + var entityKeys = this.entityIds.slice().sort(); + parts.push.apply(parts, entityKeys); + } + + return parts.join(':'); + } // An identifier suitable for use as the second argument to d3.selection#data(). + // (i.e. this should change whenever the data needs to be refreshed) + + + function generateKey() { + return this.id + ':' + Date.now().toString(); // include time of creation + } + + this.extent = function (resolver) { + if (this.loc) { + return geoExtent(this.loc); + } + + if (this.entityIds && this.entityIds.length) { + return this.entityIds.reduce(function (extent, entityId) { + return extent.extend(resolver.entity(entityId).extent(resolver)); + }, geoExtent()); + } + + return null; + }; + + this.fixes = function (context) { + var fixes = this.dynamicFixes ? this.dynamicFixes(context) : []; + var issue = this; + + if (issue.severity === 'warning') { + // allow ignoring any issue that's not an error + fixes.push(new validationIssueFix({ + title: _t.html('issues.fix.ignore_issue.title'), + icon: 'iD-icon-close', + onClick: function onClick() { + context.validator().ignoreIssue(this.issue.id); + } + })); + } + + fixes.forEach(function (fix) { + // the id doesn't matter as long as it's unique to this issue/fix + fix.id = fix.title; // add a reference to the issue for use in actions + + fix.issue = issue; + + if (fix.autoArgs) { + issue.autoFix = fix; + } + }); + return fixes; + }; + } + function validationIssueFix(attrs) { + this.title = attrs.title; // Required + + this.onClick = attrs.onClick; // Optional - the function to run to apply the fix + + this.disabledReason = attrs.disabledReason; // Optional - a string explaining why the fix is unavailable, if any + + this.icon = attrs.icon; // Optional - shows 'iD-icon-wrench' if not set + + this.entityIds = attrs.entityIds || []; // Optional - used for hover-higlighting. + + this.autoArgs = attrs.autoArgs; // Optional - pass [actions, annotation] arglist if this fix can automatically run + + this.issue = null; // Generated link - added by validationIssue + } + + var buildRuleChecks = function buildRuleChecks() { + return { + equals: function equals(_equals) { + return function (tags) { + return Object.keys(_equals).every(function (k) { + return _equals[k] === tags[k]; + }); + }; + }, + notEquals: function notEquals(_notEquals) { + return function (tags) { + return Object.keys(_notEquals).some(function (k) { + return _notEquals[k] !== tags[k]; + }); + }; + }, + absence: function absence(_absence) { + return function (tags) { + return Object.keys(tags).indexOf(_absence) === -1; + }; + }, + presence: function presence(_presence) { + return function (tags) { + return Object.keys(tags).indexOf(_presence) > -1; + }; + }, + greaterThan: function greaterThan(_greaterThan) { + var key = Object.keys(_greaterThan)[0]; + var value = _greaterThan[key]; + return function (tags) { + return tags[key] > value; + }; + }, + greaterThanEqual: function greaterThanEqual(_greaterThanEqual) { + var key = Object.keys(_greaterThanEqual)[0]; + var value = _greaterThanEqual[key]; + return function (tags) { + return tags[key] >= value; + }; + }, + lessThan: function lessThan(_lessThan) { + var key = Object.keys(_lessThan)[0]; + var value = _lessThan[key]; + return function (tags) { + return tags[key] < value; + }; + }, + lessThanEqual: function lessThanEqual(_lessThanEqual) { + var key = Object.keys(_lessThanEqual)[0]; + var value = _lessThanEqual[key]; + return function (tags) { + return tags[key] <= value; + }; + }, + positiveRegex: function positiveRegex(_positiveRegex) { + var tagKey = Object.keys(_positiveRegex)[0]; + + var expression = _positiveRegex[tagKey].join('|'); + + var regex = new RegExp(expression); + return function (tags) { + return regex.test(tags[tagKey]); + }; + }, + negativeRegex: function negativeRegex(_negativeRegex) { + var tagKey = Object.keys(_negativeRegex)[0]; + + var expression = _negativeRegex[tagKey].join('|'); + + var regex = new RegExp(expression); + return function (tags) { + return !regex.test(tags[tagKey]); + }; + } + }; + }; + + var buildLineKeys = function buildLineKeys() { + return { + highway: { + rest_area: true, + services: true + }, + railway: { + roundhouse: true, + station: true, + traverser: true, + turntable: true, + wash: true + } + }; + }; + + var serviceMapRules = { + init: function init() { + this._ruleChecks = buildRuleChecks(); + this._validationRules = []; + this._areaKeys = osmAreaKeys; + this._lineKeys = buildLineKeys(); + }, + // list of rules only relevant to tag checks... + filterRuleChecks: function filterRuleChecks(selector) { + var _ruleChecks = this._ruleChecks; + return Object.keys(selector).reduce(function (rules, key) { + if (['geometry', 'error', 'warning'].indexOf(key) === -1) { + rules.push(_ruleChecks[key](selector[key])); + } + + return rules; + }, []); + }, + // builds tagMap from mapcss-parse selector object... + buildTagMap: function buildTagMap(selector) { + var getRegexValues = function getRegexValues(regexes) { + return regexes.map(function (regex) { + return regex.replace(/\$|\^/g, ''); + }); + }; + + var tagMap = Object.keys(selector).reduce(function (expectedTags, key) { + var values; + var isRegex = /regex/gi.test(key); + var isEqual = /equals/gi.test(key); + + if (isRegex || isEqual) { + Object.keys(selector[key]).forEach(function (selectorKey) { + values = isEqual ? [selector[key][selectorKey]] : getRegexValues(selector[key][selectorKey]); + + if (expectedTags.hasOwnProperty(selectorKey)) { + values = values.concat(expectedTags[selectorKey]); + } + + expectedTags[selectorKey] = values; + }); + } else if (/(greater|less)Than(Equal)?|presence/g.test(key)) { + var tagKey = /presence/.test(key) ? selector[key] : Object.keys(selector[key])[0]; + values = [selector[key][tagKey]]; + + if (expectedTags.hasOwnProperty(tagKey)) { + values = values.concat(expectedTags[tagKey]); + } + + expectedTags[tagKey] = values; + } + + return expectedTags; + }, {}); + return tagMap; + }, + // inspired by osmWay#isArea() + inferGeometry: function inferGeometry(tagMap) { + var _lineKeys = this._lineKeys; + var _areaKeys = this._areaKeys; + + var keyValueDoesNotImplyArea = function keyValueDoesNotImplyArea(key) { + return utilArrayIntersection(tagMap[key], Object.keys(_areaKeys[key])).length > 0; + }; + + var keyValueImpliesLine = function keyValueImpliesLine(key) { + return utilArrayIntersection(tagMap[key], Object.keys(_lineKeys[key])).length > 0; + }; + + if (tagMap.hasOwnProperty('area')) { + if (tagMap.area.indexOf('yes') > -1) { + return 'area'; + } + + if (tagMap.area.indexOf('no') > -1) { + return 'line'; + } + } + + for (var key in tagMap) { + if (key in _areaKeys && !keyValueDoesNotImplyArea(key)) { + return 'area'; + } + + if (key in _lineKeys && keyValueImpliesLine(key)) { + return 'area'; + } + } + + return 'line'; + }, + // adds from mapcss-parse selector check... + addRule: function addRule(selector) { + var rule = { + // checks relevant to mapcss-selector + checks: this.filterRuleChecks(selector), + // true if all conditions for a tag error are true.. + matches: function matches(entity) { + return this.checks.every(function (check) { + return check(entity.tags); + }); + }, + // borrowed from Way#isArea() + inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys), + geometryMatches: function geometryMatches(entity, graph) { + if (entity.type === 'node' || entity.type === 'relation') { + return selector.geometry === entity.type; + } else if (entity.type === 'way') { + return this.inferredGeometry === entity.geometry(graph); + } + }, + // when geometries match and tag matches are present, return a warning... + findIssues: function findIssues(entity, graph, issues) { + if (this.geometryMatches(entity, graph) && this.matches(entity)) { + var severity = Object.keys(selector).indexOf('error') > -1 ? 'error' : 'warning'; + var _message = selector[severity]; + issues.push(new validationIssue({ + type: 'maprules', + severity: severity, + message: function message() { + return _message; + }, + entityIds: [entity.id] + })); + } + } + }; + + this._validationRules.push(rule); + }, + clearRules: function clearRules() { + this._validationRules = []; + }, + // returns validationRules... + validationRules: function validationRules() { + return this._validationRules; + }, + // returns ruleChecks + ruleChecks: function ruleChecks() { + return this._ruleChecks; + } + }; + + var apibase$2 = 'https://nominatim.openstreetmap.org/'; + var _inflight$2 = {}; + + var _nominatimCache; + + var serviceNominatim = { + init: function init() { + _inflight$2 = {}; + _nominatimCache = new RBush(); + }, + reset: function reset() { + Object.values(_inflight$2).forEach(function (controller) { + controller.abort(); + }); + _inflight$2 = {}; + _nominatimCache = new RBush(); + }, + countryCode: function countryCode(location, callback) { + this.reverse(location, function (err, result) { + if (err) { + return callback(err); + } else if (result.address) { + return callback(null, result.address.country_code); + } else { + return callback('Unable to geocode', null); + } + }); + }, + reverse: function reverse(loc, callback) { + var cached = _nominatimCache.search({ + minX: loc[0], + minY: loc[1], + maxX: loc[0], + maxY: loc[1] + }); + + if (cached.length > 0) { + if (callback) callback(null, cached[0].data); + return; + } + + var params = { + zoom: 13, + format: 'json', + addressdetails: 1, + lat: loc[1], + lon: loc[0] + }; + var url = apibase$2 + 'reverse?' + utilQsString(params); + if (_inflight$2[url]) return; + var controller = new AbortController(); + _inflight$2[url] = controller; + d3_json(url, { + signal: controller.signal + }).then(function (result) { + delete _inflight$2[url]; + + if (result && result.error) { + throw new Error(result.error); + } + + var extent = geoExtent(loc).padByMeters(200); + + _nominatimCache.insert(Object.assign(extent.bbox(), { + data: result + })); + + if (callback) callback(null, result); + })["catch"](function (err) { + delete _inflight$2[url]; + if (err.name === 'AbortError') return; + if (callback) callback(err.message); + }); + }, + search: function search(val, callback) { + var searchVal = encodeURIComponent(val); + var url = apibase$2 + 'search/' + searchVal + '?limit=10&format=json'; + if (_inflight$2[url]) return; + var controller = new AbortController(); + _inflight$2[url] = controller; + d3_json(url, { + signal: controller.signal + }).then(function (result) { + delete _inflight$2[url]; + + if (result && result.error) { + throw new Error(result.error); + } + + if (callback) callback(null, result); + })["catch"](function (err) { + delete _inflight$2[url]; + if (err.name === 'AbortError') return; + if (callback) callback(err.message); + }); + } + }; + + // for punction see https://stackoverflow.com/a/21224179 + + function simplify$1(str) { + if (typeof str !== 'string') return ''; + return diacritics.remove(str.replace(/&/g, 'and').replace(/İ/ig, 'i') // for BİM, İşbank - #5017 + .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()); + } + + 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"]}; + var matchGroupsJSON = { + matchGroups: matchGroups$1 + }; + + 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$","^церковная( лавка)?$"]; + var genericWordsJSON = { + genericWords: genericWords + }; + + 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+)$"}}}; + var treesJSON = { + trees: trees$1 + }; + + var matchGroups = matchGroupsJSON.matchGroups; + var trees = treesJSON.trees; + var Matcher = /*#__PURE__*/function () { + // + // `constructor` + // initialize the genericWords regexes + function Matcher() { + var _this = this; + + _classCallCheck$1(this, Matcher); + + // The `matchIndex` is a specialized structure that allows us to quickly answer + // _"Given a [key/value tagpair, name, location], what canonical items (brands etc) can match it?"_ + // + // The index contains all valid combinations of k/v tagpairs and names + // matchIndex: + // { + // 'k/v': { + // 'primary': Map (String 'nsimple' -> Set (itemIDs…), // matches for tags like `name`, `name:xx`, etc. + // 'alternate': Map (String 'nsimple' -> Set (itemIDs…), // matches for tags like `alt_name`, `brand`, etc. + // 'excludeNamed': Map (String 'pattern' -> RegExp), + // 'excludeGeneric': Map (String 'pattern' -> RegExp) + // }, + // } + // + // { + // 'amenity/bank': { + // 'primary': { + // 'firstbank': Set ("firstbank-978cca", "firstbank-9794e6", "firstbank-f17495", …), + // … + // }, + // 'alternate': { + // '1stbank': Set ("firstbank-f17495"), + // … + // } + // }, + // 'shop/supermarket': { + // 'primary': { + // 'coop': Set ("coop-76454b", "coop-ebf2d9", "coop-36e991", …), + // 'coopfood': Set ("coopfood-a8278b", …), + // … + // }, + // 'alternate': { + // 'coop': Set ("coopfood-a8278b", …), + // 'federatedcooperatives': Set ("coop-76454b", …), + // 'thecooperative': Set ("coopfood-a8278b", …), + // … + // } + // } + // } + // + this.matchIndex = undefined; // The `genericWords` structure matches the contents of genericWords.json to instantiated RegExp objects + // Map (String 'pattern' -> RegExp), + + this.genericWords = new Map(); + (genericWordsJSON.genericWords || []).forEach(function (s) { + return _this.genericWords.set(s, new RegExp(s, 'i')); + }); // The `itemLocation` structure maps itemIDs to locationSetIDs: + // { + // 'firstbank-f17495': '+[first_bank_western_us.geojson]', + // 'firstbank-978cca': '+[first_bank_carolinas.geojson]', + // 'coop-76454b': '+[Q16]', + // 'coopfood-a8278b': '+[Q23666]', + // … + // } + + this.itemLocation = undefined; // The `locationSets` structure maps locationSetIDs to *resolved* locationSets: + // { + // '+[first_bank_western_us.geojson]': GeoJSON {…}, + // '+[first_bank_carolinas.geojson]': GeoJSON {…}, + // '+[Q16]': GeoJSON {…}, + // '+[Q23666]': GeoJSON {…}, + // … + // } + + this.locationSets = undefined; // The `locationIndex` is an instance of which-polygon spatial index for the locationSets. + + this.locationIndex = undefined; // Array of match conflict pairs (currently unused) + + this.warnings = []; + } // + // `buildMatchIndex()` + // Call this to prepare the matcher for use + // + // `data` needs to be an Object indexed on a 'tree/key/value' path. + // (e.g. cache filled by `fileTree.read` or data found in `dist/nsi.json`) + // { + // 'brands/amenity/bank': { properties: {}, items: [ {}, {}, … ] }, + // 'brands/amenity/bar': { properties: {}, items: [ {}, {}, … ] }, + // … + // } + // + + + _createClass$1(Matcher, [{ + key: "buildMatchIndex", + value: function buildMatchIndex(data) { + var that = this; + if (that.matchIndex) return; // it was built already + + that.matchIndex = new Map(); + Object.keys(data).forEach(function (tkv) { + var category = data[tkv]; + var parts = tkv.split('/', 3); // tkv = "tree/key/value" + + var t = parts[0]; + var k = parts[1]; + var v = parts[2]; + var thiskv = "".concat(k, "/").concat(v); + var tree = trees[t]; + var branch = that.matchIndex.get(thiskv); + + if (!branch) { + branch = { + primary: new Map(), + alternate: new Map(), + excludeGeneric: new Map(), + excludeNamed: new Map() + }; + that.matchIndex.set(thiskv, branch); + } // ADD EXCLUSIONS + + + var properties = category.properties || {}; + var exclude = properties.exclude || {}; + (exclude.generic || []).forEach(function (s) { + return branch.excludeGeneric.set(s, new RegExp(s, 'i')); + }); + (exclude.named || []).forEach(function (s) { + return branch.excludeNamed.set(s, new RegExp(s, 'i')); + }); + var excludeRegexes = [].concat(_toConsumableArray(branch.excludeGeneric.values()), _toConsumableArray(branch.excludeNamed.values())); // ADD ITEMS + + var items = category.items; + if (!Array.isArray(items) || !items.length) return; // Primary name patterns, match tags to take first + // e.g. `name`, `name:ru` + + var primaryName = new RegExp(tree.nameTags.primary, 'i'); // Alternate name patterns, match tags to consider after primary + // e.g. `alt_name`, `short_name`, `brand`, `brand:ru`, etc.. + + var alternateName = new RegExp(tree.nameTags.alternate, 'i'); // There are a few exceptions to the name matching regexes. + // Usually a tag suffix contains a language code like `name:en`, `name:ru` + // but we want to exclude things like `operator:type`, `name:etymology`, etc.. + + 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` + + var skipGenericKV = skipGenericKVMatches(t, k, v); // We will collect the generic KV pairs anyway (for the purpose of filtering them out of matchTags) + + var genericKV = new Set(["".concat(k, "/yes"), "building/yes"]); // Collect alternate tagpairs for this kv category from matchGroups. + // We might also pick up a few more generic KVs (like `shop/yes`) + + var matchGroupKV = new Set(); + Object.values(matchGroups).forEach(function (matchGroup) { + var inGroup = matchGroup.some(function (otherkv) { + return otherkv === thiskv; + }); + if (!inGroup) return; + matchGroup.forEach(function (otherkv) { + if (otherkv === thiskv) return; // skip self + + matchGroupKV.add(otherkv); + var otherk = otherkv.split('/', 2)[0]; // we might pick up a `shop/yes` + + genericKV.add("".concat(otherk, "/yes")); + }); + }); // For each item, insert all [key, value, name] combinations into the match index + + items.forEach(function (item) { + if (!item.id) return; // Automatically remove redundant `matchTags` - #3417 + // (i.e. This kv is already covered by matchGroups, so it doesn't need to be in `item.matchTags`) + + if (Array.isArray(item.matchTags) && item.matchTags.length) { + item.matchTags = item.matchTags.filter(function (matchTag) { + return !matchGroupKV.has(matchTag) && !genericKV.has(matchTag); + }); + if (!item.matchTags.length) delete item.matchTags; + } // key/value tagpairs to insert into the match index.. + + + var kvTags = ["".concat(thiskv)].concat(item.matchTags || []); + + if (!skipGenericKV) { + kvTags = kvTags.concat(Array.from(genericKV)); // #3454 - match some generic tags + } // Index all the namelike tag values + + + Object.keys(item.tags).forEach(function (osmkey) { + if (notName.test(osmkey)) return; // osmkey is not a namelike tag, skip + + var osmvalue = item.tags[osmkey]; + if (!osmvalue || excludeRegexes.some(function (regex) { + return regex.test(osmvalue); + })) return; // osmvalue missing or excluded + + if (primaryName.test(osmkey)) { + kvTags.forEach(function (kv) { + return insertName('primary', kv, simplify$1(osmvalue), item.id); + }); + } else if (alternateName.test(osmkey)) { + kvTags.forEach(function (kv) { + return insertName('alternate', kv, simplify$1(osmvalue), item.id); + }); + } + }); // Index `matchNames` after indexing all other names.. + + var keepMatchNames = new Set(); + (item.matchNames || []).forEach(function (matchName) { + // If this matchname isn't already indexed, add it to the alternate index + var nsimple = simplify$1(matchName); + kvTags.forEach(function (kv) { + var branch = that.matchIndex.get(kv); + var primaryLeaf = branch && branch.primary.get(nsimple); + var alternateLeaf = branch && branch.alternate.get(nsimple); + var inPrimary = primaryLeaf && primaryLeaf.has(item.id); + var inAlternate = alternateLeaf && alternateLeaf.has(item.id); + + if (!inPrimary && !inAlternate) { + insertName('alternate', kv, nsimple, item.id); + keepMatchNames.add(matchName); + } + }); + }); // Automatically remove redundant `matchNames` - #3417 + // (i.e. This name got indexed some other way, so it doesn't need to be in `item.matchNames`) + + if (keepMatchNames.size) { + item.matchNames = Array.from(keepMatchNames); + } else { + delete item.matchNames; + } + }); // each item + }); // each tkv + // Insert this item into the matchIndex + + function insertName(which, kv, nsimple, itemID) { + if (!nsimple) return; + var branch = that.matchIndex.get(kv); + + if (!branch) { + branch = { + primary: new Map(), + alternate: new Map(), + excludeGeneric: new Map(), + excludeNamed: new Map() + }; + that.matchIndex.set(kv, branch); + } + + var leaf = branch[which].get(nsimple); + + if (!leaf) { + leaf = new Set(); + branch[which].set(nsimple, leaf); + } + + leaf.add(itemID); // insert + } // For certain categories we do not want to match generic KV pairs like `building/yes` or `amenity/yes` + + + function skipGenericKVMatches(t, k, v) { + 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'; + } + } // + // `buildLocationIndex()` + // Call this to prepare a which-polygon location index. + // This *resolves* all the locationSets into GeoJSON, which takes some time. + // You can skip this step if you don't care about matching within a location. + // + // `data` needs to be an Object indexed on a 'tree/key/value' path. + // (e.g. cache filled by `fileTree.read` or data found in `dist/nsi.json`) + // { + // 'brands/amenity/bank': { properties: {}, items: [ {}, {}, … ] }, + // 'brands/amenity/bar': { properties: {}, items: [ {}, {}, … ] }, + // … + // } + // + + }, { + key: "buildLocationIndex", + value: function buildLocationIndex(data, loco) { + var that = this; + if (that.locationIndex) return; // it was built already + + that.itemLocation = new Map(); + that.locationSets = new Map(); + Object.keys(data).forEach(function (tkv) { + var items = data[tkv].items; + if (!Array.isArray(items) || !items.length) return; + items.forEach(function (item) { + if (that.itemLocation.has(item.id)) return; // we've seen item id already - shouldn't be possible? + + var resolved; + + try { + resolved = loco.resolveLocationSet(item.locationSet); // resolve a feature for this locationSet + } catch (err) { + console.warn("buildLocationIndex: ".concat(err.message)); // couldn't resolve + } + + if (!resolved || !resolved.id) return; + that.itemLocation.set(item.id, resolved.id); // link it to the item + + if (that.locationSets.has(resolved.id)) return; // we've seen this locationSet feature before.. + // First time seeing this locationSet feature, make a copy and add to locationSet cache.. + + var feature = _cloneDeep(resolved.feature); + + feature.id = resolved.id; // Important: always use the locationSet `id` (`+[Q30]`), not the feature `id` (`Q30`) + + feature.properties.id = resolved.id; + + if (!feature.geometry.coordinates.length || !feature.properties.area) { + console.warn("buildLocationIndex: locationSet ".concat(resolved.id, " for ").concat(item.id, " resolves to an empty feature:")); + console.warn(JSON.stringify(feature)); + return; + } + + that.locationSets.set(resolved.id, feature); + }); + }); + that.locationIndex = whichPolygon_1({ + type: 'FeatureCollection', + features: _toConsumableArray(that.locationSets.values()) + }); + + function _cloneDeep(obj) { + return JSON.parse(JSON.stringify(obj)); + } + } // + // `match()` + // Pass parts and return an Array of matches. + // `k` - key + // `v` - value + // `n` - namelike + // `loc` - optional - [lon,lat] location to search + // + // 1. If the [k,v,n] tuple matches a canonical item… + // Return an Array of match results. + // Each result will include the area in km² that the item is valid. + // + // Order of results: + // Primary ordering will be on the "match" column: + // "primary" - where the query matches the `name` tag, followed by + // "alternate" - where the query matches an alternate name tag (e.g. short_name, brand, operator, etc) + // Secondary ordering will be on the "area" column: + // "area descending" if no location was provided, (worldwide before local) + // "area ascending" if location was provided (local before worldwide) + // + // [ + // { match: 'primary', itemID: String, area: Number, kv: String, nsimple: String }, + // { match: 'primary', itemID: String, area: Number, kv: String, nsimple: String }, + // { match: 'alternate', itemID: String, area: Number, kv: String, nsimple: String }, + // { match: 'alternate', itemID: String, area: Number, kv: String, nsimple: String }, + // … + // ] + // + // -or- + // + // 2. If the [k,v,n] tuple matches an exclude pattern… + // Return an Array with a single exclude result, either + // + // [ { match: 'excludeGeneric', pattern: String, kv: String } ] // "generic" e.g. "Food Court" + // or + // [ { match: 'excludeNamed', pattern: String, kv: String } ] // "named", e.g. "Kebabai" + // + // About results + // "generic" - a generic word that is probably not really a name. + // For these, iD should warn the user "Hey don't put 'food court' in the name tag". + // "named" - a real name like "Kebabai" that is just common, but not a brand. + // 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. + // + // -or- + // + // 3. If the [k,v,n] tuple matches nothing of any kind, return `null` + // + // + + }, { + key: "match", + value: function match(k, v, n, loc) { + var that = this; + + if (!that.matchIndex) { + throw new Error('match: matchIndex not built.'); + } // If we were supplied a location, and a that.locationIndex has been set up, + // get the locationSets that are valid there so we can filter results. + + + var matchLocations; + + if (Array.isArray(loc) && that.locationIndex) { + // which-polygon query returns an array of GeoJSON properties, pass true to return all results + matchLocations = that.locationIndex([loc[0], loc[1], loc[0], loc[1]], true); + } + + var nsimple = simplify$1(n); + var seen = new Set(); + var results = []; + gatherResults('primary'); + gatherResults('alternate'); + if (results.length) return results; + gatherResults('exclude'); + return results.length ? results : null; + + function gatherResults(which) { + // First try an exact match on k/v + var kv = "".concat(k, "/").concat(v); + var didMatch = tryMatch(which, kv); + if (didMatch) return; // If that didn't work, look in match groups for other pairs considered equivalent to k/v.. + + for (var mg in matchGroups) { + var matchGroup = matchGroups[mg]; + var inGroup = matchGroup.some(function (otherkv) { + return otherkv === kv; + }); + if (!inGroup) continue; + + for (var i = 0; i < matchGroup.length; i++) { + var otherkv = matchGroup[i]; + if (otherkv === kv) continue; // skip self + + didMatch = tryMatch(which, otherkv); + if (didMatch) return; + } + } // If finished 'exclude' pass and still haven't matched anything, try the global `genericWords.json` patterns + + + if (which === 'exclude') { + var regex = _toConsumableArray(that.genericWords.values()).find(function (regex) { + return regex.test(n); + }); + + if (regex) { + results.push({ + match: 'excludeGeneric', + pattern: String(regex) + }); // note no `branch`, no `kv` + + return; + } + } + } + + function tryMatch(which, kv) { + var branch = that.matchIndex.get(kv); + if (!branch) return; + + if (which === 'exclude') { + // Test name `n` against named and generic exclude patterns + var regex = _toConsumableArray(branch.excludeNamed.values()).find(function (regex) { + return regex.test(n); + }); + + if (regex) { + results.push({ + match: 'excludeNamed', + pattern: String(regex), + kv: kv + }); + return; + } + + regex = _toConsumableArray(branch.excludeGeneric.values()).find(function (regex) { + return regex.test(n); + }); + + if (regex) { + results.push({ + match: 'excludeGeneric', + pattern: String(regex), + kv: kv + }); + return; + } + + return; + } + + var leaf = branch[which].get(nsimple); + if (!leaf || !leaf.size) return; // If we get here, we matched something.. + // Prepare the results, calculate areas (if location index was set up) + + var hits = Array.from(leaf).map(function (itemID) { + var area = Infinity; + + if (that.itemLocation && that.locationSets) { + var location = that.locationSets.get(that.itemLocation.get(itemID)); + area = location && location.properties.area || Infinity; + } + + return { + match: which, + itemID: itemID, + area: area, + kv: kv, + nsimple: nsimple + }; + }); + var sortFn = byAreaDescending; // Filter the match to include only results valid in the requested `loc`.. + + if (matchLocations) { + hits = hits.filter(isValidLocation); + sortFn = byAreaAscending; + } + + if (!hits.length) return; // push results + + hits.sort(sortFn).forEach(function (hit) { + if (seen.has(hit.itemID)) return; + seen.add(hit.itemID); + results.push(hit); + }); + return true; + + function isValidLocation(hit) { + if (!that.itemLocation) return true; + return matchLocations.find(function (props) { + return props.id === that.itemLocation.get(hit.itemID); + }); + } // Sort smaller (more local) locations first. + + + function byAreaAscending(hitA, hitB) { + return hitA.area - hitB.area; + } // Sort larger (more worldwide) locations first. + + + function byAreaDescending(hitA, hitB) { + return hitB.area - hitA.area; + } + } + } // + // `getWarnings()` + // Return any warnings discovered when buiding the index. + // (currently this does nothing) + // + + }, { + key: "getWarnings", + value: function getWarnings() { + return this.warnings; + } + }]); + + return Matcher; + }(); + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject$2(value) { + var type = _typeof(value); + + return value != null && (type == 'object' || type == 'function'); + } + + /** Detect free variable `global` from Node.js. */ + var freeGlobal = (typeof global === "undefined" ? "undefined" : _typeof(global)) == 'object' && global && global.Object === Object && global; + + /** Detect free variable `self`. */ + + var freeSelf = (typeof self === "undefined" ? "undefined" : _typeof(self)) == 'object' && self && self.Object === Object && self; + /** Used as a reference to the global object. */ + + var root = freeGlobal || freeSelf || Function('return this')(); + + /** + * Gets the timestamp of the number of milliseconds that have elapsed since + * the Unix epoch (1 January 1970 00:00:00 UTC). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Date + * @returns {number} Returns the timestamp. + * @example + * + * _.defer(function(stamp) { + * console.log(_.now() - stamp); + * }, _.now()); + * // => Logs the number of milliseconds it took for the deferred invocation. + */ + + var now = function now() { + return root.Date.now(); + }; + + /** Used to match a single whitespace character. */ + var reWhitespace = /\s/; + /** + * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace + * character of `string`. + * + * @private + * @param {string} string The string to inspect. + * @returns {number} Returns the index of the last non-whitespace character. + */ + + function trimmedEndIndex(string) { + var index = string.length; + + while (index-- && reWhitespace.test(string.charAt(index))) {} + + return index; + } + + /** Used to match leading whitespace. */ + + var reTrimStart = /^\s+/; + /** + * The base implementation of `_.trim`. + * + * @private + * @param {string} string The string to trim. + * @returns {string} Returns the trimmed string. + */ + + function baseTrim(string) { + return string ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '') : string; + } + + /** Built-in value references. */ + + var _Symbol = root.Symbol; + + /** Used for built-in method references. */ + + var objectProto$1 = Object.prototype; + /** Used to check objects for own properties. */ + + var hasOwnProperty$2 = objectProto$1.hasOwnProperty; + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + + var nativeObjectToString$1 = objectProto$1.toString; + /** Built-in value references. */ + + var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined; + /** + * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the raw `toStringTag`. + */ + + function getRawTag(value) { + var isOwn = hasOwnProperty$2.call(value, symToStringTag$1), + tag = value[symToStringTag$1]; + + try { + value[symToStringTag$1] = undefined; + var unmasked = true; + } catch (e) {} + + var result = nativeObjectToString$1.call(value); + + if (unmasked) { + if (isOwn) { + value[symToStringTag$1] = tag; + } else { + delete value[symToStringTag$1]; + } + } + + return result; + } + + /** Used for built-in method references. */ + var objectProto = Object.prototype; + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + + var nativeObjectToString = objectProto.toString; + /** + * Converts `value` to a string using `Object.prototype.toString`. + * + * @private + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + */ + + function objectToString(value) { + return nativeObjectToString.call(value); + } + + /** `Object#toString` result references. */ + + var nullTag = '[object Null]', + undefinedTag = '[object Undefined]'; + /** Built-in value references. */ + + var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined; + /** + * The base implementation of `getTag` without fallbacks for buggy environments. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + + function baseGetTag(value) { + if (value == null) { + return value === undefined ? undefinedTag : nullTag; + } + + return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return value != null && _typeof(value) == 'object'; + } + + /** `Object#toString` result references. */ + + var symbolTag = '[object Symbol]'; + /** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ + + function isSymbol(value) { + return _typeof(value) == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag; + } + + /** Used as references for various `Number` constants. */ + + var NAN = 0 / 0; + /** Used to detect bad signed hexadecimal string values. */ + + var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + /** Used to detect binary string values. */ + + var reIsBinary = /^0b[01]+$/i; + /** Used to detect octal string values. */ + + var reIsOctal = /^0o[0-7]+$/i; + /** Built-in method references without a dependency on `root`. */ + + var freeParseInt = parseInt; + /** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ + + function toNumber(value) { + if (typeof value == 'number') { + return value; + } + + if (isSymbol(value)) { + return NAN; + } + + if (isObject$2(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject$2(other) ? other + '' : other; + } + + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + + value = baseTrim(value); + var isBinary = reIsBinary.test(value); + return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value; + } + + /** Error message constants. */ + + var FUNC_ERROR_TEXT$1 = 'Expected a function'; + /* Built-in method references for those with the same name as other `lodash` methods. */ + + var nativeMax = Math.max, + nativeMin = Math.min; + /** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked. The debounced function comes with a `cancel` method to cancel + * delayed `func` invocations and a `flush` method to immediately invoke them. + * Provide `options` to indicate whether `func` should be invoked on the + * leading and/or trailing edge of the `wait` timeout. The `func` is invoked + * with the last arguments provided to the debounced function. Subsequent + * calls to the debounced function return the result of the last `func` + * invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the debounced function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.debounce` and `_.throttle`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] The number of milliseconds to delay. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=false] + * Specify invoking on the leading edge of the timeout. + * @param {number} [options.maxWait] + * The maximum time `func` is allowed to be delayed before it's invoked. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // Avoid costly calculations while the window size is in flux. + * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); + * + * // Invoke `sendMail` when clicked, debouncing subsequent calls. + * jQuery(element).on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })); + * + * // Ensure `batchLog` is invoked once after 1 second of debounced calls. + * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); + * var source = new EventSource('/stream'); + * jQuery(source).on('message', debounced); + * + * // Cancel the trailing debounced invocation. + * jQuery(window).on('popstate', debounced.cancel); + */ + + function debounce(func, wait, options) { + var lastArgs, + lastThis, + maxWait, + result, + timerId, + lastCallTime, + lastInvokeTime = 0, + leading = false, + maxing = false, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT$1); + } + + wait = toNumber(wait) || 0; + + if (isObject$2(options)) { + leading = !!options.leading; + maxing = 'maxWait' in options; + maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + function invokeFunc(time) { + var args = lastArgs, + thisArg = lastThis; + lastArgs = lastThis = undefined; + lastInvokeTime = time; + result = func.apply(thisArg, args); + return result; + } + + function leadingEdge(time) { + // Reset any `maxWait` timer. + lastInvokeTime = time; // Start the timer for the trailing edge. + + timerId = setTimeout(timerExpired, wait); // Invoke the leading edge. + + return leading ? invokeFunc(time) : result; + } + + function remainingWait(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime, + timeWaiting = wait - timeSinceLastCall; + return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting; + } + + function shouldInvoke(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime; // Either this is the first call, activity has stopped and we're at the + // trailing edge, the system time has gone backwards and we're treating + // it as the trailing edge, or we've hit the `maxWait` limit. + + return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait; + } + + function timerExpired() { + var time = now(); + + if (shouldInvoke(time)) { + return trailingEdge(time); + } // Restart the timer. + + + timerId = setTimeout(timerExpired, remainingWait(time)); + } + + function trailingEdge(time) { + timerId = undefined; // Only invoke if we have `lastArgs` which means `func` has been + // debounced at least once. + + if (trailing && lastArgs) { + return invokeFunc(time); + } + + lastArgs = lastThis = undefined; + return result; + } + + function cancel() { + if (timerId !== undefined) { + clearTimeout(timerId); + } + + lastInvokeTime = 0; + lastArgs = lastCallTime = lastThis = timerId = undefined; + } + + function flush() { + return timerId === undefined ? result : trailingEdge(now()); + } + + function debounced() { + var time = now(), + isInvoking = shouldInvoke(time); + lastArgs = arguments; + lastThis = this; + lastCallTime = time; + + if (isInvoking) { + if (timerId === undefined) { + return leadingEdge(lastCallTime); + } + + if (maxing) { + // Handle invocations in a tight loop. + clearTimeout(timerId); + timerId = setTimeout(timerExpired, wait); + return invokeFunc(lastCallTime); + } + } + + if (timerId === undefined) { + timerId = setTimeout(timerExpired, wait); + } + + return result; + } + + debounced.cancel = cancel; + debounced.flush = flush; + return debounced; + } + + /* + iD.coreDifference represents the difference between two graphs. + It knows how to calculate the set of entities that were + created, modified, or deleted, and also contains the logic + for recursively extending a difference to the complete set + of entities that will require a redraw, taking into account + child and parent relationships. + */ + + function coreDifference(base, head) { + var _changes = {}; + var _didChange = {}; // 'addition', 'deletion', 'geometry', 'properties' + + var _diff = {}; + + function checkEntityID(id) { + var h = head.entities[id]; + var b = base.entities[id]; + if (h === b) return; + if (_changes[id]) return; + + if (!h && b) { + _changes[id] = { + base: b, + head: h + }; + _didChange.deletion = true; + return; + } + + if (h && !b) { + _changes[id] = { + base: b, + head: h + }; + _didChange.addition = true; + return; + } + + if (h && b) { + if (h.members && b.members && !fastDeepEqual(h.members, b.members)) { + _changes[id] = { + base: b, + head: h + }; + _didChange.geometry = true; + _didChange.properties = true; + return; + } + + if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) { + _changes[id] = { + base: b, + head: h + }; + _didChange.geometry = true; + } + + if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) { + _changes[id] = { + base: b, + head: h + }; + _didChange.geometry = true; + } + + if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) { + _changes[id] = { + base: b, + head: h + }; + _didChange.properties = true; + } + } + } + + function load() { + // HOT CODE: there can be many thousands of downloaded entities, so looping + // through them all can become a performance bottleneck. Optimize by + // resolving duplicates and using a basic `for` loop + var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities))); + + for (var i = 0; i < ids.length; i++) { + checkEntityID(ids[i]); + } + } + + load(); + + _diff.length = function length() { + return Object.keys(_changes).length; + }; + + _diff.changes = function changes() { + return _changes; + }; + + _diff.didChange = _didChange; // pass true to include affected relation members + + _diff.extantIDs = function extantIDs(includeRelMembers) { + var result = new Set(); + Object.keys(_changes).forEach(function (id) { + if (_changes[id].head) { + result.add(id); + } + + var h = _changes[id].head; + var b = _changes[id].base; + var entity = h || b; + + if (includeRelMembers && entity.type === 'relation') { + var mh = h ? h.members.map(function (m) { + return m.id; + }) : []; + var mb = b ? b.members.map(function (m) { + return m.id; + }) : []; + utilArrayUnion(mh, mb).forEach(function (memberID) { + if (head.hasEntity(memberID)) { + result.add(memberID); + } + }); + } + }); + return Array.from(result); + }; + + _diff.modified = function modified() { + var result = []; + Object.values(_changes).forEach(function (change) { + if (change.base && change.head) { + result.push(change.head); + } + }); + return result; + }; + + _diff.created = function created() { + var result = []; + Object.values(_changes).forEach(function (change) { + if (!change.base && change.head) { + result.push(change.head); + } + }); + return result; + }; + + _diff.deleted = function deleted() { + var result = []; + Object.values(_changes).forEach(function (change) { + if (change.base && !change.head) { + result.push(change.base); + } + }); + return result; + }; + + _diff.summary = function summary() { + var relevant = {}; + var keys = Object.keys(_changes); + + for (var i = 0; i < keys.length; i++) { + var change = _changes[keys[i]]; + + if (change.head && change.head.geometry(head) !== 'vertex') { + addEntity(change.head, head, change.base ? 'modified' : 'created'); + } else if (change.base && change.base.geometry(base) !== 'vertex') { + addEntity(change.base, base, 'deleted'); + } else if (change.base && change.head) { + // modified vertex + var moved = !fastDeepEqual(change.base.loc, change.head.loc); + var retagged = !fastDeepEqual(change.base.tags, change.head.tags); + + if (moved) { + addParents(change.head); + } + + if (retagged || moved && change.head.hasInterestingTags()) { + addEntity(change.head, head, 'modified'); + } + } else if (change.head && change.head.hasInterestingTags()) { + // created vertex + addEntity(change.head, head, 'created'); + } else if (change.base && change.base.hasInterestingTags()) { + // deleted vertex + addEntity(change.base, base, 'deleted'); + } + } + + return Object.values(relevant); + + function addEntity(entity, graph, changeType) { + relevant[entity.id] = { + entity: entity, + graph: graph, + changeType: changeType + }; + } + + function addParents(entity) { + var parents = head.parentWays(entity); + + for (var j = parents.length - 1; j >= 0; j--) { + var parent = parents[j]; + + if (!(parent.id in relevant)) { + addEntity(parent, head, 'modified'); + } + } + } + }; // returns complete set of entities that require a redraw + // (optionally within given `extent`) + + + _diff.complete = function complete(extent) { + var result = {}; + var id, change; + + for (id in _changes) { + change = _changes[id]; + var h = change.head; + var b = change.base; + var entity = h || b; + var i; + + if (extent && (!h || !h.intersects(extent, head)) && (!b || !b.intersects(extent, base))) { + continue; + } + + result[id] = h; + + if (entity.type === 'way') { + var nh = h ? h.nodes : []; + var nb = b ? b.nodes : []; + var diff; + diff = utilArrayDifference(nh, nb); + + for (i = 0; i < diff.length; i++) { + result[diff[i]] = head.hasEntity(diff[i]); + } + + diff = utilArrayDifference(nb, nh); + + for (i = 0; i < diff.length; i++) { + result[diff[i]] = head.hasEntity(diff[i]); + } + } + + if (entity.type === 'relation' && entity.isMultipolygon()) { + var mh = h ? h.members.map(function (m) { + return m.id; + }) : []; + var mb = b ? b.members.map(function (m) { + return m.id; + }) : []; + var ids = utilArrayUnion(mh, mb); + + for (i = 0; i < ids.length; i++) { + var member = head.hasEntity(ids[i]); + if (!member) continue; // not downloaded + + if (extent && !member.intersects(extent, head)) continue; // not visible + + result[ids[i]] = member; + } + } + + addParents(head.parentWays(entity), result); + addParents(head.parentRelations(entity), result); + } + + return result; + + function addParents(parents, result) { + for (var i = 0; i < parents.length; i++) { + var parent = parents[i]; + if (parent.id in result) continue; + result[parent.id] = parent; + addParents(head.parentRelations(parent), result); + } + } + }; + + return _diff; + } + + function coreTree(head) { + // tree for entities + var _rtree = new RBush(); + + var _bboxes = {}; // maintain a separate tree for granular way segments + + var _segmentsRTree = new RBush(); + + var _segmentsBBoxes = {}; + var _segmentsByWayId = {}; + var tree = {}; + + function entityBBox(entity) { + var bbox = entity.extent(head).bbox(); + bbox.id = entity.id; + _bboxes[entity.id] = bbox; + return bbox; + } + + function segmentBBox(segment) { + var extent = segment.extent(head); // extent can be null if the node entities aren't in the graph for some reason + + if (!extent) return null; + var bbox = extent.bbox(); + bbox.segment = segment; + _segmentsBBoxes[segment.id] = bbox; + return bbox; + } + + function removeEntity(entity) { + _rtree.remove(_bboxes[entity.id]); + + delete _bboxes[entity.id]; + + if (_segmentsByWayId[entity.id]) { + _segmentsByWayId[entity.id].forEach(function (segment) { + _segmentsRTree.remove(_segmentsBBoxes[segment.id]); + + delete _segmentsBBoxes[segment.id]; + }); + + delete _segmentsByWayId[entity.id]; + } + } + + function loadEntities(entities) { + _rtree.load(entities.map(entityBBox)); + + var segments = []; + entities.forEach(function (entity) { + if (entity.segments) { + var entitySegments = entity.segments(head); // cache these to make them easy to remove later + + _segmentsByWayId[entity.id] = entitySegments; + segments = segments.concat(entitySegments); + } + }); + if (segments.length) _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean)); + } + + function updateParents(entity, insertions, memo) { + head.parentWays(entity).forEach(function (way) { + if (_bboxes[way.id]) { + removeEntity(way); + insertions[way.id] = way; + } + + updateParents(way, insertions, memo); + }); + head.parentRelations(entity).forEach(function (relation) { + if (memo[entity.id]) return; + memo[entity.id] = true; + + if (_bboxes[relation.id]) { + removeEntity(relation); + insertions[relation.id] = relation; + } + + updateParents(relation, insertions, memo); + }); + } + + tree.rebase = function (entities, force) { + var insertions = {}; + + for (var i = 0; i < entities.length; i++) { + var entity = entities[i]; + if (!entity.visible) continue; + + if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) { + if (!force) { + continue; + } else if (_bboxes[entity.id]) { + removeEntity(entity); + } + } + + insertions[entity.id] = entity; + updateParents(entity, insertions, {}); + } + + loadEntities(Object.values(insertions)); + return tree; + }; + + function updateToGraph(graph) { + if (graph === head) return; + var diff = coreDifference(head, graph); + head = graph; + var changed = diff.didChange; + if (!changed.addition && !changed.deletion && !changed.geometry) return; + var insertions = {}; + + if (changed.deletion) { + diff.deleted().forEach(function (entity) { + removeEntity(entity); + }); + } + + if (changed.geometry) { + diff.modified().forEach(function (entity) { + removeEntity(entity); + insertions[entity.id] = entity; + updateParents(entity, insertions, {}); + }); + } + + if (changed.addition) { + diff.created().forEach(function (entity) { + insertions[entity.id] = entity; + }); + } + + loadEntities(Object.values(insertions)); + } // returns an array of entities with bounding boxes overlapping `extent` for the given `graph` + + + tree.intersects = function (extent, graph) { + updateToGraph(graph); + return _rtree.search(extent.bbox()).map(function (bbox) { + return graph.entity(bbox.id); + }); + }; // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph` + + + tree.waySegments = function (extent, graph) { + updateToGraph(graph); + return _segmentsRTree.search(extent.bbox()).map(function (bbox) { + return bbox.segment; + }); + }; + + return tree; + } + + function svgIcon(name, svgklass, useklass) { + return function drawIcon(selection) { + selection.selectAll('svg.icon' + (svgklass ? '.' + svgklass.split(' ')[0] : '')).data([0]).enter().append('svg').attr('class', 'icon ' + (svgklass || '')).append('use').attr('xlink:href', name).attr('class', useklass); + }; + } + + function uiModal(selection, blocking) { + var _this = this; + + var keybinding = utilKeybinding('modal'); + var previous = selection.select('div.modal'); + var animate = previous.empty(); + previous.transition().duration(200).style('opacity', 0).remove(); + var shaded = selection.append('div').attr('class', 'shaded').style('opacity', 0); + + shaded.close = function () { + shaded.transition().duration(200).style('opacity', 0).remove(); + modal.transition().duration(200).style('top', '0px'); + select(document).call(keybinding.unbind); + }; + + var modal = shaded.append('div').attr('class', 'modal fillL'); + modal.append('input').attr('class', 'keytrap keytrap-first').on('focus.keytrap', moveFocusToLast); + + if (!blocking) { + shaded.on('click.remove-modal', function (d3_event) { + if (d3_event.target === _this) { + shaded.close(); + } + }); + modal.append('button').attr('class', 'close').on('click', shaded.close).call(svgIcon('#iD-icon-close')); + keybinding.on('⌫', shaded.close).on('⎋', shaded.close); + select(document).call(keybinding); + } + + modal.append('div').attr('class', 'content'); + modal.append('input').attr('class', 'keytrap keytrap-last').on('focus.keytrap', moveFocusToFirst); + + if (animate) { + shaded.transition().style('opacity', 1); + } else { + shaded.style('opacity', 1); + } + + return shaded; + + function moveFocusToFirst() { + var node = modal // there are additional rules about what's focusable, but this suits our purposes + .select('a, button, input:not(.keytrap), select, textarea').node(); + + if (node) { + node.focus(); + } else { + select(this).node().blur(); + } + } + + function moveFocusToLast() { + var nodes = modal.selectAll('a, button, input:not(.keytrap), select, textarea').nodes(); + + if (nodes.length) { + nodes[nodes.length - 1].focus(); + } else { + select(this).node().blur(); + } + } + } + + function uiLoading(context) { + var _modalSelection = select(null); + + var _message = ''; + var _blocking = false; + + var loading = function loading(selection) { + _modalSelection = uiModal(selection, _blocking); + + var loadertext = _modalSelection.select('.content').classed('loading-modal', true).append('div').attr('class', 'modal-section fillL'); + + loadertext.append('img').attr('class', 'loader').attr('src', context.imagePath('loader-white.gif')); + loadertext.append('h3').html(_message); + + _modalSelection.select('button.close').attr('class', 'hide'); + + return loading; + }; + + loading.message = function (val) { + if (!arguments.length) return _message; + _message = val; + return loading; + }; + + loading.blocking = function (val) { + if (!arguments.length) return _blocking; + _blocking = val; + return loading; + }; + + loading.close = function () { + _modalSelection.remove(); + }; + + loading.isShown = function () { + return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode; + }; + + return loading; + } + + function coreHistory(context) { + var dispatch = dispatch$8('reset', 'change', 'merge', 'restore', 'undone', 'redone', 'storage_error'); + + var _lock = utilSessionMutex('lock'); // restorable if iD not open in another window/tab and a saved history exists in localStorage + + + var _hasUnresolvedRestorableChanges = _lock.lock() && !!corePreferences(getKey('saved_history')); + + var duration = 150; + var _imageryUsed = []; + var _photoOverlaysUsed = []; + var _checkpoints = {}; + + var _pausedGraph; + + var _stack; + + var _index; + + var _tree; // internal _act, accepts list of actions and eased time + + + function _act(actions, t) { + actions = Array.prototype.slice.call(actions); + var annotation; + + if (typeof actions[actions.length - 1] !== 'function') { + annotation = actions.pop(); + } + + var graph = _stack[_index].graph; + + for (var i = 0; i < actions.length; i++) { + graph = actions[i](graph, t); + } + + return { + graph: graph, + annotation: annotation, + imageryUsed: _imageryUsed, + photoOverlaysUsed: _photoOverlaysUsed, + transform: context.projection.transform(), + selectedIDs: context.selectedIDs() + }; + } // internal _perform with eased time + + + function _perform(args, t) { + var previous = _stack[_index].graph; + _stack = _stack.slice(0, _index + 1); + + var actionResult = _act(args, t); + + _stack.push(actionResult); + + _index++; + return change(previous); + } // internal _replace with eased time + + + function _replace(args, t) { + var previous = _stack[_index].graph; // assert(_index == _stack.length - 1) + + var actionResult = _act(args, t); + + _stack[_index] = actionResult; + return change(previous); + } // internal _overwrite with eased time + + + function _overwrite(args, t) { + var previous = _stack[_index].graph; + + if (_index > 0) { + _index--; + + _stack.pop(); + } + + _stack = _stack.slice(0, _index + 1); + + var actionResult = _act(args, t); + + _stack.push(actionResult); + + _index++; + return change(previous); + } // determine difference and dispatch a change event + + + function change(previous) { + var difference = coreDifference(previous, history.graph()); + + if (!_pausedGraph) { + dispatch.call('change', this, difference); + } + + return difference; + } // iD uses namespaced keys so multiple installations do not conflict + + + function getKey(n) { + return 'iD_' + window.location.origin + '_' + n; + } + + var history = { + graph: function graph() { + return _stack[_index].graph; + }, + tree: function tree() { + return _tree; + }, + base: function base() { + return _stack[0].graph; + }, + merge: function merge(entities + /*, extent*/ + ) { + var stack = _stack.map(function (state) { + return state.graph; + }); + + _stack[0].graph.rebase(entities, stack, false); + + _tree.rebase(entities, false); + + dispatch.call('merge', this, entities); + }, + perform: function perform() { + // complete any transition already in progress + select(document).interrupt('history.perform'); + var transitionable = false; + var action0 = arguments[0]; + + if (arguments.length === 1 || arguments.length === 2 && typeof arguments[1] !== 'function') { + transitionable = !!action0.transitionable; + } + + if (transitionable) { + var origArguments = arguments; + select(document).transition('history.perform').duration(duration).ease(linear$1).tween('history.tween', function () { + return function (t) { + if (t < 1) _overwrite([action0], t); + }; + }).on('start', function () { + _perform([action0], 0); + }).on('end interrupt', function () { + _overwrite(origArguments, 1); + }); + } else { + return _perform(arguments); + } + }, + replace: function replace() { + select(document).interrupt('history.perform'); + return _replace(arguments, 1); + }, + // Same as calling pop and then perform + overwrite: function overwrite() { + select(document).interrupt('history.perform'); + return _overwrite(arguments, 1); + }, + pop: function pop(n) { + select(document).interrupt('history.perform'); + var previous = _stack[_index].graph; + + if (isNaN(+n) || +n < 0) { + n = 1; + } + + while (n-- > 0 && _index > 0) { + _index--; + + _stack.pop(); + } + + return change(previous); + }, + // Back to the previous annotated state or _index = 0. + undo: function undo() { + select(document).interrupt('history.perform'); + var previousStack = _stack[_index]; + var previous = previousStack.graph; + + while (_index > 0) { + _index--; + if (_stack[_index].annotation) break; + } + + dispatch.call('undone', this, _stack[_index], previousStack); + return change(previous); + }, + // Forward to the next annotated state. + redo: function redo() { + select(document).interrupt('history.perform'); + var previousStack = _stack[_index]; + var previous = previousStack.graph; + var tryIndex = _index; + + while (tryIndex < _stack.length - 1) { + tryIndex++; + + if (_stack[tryIndex].annotation) { + _index = tryIndex; + dispatch.call('redone', this, _stack[_index], previousStack); + break; + } + } + + return change(previous); + }, + pauseChangeDispatch: function pauseChangeDispatch() { + if (!_pausedGraph) { + _pausedGraph = _stack[_index].graph; + } + }, + resumeChangeDispatch: function resumeChangeDispatch() { + if (_pausedGraph) { + var previous = _pausedGraph; + _pausedGraph = null; + return change(previous); + } + }, + undoAnnotation: function undoAnnotation() { + var i = _index; + + while (i >= 0) { + if (_stack[i].annotation) return _stack[i].annotation; + i--; + } + }, + redoAnnotation: function redoAnnotation() { + var i = _index + 1; + + while (i <= _stack.length - 1) { + if (_stack[i].annotation) return _stack[i].annotation; + i++; + } + }, + // Returns the entities from the active graph with bounding boxes + // overlapping the given `extent`. + intersects: function intersects(extent) { + return _tree.intersects(extent, _stack[_index].graph); + }, + difference: function difference() { + var base = _stack[0].graph; + var head = _stack[_index].graph; + return coreDifference(base, head); + }, + changes: function changes(action) { + var base = _stack[0].graph; + var head = _stack[_index].graph; + + if (action) { + head = action(head); + } + + var difference = coreDifference(base, head); + return { + modified: difference.modified(), + created: difference.created(), + deleted: difference.deleted() + }; + }, + hasChanges: function hasChanges() { + return this.difference().length() > 0; + }, + imageryUsed: function imageryUsed(sources) { + if (sources) { + _imageryUsed = sources; + return history; + } else { + var s = new Set(); + + _stack.slice(1, _index + 1).forEach(function (state) { + state.imageryUsed.forEach(function (source) { + if (source !== 'Custom') { + s.add(source); + } + }); + }); + + return Array.from(s); + } + }, + photoOverlaysUsed: function photoOverlaysUsed(sources) { + if (sources) { + _photoOverlaysUsed = sources; + return history; + } else { + var s = new Set(); + + _stack.slice(1, _index + 1).forEach(function (state) { + if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) { + state.photoOverlaysUsed.forEach(function (photoOverlay) { + s.add(photoOverlay); + }); + } + }); + + return Array.from(s); + } + }, + // save the current history state + checkpoint: function checkpoint(key) { + _checkpoints[key] = { + stack: _stack, + index: _index + }; + return history; + }, + // restore history state to a given checkpoint or reset completely + reset: function reset(key) { + if (key !== undefined && _checkpoints.hasOwnProperty(key)) { + _stack = _checkpoints[key].stack; + _index = _checkpoints[key].index; + } else { + _stack = [{ + graph: coreGraph() + }]; + _index = 0; + _tree = coreTree(_stack[0].graph); + _checkpoints = {}; + } + + dispatch.call('reset'); + dispatch.call('change'); + return history; + }, + // `toIntroGraph()` is used to export the intro graph used by the walkthrough. + // + // To use it: + // 1. Start the walkthrough. + // 2. Get to a "free editing" tutorial step + // 3. Make your edits to the walkthrough map + // 4. In your browser dev console run: + // `id.history().toIntroGraph()` + // 5. This outputs stringified JSON to the browser console + // 6. Copy it to `data/intro_graph.json` and prettify it in your code editor + toIntroGraph: function toIntroGraph() { + var nextID = { + n: 0, + r: 0, + w: 0 + }; + var permIDs = {}; + var graph = this.graph(); + var baseEntities = {}; // clone base entities.. + + Object.values(graph.base().entities).forEach(function (entity) { + var copy = copyIntroEntity(entity); + baseEntities[copy.id] = copy; + }); // replace base entities with head entities.. + + Object.keys(graph.entities).forEach(function (id) { + var entity = graph.entities[id]; + + if (entity) { + var copy = copyIntroEntity(entity); + baseEntities[copy.id] = copy; + } else { + delete baseEntities[id]; + } + }); // swap temporary for permanent ids.. + + Object.values(baseEntities).forEach(function (entity) { + if (Array.isArray(entity.nodes)) { + entity.nodes = entity.nodes.map(function (node) { + return permIDs[node] || node; + }); + } + + if (Array.isArray(entity.members)) { + entity.members = entity.members.map(function (member) { + member.id = permIDs[member.id] || member.id; + return member; + }); + } + }); + return JSON.stringify({ + dataIntroGraph: baseEntities + }); + + function copyIntroEntity(source) { + var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']); // Note: the copy is no longer an osmEntity, so it might not have `tags` + + if (copy.tags && !Object.keys(copy.tags)) { + delete copy.tags; + } + + if (Array.isArray(copy.loc)) { + copy.loc[0] = +copy.loc[0].toFixed(6); + copy.loc[1] = +copy.loc[1].toFixed(6); + } + + var match = source.id.match(/([nrw])-\d*/); // temporary id + + if (match !== null) { + var nrw = match[1]; + var permID; + + do { + permID = nrw + ++nextID[nrw]; + } while (baseEntities.hasOwnProperty(permID)); + + copy.id = permIDs[source.id] = permID; + } + + return copy; + } + }, + toJSON: function toJSON() { + if (!this.hasChanges()) return; + var allEntities = {}; + var baseEntities = {}; + var base = _stack[0]; + + var s = _stack.map(function (i) { + var modified = []; + var deleted = []; + Object.keys(i.graph.entities).forEach(function (id) { + var entity = i.graph.entities[id]; + + if (entity) { + var key = osmEntity.key(entity); + allEntities[key] = entity; + modified.push(key); + } else { + deleted.push(id); + } // make sure that the originals of changed or deleted entities get merged + // into the base of the _stack after restoring the data from JSON. + + + if (id in base.graph.entities) { + baseEntities[id] = base.graph.entities[id]; + } + + if (entity && entity.nodes) { + // get originals of pre-existing child nodes + entity.nodes.forEach(function (nodeID) { + if (nodeID in base.graph.entities) { + baseEntities[nodeID] = base.graph.entities[nodeID]; + } + }); + } // get originals of parent entities too + + + var baseParents = base.graph._parentWays[id]; + + if (baseParents) { + baseParents.forEach(function (parentID) { + if (parentID in base.graph.entities) { + baseEntities[parentID] = base.graph.entities[parentID]; + } + }); + } + }); + var x = {}; + if (modified.length) x.modified = modified; + if (deleted.length) x.deleted = deleted; + if (i.imageryUsed) x.imageryUsed = i.imageryUsed; + if (i.photoOverlaysUsed) x.photoOverlaysUsed = i.photoOverlaysUsed; + if (i.annotation) x.annotation = i.annotation; + if (i.transform) x.transform = i.transform; + if (i.selectedIDs) x.selectedIDs = i.selectedIDs; + return x; + }); + + return JSON.stringify({ + version: 3, + entities: Object.values(allEntities), + baseEntities: Object.values(baseEntities), + stack: s, + nextIDs: osmEntity.id.next, + index: _index, + // note the time the changes were saved + timestamp: new Date().getTime() + }); + }, + fromJSON: function fromJSON(json, loadChildNodes) { + var h = JSON.parse(json); + var loadComplete = true; + osmEntity.id.next = h.nextIDs; + _index = h.index; + + if (h.version === 2 || h.version === 3) { + var allEntities = {}; + h.entities.forEach(function (entity) { + allEntities[osmEntity.key(entity)] = osmEntity(entity); + }); + + if (h.version === 3) { + // This merges originals for changed entities into the base of + // the _stack even if the current _stack doesn't have them (for + // example when iD has been restarted in a different region) + var baseEntities = h.baseEntities.map(function (d) { + return osmEntity(d); + }); + + var stack = _stack.map(function (state) { + return state.graph; + }); + + _stack[0].graph.rebase(baseEntities, stack, true); + + _tree.rebase(baseEntities, true); // When we restore a modified way, we also need to fetch any missing + // childnodes that would normally have been downloaded with it.. #2142 + + + if (loadChildNodes) { + var osm = context.connection(); + var baseWays = baseEntities.filter(function (e) { + return e.type === 'way'; + }); + var nodeIDs = baseWays.reduce(function (acc, way) { + return utilArrayUnion(acc, way.nodes); + }, []); + var missing = nodeIDs.filter(function (n) { + return !_stack[0].graph.hasEntity(n); + }); + + if (missing.length && osm) { + loadComplete = false; + context.map().redrawEnable(false); + var loading = uiLoading(context).blocking(true); + context.container().call(loading); + + var childNodesLoaded = function childNodesLoaded(err, result) { + if (!err) { + var visibleGroups = utilArrayGroupBy(result.data, 'visible'); + var visibles = visibleGroups["true"] || []; // alive nodes + + var invisibles = visibleGroups["false"] || []; // deleted nodes + + if (visibles.length) { + var visibleIDs = visibles.map(function (entity) { + return entity.id; + }); + + var stack = _stack.map(function (state) { + return state.graph; + }); + + missing = utilArrayDifference(missing, visibleIDs); + + _stack[0].graph.rebase(visibles, stack, true); + + _tree.rebase(visibles, true); + } // fetch older versions of nodes that were deleted.. + + + invisibles.forEach(function (entity) { + osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded); + }); + } + + if (err || !missing.length) { + loading.close(); + context.map().redrawEnable(true); + dispatch.call('change'); + dispatch.call('restore', this); + } + }; + + osm.loadMultiple(missing, childNodesLoaded); + } + } + } + + _stack = h.stack.map(function (d) { + var entities = {}, + entity; + + if (d.modified) { + d.modified.forEach(function (key) { + entity = allEntities[key]; + entities[entity.id] = entity; + }); + } + + if (d.deleted) { + d.deleted.forEach(function (id) { + entities[id] = undefined; + }); + } + + return { + graph: coreGraph(_stack[0].graph).load(entities), + annotation: d.annotation, + imageryUsed: d.imageryUsed, + photoOverlaysUsed: d.photoOverlaysUsed, + transform: d.transform, + selectedIDs: d.selectedIDs + }; + }); + } else { + // original version + _stack = h.stack.map(function (d) { + var entities = {}; + + for (var i in d.entities) { + var entity = d.entities[i]; + entities[i] = entity === 'undefined' ? undefined : osmEntity(entity); + } + + d.graph = coreGraph(_stack[0].graph).load(entities); + return d; + }); + } + + var transform = _stack[_index].transform; + + if (transform) { + context.map().transformEase(transform, 0); // 0 = immediate, no easing + } + + if (loadComplete) { + dispatch.call('change'); + dispatch.call('restore', this); + } + + return history; + }, + lock: function lock() { + return _lock.lock(); + }, + unlock: function unlock() { + _lock.unlock(); + }, + save: function save() { + if (_lock.locked() && // don't overwrite existing, unresolved changes + !_hasUnresolvedRestorableChanges) { + var success = corePreferences(getKey('saved_history'), history.toJSON() || null); + if (!success) dispatch.call('storage_error'); + } + + return history; + }, + // delete the history version saved in localStorage + clearSaved: function clearSaved() { + context.debouncedSave.cancel(); + + if (_lock.locked()) { + _hasUnresolvedRestorableChanges = false; + corePreferences(getKey('saved_history'), null); // clear the changeset metadata associated with the saved history + + corePreferences('comment', null); + corePreferences('hashtags', null); + corePreferences('source', null); + } + + return history; + }, + savedHistoryJSON: function savedHistoryJSON() { + return corePreferences(getKey('saved_history')); + }, + hasRestorableChanges: function hasRestorableChanges() { + return _hasUnresolvedRestorableChanges; + }, + // load history from a version stored in localStorage + restore: function restore() { + if (_lock.locked()) { + _hasUnresolvedRestorableChanges = false; + var json = this.savedHistoryJSON(); + if (json) history.fromJSON(json, true); + } + }, + _getKey: getKey + }; + history.reset(); + return utilRebind(history, dispatch, 'on'); + } + + /** + * Look for roads that can be connected to other roads with a short extension + */ + + function validationAlmostJunction(context) { + var type = 'almost_junction'; + var EXTEND_TH_METERS = 5; + var WELD_TH_METERS = 0.75; // Comes from considering bounding case of parallel ways + + var CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS; // Comes from considering bounding case of perpendicular ways + + var SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS); + + function isHighway(entity) { + return entity.type === 'way' && osmRoutableHighwayTagValues[entity.tags.highway]; + } + + function isTaggedAsNotContinuing(node) { + return node.tags.noexit === 'yes' || node.tags.amenity === 'parking_entrance' || node.tags.entrance && node.tags.entrance !== 'no'; + } + + var validation = function checkAlmostJunction(entity, graph) { + if (!isHighway(entity)) return []; + if (entity.isDegenerate()) return []; + var tree = context.history().tree(); + var extendableNodeInfos = findConnectableEndNodesByExtension(entity); + var issues = []; + extendableNodeInfos.forEach(function (extendableNodeInfo) { + issues.push(new validationIssue({ + type: type, + subtype: 'highway-highway', + severity: 'warning', + message: function message(context) { + var entity1 = context.hasEntity(this.entityIds[0]); + + if (this.entityIds[0] === this.entityIds[2]) { + return entity1 ? _t.html('issues.almost_junction.self.message', { + feature: utilDisplayLabel(entity1, context.graph()) + }) : ''; + } else { + var entity2 = context.hasEntity(this.entityIds[2]); + return entity1 && entity2 ? _t.html('issues.almost_junction.message', { + feature: utilDisplayLabel(entity1, context.graph()), + feature2: utilDisplayLabel(entity2, context.graph()) + }) : ''; + } + }, + reference: showReference, + entityIds: [entity.id, extendableNodeInfo.node.id, extendableNodeInfo.wid], + loc: extendableNodeInfo.node.loc, + hash: JSON.stringify(extendableNodeInfo.node.loc), + data: { + midId: extendableNodeInfo.mid.id, + edge: extendableNodeInfo.edge, + cross_loc: extendableNodeInfo.cross_loc + }, + dynamicFixes: makeFixes + })); + }); + return issues; + + function makeFixes(context) { + var fixes = [new validationIssueFix({ + icon: 'iD-icon-abutment', + title: _t.html('issues.fix.connect_features.title'), + onClick: function onClick(context) { + var annotation = _t('issues.fix.connect_almost_junction.annotation'); + + var _this$issue$entityIds = _slicedToArray(this.issue.entityIds, 3), + endNodeId = _this$issue$entityIds[1], + crossWayId = _this$issue$entityIds[2]; + + var midNode = context.entity(this.issue.data.midId); + var endNode = context.entity(endNodeId); + var crossWay = context.entity(crossWayId); // When endpoints are close, just join if resulting small change in angle (#7201) + + var nearEndNodes = findNearbyEndNodes(endNode, crossWay); + + if (nearEndNodes.length > 0) { + var collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes); + + if (collinear) { + context.perform(actionMergeNodes([collinear.id, endNode.id], collinear.loc), annotation); + return; + } + } + + var targetEdge = this.issue.data.edge; + var crossLoc = this.issue.data.cross_loc; + var edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])]; + var closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc); // already a point nearby, just connect to that + + if (closestNodeInfo.distance < WELD_TH_METERS) { + context.perform(actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc), annotation); // else add the end node to the edge way + } else { + context.perform(actionAddMidpoint({ + loc: crossLoc, + edge: targetEdge + }, endNode), annotation); + } + } + })]; + var node = context.hasEntity(this.entityIds[1]); + + if (node && !node.hasInterestingTags()) { + // node has no descriptive tags, suggest noexit fix + fixes.push(new validationIssueFix({ + icon: 'maki-barrier', + title: _t.html('issues.fix.tag_as_disconnected.title'), + onClick: function onClick(context) { + var nodeID = this.issue.entityIds[1]; + var tags = Object.assign({}, context.entity(nodeID).tags); + tags.noexit = 'yes'; + context.perform(actionChangeTags(nodeID, tags), _t('issues.fix.tag_as_disconnected.annotation')); + } + })); + } + + return fixes; + } + + function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.almost_junction.highway-highway.reference')); + } + + function isExtendableCandidate(node, way) { + // can not accurately test vertices on tiles not downloaded from osm - #5938 + var osm = services.osm; + + if (osm && !osm.isDataLoaded(node.loc)) { + return false; + } + + if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) { + return false; + } + + var occurrences = 0; + + for (var index in way.nodes) { + if (way.nodes[index] === node.id) { + occurrences += 1; + + if (occurrences > 1) { + return false; + } + } + } + + return true; + } + + function findConnectableEndNodesByExtension(way) { + var results = []; + if (way.isClosed()) return results; + var testNodes; + var indices = [0, way.nodes.length - 1]; + indices.forEach(function (nodeIndex) { + var nodeID = way.nodes[nodeIndex]; + var node = graph.entity(nodeID); + if (!isExtendableCandidate(node, way)) return; + var connectionInfo = canConnectByExtend(way, nodeIndex); + if (!connectionInfo) return; + testNodes = graph.childNodes(way).slice(); // shallow copy + + testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc); // don't flag issue if connecting the ways would cause self-intersection + + if (geoHasSelfIntersections(testNodes, nodeID)) return; + results.push(connectionInfo); + }); + return results; + } + + function findNearbyEndNodes(node, way) { + return [way.nodes[0], way.nodes[way.nodes.length - 1]].map(function (d) { + return graph.entity(d); + }).filter(function (d) { + // Node cannot be near to itself, but other endnode of same way could be + return d.id !== node.id && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH; + }); + } + + function findSmallJoinAngle(midNode, tipNode, endNodes) { + // Both nodes could be close, so want to join whichever is closest to collinear + var joinTo; + var minAngle = Infinity; // Checks midNode -> tipNode -> endNode for collinearity + + endNodes.forEach(function (endNode) { + var a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI; + var a2 = geoAngle(midNode, endNode, context.projection) + Math.PI; + var diff = Math.max(a1, a2) - Math.min(a1, a2); + + if (diff < minAngle) { + joinTo = endNode; + minAngle = diff; + } + }); + /* Threshold set by considering right angle triangle + based on node joining threshold and extension distance */ + + if (minAngle <= SIG_ANGLE_TH) return joinTo; + return null; + } + + function hasTag(tags, key) { + return tags[key] !== undefined && tags[key] !== 'no'; + } + + function canConnectWays(way, way2) { + // allow self-connections + if (way.id === way2.id) return true; // if one is bridge or tunnel, both must be bridge or tunnel + + if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) && !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) return false; + if ((hasTag(way.tags, 'tunnel') || hasTag(way2.tags, 'tunnel')) && !(hasTag(way.tags, 'tunnel') && hasTag(way2.tags, 'tunnel'))) return false; // must have equivalent layers and levels + + var layer1 = way.tags.layer || '0', + layer2 = way2.tags.layer || '0'; + if (layer1 !== layer2) return false; + var level1 = way.tags.level || '0', + level2 = way2.tags.level || '0'; + if (level1 !== level2) return false; + return true; + } + + function canConnectByExtend(way, endNodeIdx) { + var tipNid = way.nodes[endNodeIdx]; // the 'tip' node for extension point + + var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2]; // the other node of the edge + + var tipNode = graph.entity(tipNid); + var midNode = graph.entity(midNid); + var lon = tipNode.loc[0]; + var lat = tipNode.loc[1]; + var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2; + var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2; + 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 + + var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc); + var t = EXTEND_TH_METERS / edgeLen + 1.0; + var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t); // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways + + var segmentInfos = tree.waySegments(queryExtent, graph); + + for (var i = 0; i < segmentInfos.length; i++) { + var segmentInfo = segmentInfos[i]; + var way2 = graph.entity(segmentInfo.wayId); + if (!isHighway(way2)) continue; + if (!canConnectWays(way, way2)) continue; + var nAid = segmentInfo.nodes[0], + nBid = segmentInfo.nodes[1]; + if (nAid === tipNid || nBid === tipNid) continue; + var nA = graph.entity(nAid), + nB = graph.entity(nBid); + var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]); + + if (crossLoc) { + return { + mid: midNode, + node: tipNode, + wid: way2.id, + edge: [nA.id, nB.id], + cross_loc: crossLoc + }; + } + } + + return null; + } + }; + + validation.type = type; + return validation; + } + + function validationCloseNodes(context) { + var type = 'close_nodes'; + var pointThresholdMeters = 0.2; + + var validation = function validation(entity, graph) { + if (entity.type === 'node') { + return getIssuesForNode(entity); + } else if (entity.type === 'way') { + return getIssuesForWay(entity); + } + + return []; + + function getIssuesForNode(node) { + var parentWays = graph.parentWays(node); + + if (parentWays.length) { + return getIssuesForVertex(node, parentWays); + } else { + return getIssuesForDetachedPoint(node); + } + } + + function wayTypeFor(way) { + if (way.tags.boundary && way.tags.boundary !== 'no') return 'boundary'; + if (way.tags.indoor && way.tags.indoor !== 'no') return 'indoor'; + if (way.tags.building && way.tags.building !== 'no' || way.tags['building:part'] && way.tags['building:part'] !== 'no') return 'building'; + if (osmPathHighwayTagValues[way.tags.highway]) return 'path'; + var parentRelations = graph.parentRelations(way); + + for (var i in parentRelations) { + var relation = parentRelations[i]; + if (relation.tags.type === 'boundary') return 'boundary'; + + if (relation.isMultipolygon()) { + if (relation.tags.indoor && relation.tags.indoor !== 'no') return 'indoor'; + if (relation.tags.building && relation.tags.building !== 'no' || relation.tags['building:part'] && relation.tags['building:part'] !== 'no') return 'building'; + } + } + + return 'other'; + } + + function shouldCheckWay(way) { + // don't flag issues where merging would create degenerate ways + if (way.nodes.length <= 2 || way.isClosed() && way.nodes.length <= 4) return false; + var bbox = way.extent(graph).bbox(); + var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]); // don't flag close nodes in very small ways + + if (hypotenuseMeters < 1.5) return false; + return true; + } + + function getIssuesForWay(way) { + if (!shouldCheckWay(way)) return []; + var issues = [], + nodes = graph.childNodes(way); + + for (var i = 0; i < nodes.length - 1; i++) { + var node1 = nodes[i]; + var node2 = nodes[i + 1]; + var issue = getWayIssueIfAny(node1, node2, way); + if (issue) issues.push(issue); + } + + return issues; + } + + function getIssuesForVertex(node, parentWays) { + var issues = []; + + function checkForCloseness(node1, node2, way) { + var issue = getWayIssueIfAny(node1, node2, way); + if (issue) issues.push(issue); + } + + for (var i = 0; i < parentWays.length; i++) { + var parentWay = parentWays[i]; + if (!shouldCheckWay(parentWay)) continue; + var lastIndex = parentWay.nodes.length - 1; + + for (var j = 0; j < parentWay.nodes.length; j++) { + if (j !== 0) { + if (parentWay.nodes[j - 1] === node.id) { + checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay); + } + } + + if (j !== lastIndex) { + if (parentWay.nodes[j + 1] === node.id) { + checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay); + } + } + } + } + + return issues; + } + + function thresholdMetersForWay(way) { + if (!shouldCheckWay(way)) return 0; + var wayType = wayTypeFor(way); // don't flag boundaries since they might be highly detailed and can't be easily verified + + if (wayType === 'boundary') return 0; // expect some features to be mapped with higher levels of detail + + if (wayType === 'indoor') return 0.01; + if (wayType === 'building') return 0.05; + if (wayType === 'path') return 0.1; + return 0.2; + } + + function getIssuesForDetachedPoint(node) { + var issues = []; + var lon = node.loc[0]; + var lat = node.loc[1]; + var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2; + var lat_range = geoMetersToLat(pointThresholdMeters) / 2; + var queryExtent = geoExtent([[lon - lon_range, lat - lat_range], [lon + lon_range, lat + lat_range]]); + var intersected = context.history().tree().intersects(queryExtent, graph); + + for (var j = 0; j < intersected.length; j++) { + var nearby = intersected[j]; + if (nearby.id === node.id) continue; + if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') continue; + + if (nearby.loc === node.loc || geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) { + // allow very close points if tags indicate the z-axis might vary + var zAxisKeys = { + layer: true, + level: true, + 'addr:housenumber': true, + 'addr:unit': true + }; + var zAxisDifferentiates = false; + + for (var key in zAxisKeys) { + var nodeValue = node.tags[key] || '0'; + var nearbyValue = nearby.tags[key] || '0'; + + if (nodeValue !== nearbyValue) { + zAxisDifferentiates = true; + break; + } + } + + if (zAxisDifferentiates) continue; + issues.push(new validationIssue({ + type: type, + subtype: 'detached', + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]), + entity2 = context.hasEntity(this.entityIds[1]); + return entity && entity2 ? _t.html('issues.close_nodes.detached.message', { + feature: utilDisplayLabel(entity, context.graph()), + feature2: utilDisplayLabel(entity2, context.graph()) + }) : ''; + }, + reference: showReference, + entityIds: [node.id, nearby.id], + dynamicFixes: function dynamicFixes() { + return [new validationIssueFix({ + icon: 'iD-operation-disconnect', + title: _t.html('issues.fix.move_points_apart.title') + }), new validationIssueFix({ + icon: 'iD-icon-layers', + title: _t.html('issues.fix.use_different_layers_or_levels.title') + })]; + } + })); + } + } + + return issues; + + function showReference(selection) { + var referenceText = _t('issues.close_nodes.detached.reference'); + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText); + } + } + + function getWayIssueIfAny(node1, node2, way) { + if (node1.id === node2.id || node1.hasInterestingTags() && node2.hasInterestingTags()) { + return null; + } + + if (node1.loc !== node2.loc) { + var parentWays1 = graph.parentWays(node1); + var parentWays2 = new Set(graph.parentWays(node2)); + var sharedWays = parentWays1.filter(function (parentWay) { + return parentWays2.has(parentWay); + }); + var thresholds = sharedWays.map(function (parentWay) { + return thresholdMetersForWay(parentWay); + }); + var threshold = Math.min.apply(Math, _toConsumableArray(thresholds)); + var distance = geoSphericalDistance(node1.loc, node2.loc); + if (distance > threshold) return null; + } + + return new validationIssue({ + type: type, + subtype: 'vertices', + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]); + return entity ? _t.html('issues.close_nodes.message', { + way: utilDisplayLabel(entity, context.graph()) + }) : ''; + }, + reference: showReference, + entityIds: [way.id, node1.id, node2.id], + loc: node1.loc, + dynamicFixes: function dynamicFixes() { + return [new validationIssueFix({ + icon: 'iD-icon-plus', + title: _t.html('issues.fix.merge_points.title'), + onClick: function onClick(context) { + var entityIds = this.issue.entityIds; + var action = actionMergeNodes([entityIds[1], entityIds[2]]); + context.perform(action, _t('issues.fix.merge_close_vertices.annotation')); + } + }), new validationIssueFix({ + icon: 'iD-operation-disconnect', + title: _t.html('issues.fix.move_points_apart.title') + })]; + } + }); + + function showReference(selection) { + var referenceText = _t('issues.close_nodes.reference'); + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText); + } + } + }; + + validation.type = type; + return validation; + } + + function validationCrossingWays(context) { + var type = 'crossing_ways'; // returns the way or its parent relation, whichever has a useful feature type + + function getFeatureWithFeatureTypeTagsForWay(way, graph) { + if (getFeatureType(way, graph) === null) { + // if the way doesn't match a feature type, check its parent relations + var parentRels = graph.parentRelations(way); + + for (var i = 0; i < parentRels.length; i++) { + var rel = parentRels[i]; + + if (getFeatureType(rel, graph) !== null) { + return rel; + } + } + } + + return way; + } + + function hasTag(tags, key) { + return tags[key] !== undefined && tags[key] !== 'no'; + } + + function taggedAsIndoor(tags) { + return hasTag(tags, 'indoor') || hasTag(tags, 'level') || tags.highway === 'corridor'; + } + + function allowsBridge(featureType) { + return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway'; + } + + function allowsTunnel(featureType) { + return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway'; + } // discard + + + var ignoredBuildings = { + demolished: true, + dismantled: true, + proposed: true, + razed: true + }; + + function getFeatureType(entity, graph) { + var geometry = entity.geometry(graph); + if (geometry !== 'line' && geometry !== 'area') return null; + var tags = entity.tags; + if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) return 'building'; + if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) return 'highway'; // don't check railway or waterway areas + + if (geometry !== 'line') return null; + if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) return 'railway'; + if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) return 'waterway'; + return null; + } + + function isLegitCrossing(tags1, featureType1, tags2, featureType2) { + // assume 0 by default + var level1 = tags1.level || '0'; + var level2 = tags2.level || '0'; + + if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) { + // assume features don't interact if they're indoor on different levels + return true; + } // assume 0 by default; don't use way.layer() since we account for structures here + + + var layer1 = tags1.layer || '0'; + var layer2 = tags2.layer || '0'; + + if (allowsBridge(featureType1) && allowsBridge(featureType2)) { + if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) return true; + if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) return true; // crossing bridges must use different layers + + if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) return true; + } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) return true;else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) return true; + + if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) { + if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) return true; + if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) return true; // crossing tunnels must use different layers + + if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) return true; + } else if (allowsTunnel(featureType1) && hasTag(tags1, 'tunnel')) return true;else if (allowsTunnel(featureType2) && hasTag(tags2, 'tunnel')) return true; // don't flag crossing waterways and pier/highways + + + if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') return true; + if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') return true; + + if (featureType1 === 'building' || featureType2 === 'building') { + // for building crossings, different layers are enough + if (layer1 !== layer2) return true; + } + + return false; + } // highway values for which we shouldn't recommend connecting to waterways + + + var highwaysDisallowingFords = { + motorway: true, + motorway_link: true, + trunk: true, + trunk_link: true, + primary: true, + primary_link: true, + secondary: true, + secondary_link: true + }; + var nonCrossingHighways = { + track: true + }; + + function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) { + var featureType1 = getFeatureType(entity1, graph); + var featureType2 = getFeatureType(entity2, graph); + var geometry1 = entity1.geometry(graph); + var geometry2 = entity2.geometry(graph); + var bothLines = geometry1 === 'line' && geometry2 === 'line'; + + if (featureType1 === featureType2) { + if (featureType1 === 'highway') { + var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway]; + var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway]; + + if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) { + // one feature is a path but not both + var roadFeature = entity1IsPath ? entity2 : entity1; + + if (nonCrossingHighways[roadFeature.tags.highway]) { + // don't mark path connections with certain roads as crossings + return {}; + } + + var pathFeature = entity1IsPath ? entity1 : entity2; + + if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) { + // if the path is a crossing, match the crossing type + return bothLines ? { + highway: 'crossing', + crossing: pathFeature.tags.crossing + } : {}; + } // don't add a `crossing` subtag to ambiguous crossings + + + return bothLines ? { + highway: 'crossing' + } : {}; + } + + return {}; + } + + if (featureType1 === 'waterway') return {}; + if (featureType1 === 'railway') return {}; + } else { + var featureTypes = [featureType1, featureType2]; + + if (featureTypes.indexOf('highway') !== -1) { + if (featureTypes.indexOf('railway') !== -1) { + if (!bothLines) return {}; + var isTram = entity1.tags.railway === 'tram' || entity2.tags.railway === 'tram'; + + if (osmPathHighwayTagValues[entity1.tags.highway] || osmPathHighwayTagValues[entity2.tags.highway]) { + // path-tram connections use this tag + if (isTram) return { + railway: 'tram_crossing' + }; // other path-rail connections use this tag + + return { + railway: 'crossing' + }; + } else { + // path-tram connections use this tag + if (isTram) return { + railway: 'tram_level_crossing' + }; // other road-rail connections use this tag + + return { + railway: 'level_crossing' + }; + } + } + + if (featureTypes.indexOf('waterway') !== -1) { + // do not allow fords on structures + if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) return null; + if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) return null; + + if (highwaysDisallowingFords[entity1.tags.highway] || highwaysDisallowingFords[entity2.tags.highway]) { + // do not allow fords on major highways + return null; + } + + return bothLines ? { + ford: 'yes' + } : {}; + } + } + } + + return null; + } + + function findCrossingsByWay(way1, graph, tree) { + var edgeCrossInfos = []; + if (way1.type !== 'way') return edgeCrossInfos; + var taggedFeature1 = getFeatureWithFeatureTypeTagsForWay(way1, graph); + var way1FeatureType = getFeatureType(taggedFeature1, graph); + if (way1FeatureType === null) return edgeCrossInfos; + var checkedSingleCrossingWays = {}; // declare vars ahead of time to reduce garbage collection + + var i, j; + var extent; + var n1, n2, nA, nB, nAId, nBId; + var segment1, segment2; + var oneOnly; + var segmentInfos, segment2Info, way2, taggedFeature2, way2FeatureType; + var way1Nodes = graph.childNodes(way1); + var comparedWays = {}; + + for (i = 0; i < way1Nodes.length - 1; i++) { + n1 = way1Nodes[i]; + n2 = way1Nodes[i + 1]; + extent = geoExtent([[Math.min(n1.loc[0], n2.loc[0]), Math.min(n1.loc[1], n2.loc[1])], [Math.max(n1.loc[0], n2.loc[0]), Math.max(n1.loc[1], n2.loc[1])]]); // Optimize by only checking overlapping segments, not every segment + // of overlapping ways + + segmentInfos = tree.waySegments(extent, graph); + + for (j = 0; j < segmentInfos.length; j++) { + segment2Info = segmentInfos[j]; // don't check for self-intersection in this validation + + if (segment2Info.wayId === way1.id) continue; // skip if this way was already checked and only one issue is needed + + if (checkedSingleCrossingWays[segment2Info.wayId]) continue; // mark this way as checked even if there are no crossings + + comparedWays[segment2Info.wayId] = true; + way2 = graph.hasEntity(segment2Info.wayId); + if (!way2) continue; + taggedFeature2 = getFeatureWithFeatureTypeTagsForWay(way2, graph); // only check crossing highway, waterway, building, and railway + + way2FeatureType = getFeatureType(taggedFeature2, graph); + + if (way2FeatureType === null || isLegitCrossing(taggedFeature1.tags, way1FeatureType, taggedFeature2.tags, way2FeatureType)) { + continue; + } // create only one issue for building crossings + + + oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building'; + nAId = segment2Info.nodes[0]; + nBId = segment2Info.nodes[1]; + + if (nAId === n1.id || nAId === n2.id || nBId === n1.id || nBId === n2.id) { + // n1 or n2 is a connection node; skip + continue; + } + + nA = graph.hasEntity(nAId); + if (!nA) continue; + nB = graph.hasEntity(nBId); + if (!nB) continue; + segment1 = [n1.loc, n2.loc]; + segment2 = [nA.loc, nB.loc]; + var point = geoLineIntersection(segment1, segment2); + + if (point) { + edgeCrossInfos.push({ + wayInfos: [{ + way: way1, + featureType: way1FeatureType, + edge: [n1.id, n2.id] + }, { + way: way2, + featureType: way2FeatureType, + edge: [nA.id, nB.id] + }], + crossPoint: point + }); + + if (oneOnly) { + checkedSingleCrossingWays[way2.id] = true; + break; + } + } + } + } + + return edgeCrossInfos; + } + + function waysToCheck(entity, graph) { + var featureType = getFeatureType(entity, graph); + if (!featureType) return []; + + if (entity.type === 'way') { + return [entity]; + } else if (entity.type === 'relation') { + return entity.members.reduce(function (array, member) { + if (member.type === 'way' && ( // only look at geometry ways + !member.role || member.role === 'outer' || member.role === 'inner')) { + var entity = graph.hasEntity(member.id); // don't add duplicates + + if (entity && array.indexOf(entity) === -1) { + array.push(entity); + } + } + + return array; + }, []); + } + + return []; + } + + var validation = function checkCrossingWays(entity, graph) { + var tree = context.history().tree(); + var ways = waysToCheck(entity, graph); + var issues = []; // declare these here to reduce garbage collection + + var wayIndex, crossingIndex, crossings; + + for (wayIndex in ways) { + crossings = findCrossingsByWay(ways[wayIndex], graph, tree); + + for (crossingIndex in crossings) { + issues.push(createIssue(crossings[crossingIndex], graph)); + } + } + + return issues; + }; + + function createIssue(crossing, graph) { + // use the entities with the tags that define the feature type + crossing.wayInfos.sort(function (way1Info, way2Info) { + var type1 = way1Info.featureType; + var type2 = way2Info.featureType; + + if (type1 === type2) { + return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph); + } else if (type1 === 'waterway') { + return true; + } else if (type2 === 'waterway') { + return false; + } + + return type1 < type2; + }); + var entities = crossing.wayInfos.map(function (wayInfo) { + return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph); + }); + var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge]; + var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType]; + var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph); + var featureType1 = crossing.wayInfos[0].featureType; + var featureType2 = crossing.wayInfos[1].featureType; + var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags); + var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') && allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel'); + var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') && allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge'); + var subtype = [featureType1, featureType2].sort().join('-'); + var crossingTypeID = subtype; + + if (isCrossingIndoors) { + crossingTypeID = 'indoor-indoor'; + } else if (isCrossingTunnels) { + crossingTypeID = 'tunnel-tunnel'; + } else if (isCrossingBridges) { + crossingTypeID = 'bridge-bridge'; + } + + if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) { + crossingTypeID += '_connectable'; + } // Differentiate based on the loc rounded to 4 digits, since two ways can cross multiple times. + + + var uniqueID = '' + crossing.crossPoint[0].toFixed(4) + ',' + crossing.crossPoint[1].toFixed(4); + return new validationIssue({ + type: type, + subtype: subtype, + severity: 'warning', + message: function message(context) { + var graph = context.graph(); + var entity1 = graph.hasEntity(this.entityIds[0]), + entity2 = graph.hasEntity(this.entityIds[1]); + return entity1 && entity2 ? _t.html('issues.crossing_ways.message', { + feature: utilDisplayLabel(entity1, graph), + feature2: utilDisplayLabel(entity2, graph) + }) : ''; + }, + reference: showReference, + entityIds: entities.map(function (entity) { + return entity.id; + }), + data: { + edges: edges, + featureTypes: featureTypes, + connectionTags: connectionTags + }, + hash: uniqueID, + loc: crossing.crossPoint, + dynamicFixes: function dynamicFixes(context) { + var mode = context.mode(); + if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) return []; + var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1; + var selectedFeatureType = this.data.featureTypes[selectedIndex]; + var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0]; + var fixes = []; + + if (connectionTags) { + fixes.push(makeConnectWaysFix(this.data.connectionTags)); + } + + if (isCrossingIndoors) { + fixes.push(new validationIssueFix({ + icon: 'iD-icon-layers', + title: _t.html('issues.fix.use_different_levels.title') + })); + } else if (isCrossingTunnels || isCrossingBridges || featureType1 === 'building' || featureType2 === 'building') { + fixes.push(makeChangeLayerFix('higher')); + fixes.push(makeChangeLayerFix('lower')); // can only add bridge/tunnel if both features are lines + } else if (context.graph().geometry(this.entityIds[0]) === 'line' && context.graph().geometry(this.entityIds[1]) === 'line') { + // don't recommend adding bridges to waterways since they're uncommon + if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') { + fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge')); + } // don't recommend adding tunnels under waterways since they're uncommon + + + var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway'; + + if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) { + fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel')); + } + } // repositioning the features is always an option + + + fixes.push(new validationIssueFix({ + icon: 'iD-operation-move', + title: _t.html('issues.fix.reposition_features.title') + })); + return fixes; + } + }); + + function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.crossing_ways.' + crossingTypeID + '.reference')); + } + } + + function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel) { + return new validationIssueFix({ + icon: iconName, + title: _t.html('issues.fix.' + fixTitleID + '.title'), + onClick: function onClick(context) { + var mode = context.mode(); + if (!mode || mode.id !== 'select') return; + var selectedIDs = mode.selectedIDs(); + if (selectedIDs.length !== 1) return; + var selectedWayID = selectedIDs[0]; + if (!context.hasEntity(selectedWayID)) return; + var resultWayIDs = [selectedWayID]; + var edge, crossedEdge, crossedWayID; + + if (this.issue.entityIds[0] === selectedWayID) { + edge = this.issue.data.edges[0]; + crossedEdge = this.issue.data.edges[1]; + crossedWayID = this.issue.entityIds[1]; + } else { + edge = this.issue.data.edges[1]; + crossedEdge = this.issue.data.edges[0]; + crossedWayID = this.issue.entityIds[0]; + } + + var crossingLoc = this.issue.loc; + var projection = context.projection; + + var action = function actionAddStructure(graph) { + var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])]; + var crossedWay = graph.hasEntity(crossedWayID); // use the explicit width of the crossed feature as the structure length, if available + + var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width); + + if (!structLengthMeters) { + // if no explicit width is set, approximate the width based on the tags + structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters(); + } + + if (structLengthMeters) { + if (getFeatureType(crossedWay, graph) === 'railway') { + // bridges over railways are generally much longer than the rail bed itself, compensate + structLengthMeters *= 2; + } + } else { + // should ideally never land here since all rail/water/road tags should have an implied width + structLengthMeters = 8; + } + + var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI; + var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI; + var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2); + if (crossingAngle > Math.PI) crossingAngle -= Math.PI; // lengthen the structure to account for the angle of the crossing + + structLengthMeters = structLengthMeters / 2 / Math.sin(crossingAngle) * 2; // add padding since the structure must extend past the edges of the crossed feature + + structLengthMeters += 4; // clamp the length to a reasonable range + + structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50); + + function geomToProj(geoPoint) { + return [geoLonToMeters(geoPoint[0], geoPoint[1]), geoLatToMeters(geoPoint[1])]; + } + + function projToGeom(projPoint) { + var lat = geoMetersToLat(projPoint[1]); + return [geoMetersToLon(projPoint[0], lat), lat]; + } + + var projEdgeNode1 = geomToProj(edgeNodes[0].loc); + var projEdgeNode2 = geomToProj(edgeNodes[1].loc); + var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2); + var projectedCrossingLoc = geomToProj(crossingLoc); + var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) / geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc); + + function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) { + var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio; + return projToGeom([projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters, projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters]); + } + + var endpointLocGetter1 = function endpointLocGetter1(lengthMeters) { + return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters); + }; + + var endpointLocGetter2 = function endpointLocGetter2(lengthMeters) { + return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters); + }; // avoid creating very short edges from splitting too close to another node + + + var minEdgeLengthMeters = 0.55; // decide where to bound the structure along the way, splitting as necessary + + function determineEndpoint(edge, endNode, locGetter) { + var newNode; + var idealLengthMeters = structLengthMeters / 2; // distance between the crossing location and the end of the edge, + // the maximum length of this side of the structure + + var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc); + + if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) { + // the edge is long enough to insert a new node + // the loc that would result in the full expected length + var idealNodeLoc = locGetter(idealLengthMeters); + newNode = osmNode(); + graph = actionAddMidpoint({ + loc: idealNodeLoc, + edge: edge + }, newNode)(graph); + } else { + var edgeCount = 0; + endNode.parentIntersectionWays(graph).forEach(function (way) { + way.nodes.forEach(function (nodeID) { + if (nodeID === endNode.id) { + if (endNode.id === way.first() && endNode.id !== way.last() || endNode.id === way.last() && endNode.id !== way.first()) { + edgeCount += 1; + } else { + edgeCount += 2; + } + } + }); + }); + + if (edgeCount >= 3) { + // the end node is a junction, try to leave a segment + // between it and the structure - #7202 + var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters; + + if (insetLength > minEdgeLengthMeters) { + var insetNodeLoc = locGetter(insetLength); + newNode = osmNode(); + graph = actionAddMidpoint({ + loc: insetNodeLoc, + edge: edge + }, newNode)(graph); + } + } + } // if the edge is too short to subdivide as desired, then + // just bound the structure at the existing end node + + + if (!newNode) newNode = endNode; + var splitAction = actionSplit([newNode.id]).limitWays(resultWayIDs); // only split selected or created ways + // do the split + + graph = splitAction(graph); + + if (splitAction.getCreatedWayIDs().length) { + resultWayIDs.push(splitAction.getCreatedWayIDs()[0]); + } + + return newNode; + } + + var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1); + var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2); + var structureWay = resultWayIDs.map(function (id) { + return graph.entity(id); + }).find(function (way) { + return way.nodes.indexOf(structEndNode1.id) !== -1 && way.nodes.indexOf(structEndNode2.id) !== -1; + }); + var tags = Object.assign({}, structureWay.tags); // copy tags + + if (bridgeOrTunnel === 'bridge') { + tags.bridge = 'yes'; + tags.layer = '1'; + } else { + var tunnelValue = 'yes'; + + if (getFeatureType(structureWay, graph) === 'waterway') { + // use `tunnel=culvert` for waterways by default + tunnelValue = 'culvert'; + } + + tags.tunnel = tunnelValue; + tags.layer = '-1'; + } // apply the structure tags to the way + + + graph = actionChangeTags(structureWay.id, tags)(graph); + return graph; + }; + + context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation')); + context.enter(modeSelect(context, resultWayIDs)); + } + }); + } + + function makeConnectWaysFix(connectionTags) { + var fixTitleID = 'connect_features'; + + if (connectionTags.ford) { + fixTitleID = 'connect_using_ford'; + } + + return new validationIssueFix({ + icon: 'iD-icon-crossing', + title: _t.html('issues.fix.' + fixTitleID + '.title'), + onClick: function onClick(context) { + var loc = this.issue.loc; + var connectionTags = this.issue.data.connectionTags; + var edges = this.issue.data.edges; + context.perform(function actionConnectCrossingWays(graph) { + // create the new node for the points + var node = osmNode({ + loc: loc, + tags: connectionTags + }); + graph = graph.replace(node); + var nodesToMerge = [node.id]; + var mergeThresholdInMeters = 0.75; + edges.forEach(function (edge) { + var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])]; + var nearby = geoSphericalClosestNode(edgeNodes, loc); // if there is already a suitable node nearby, use that + // use the node if node has no interesting tags or if it is a crossing node #8326 + + if ((!nearby.node.hasInterestingTags() || nearby.node.isCrossing()) && nearby.distance < mergeThresholdInMeters) { + nodesToMerge.push(nearby.node.id); // else add the new node to the way + } else { + graph = actionAddMidpoint({ + loc: loc, + edge: edge + }, node)(graph); + } + }); + + if (nodesToMerge.length > 1) { + // if we're using nearby nodes, merge them with the new node + graph = actionMergeNodes(nodesToMerge, loc)(graph); + } + + return graph; + }, _t('issues.fix.connect_crossing_features.annotation')); + } + }); + } + + function makeChangeLayerFix(higherOrLower) { + return new validationIssueFix({ + icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'), + title: _t.html('issues.fix.tag_this_as_' + higherOrLower + '.title'), + onClick: function onClick(context) { + var mode = context.mode(); + if (!mode || mode.id !== 'select') return; + var selectedIDs = mode.selectedIDs(); + if (selectedIDs.length !== 1) return; + var selectedID = selectedIDs[0]; + if (!this.issue.entityIds.some(function (entityId) { + return entityId === selectedID; + })) return; + var entity = context.hasEntity(selectedID); + if (!entity) return; + var tags = Object.assign({}, entity.tags); // shallow copy + + var layer = tags.layer && Number(tags.layer); + + if (layer && !isNaN(layer)) { + if (higherOrLower === 'higher') { + layer += 1; + } else { + layer -= 1; + } + } else { + if (higherOrLower === 'higher') { + layer = 1; + } else { + layer = -1; + } + } + + tags.layer = layer.toString(); + context.perform(actionChangeTags(entity.id, tags), _t('operations.change_tags.annotation')); + } + }); + } + + validation.type = type; + return validation; + } + + function behaviorDrawWay(context, wayID, mode, startGraph) { + var keybinding = utilKeybinding('drawWay'); + var dispatch = dispatch$8('rejectedSelfIntersection'); + var behavior = behaviorDraw(context); // Must be set by `drawWay.nodeIndex` before each install of this behavior. + + var _nodeIndex; + + var _origWay; + + var _wayGeometry; + + var _headNodeID; + + var _annotation; + + var _pointerHasMoved = false; // The osmNode to be placed. + // This is temporary and just follows the mouse cursor until an "add" event occurs. + + var _drawNode; + + var _didResolveTempEdit = false; + + function createDrawNode(loc) { + // don't make the draw node until we actually need it + _drawNode = osmNode({ + loc: loc + }); + context.pauseChangeDispatch(); + context.replace(function actionAddDrawNode(graph) { + // add the draw node to the graph and insert it into the way + var way = graph.entity(wayID); + return graph.replace(_drawNode).replace(way.addNode(_drawNode.id, _nodeIndex)); + }, _annotation); + context.resumeChangeDispatch(); + setActiveElements(); + } + + function removeDrawNode() { + context.pauseChangeDispatch(); + context.replace(function actionDeleteDrawNode(graph) { + var way = graph.entity(wayID); + return graph.replace(way.removeNode(_drawNode.id)).remove(_drawNode); + }, _annotation); + _drawNode = undefined; + context.resumeChangeDispatch(); + } + + function keydown(d3_event) { + if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) { + if (context.surface().classed('nope')) { + context.surface().classed('nope-suppressed', true); + } + + context.surface().classed('nope', false).classed('nope-disabled', true); + } + } + + function keyup(d3_event) { + if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) { + if (context.surface().classed('nope-suppressed')) { + context.surface().classed('nope', true); + } + + context.surface().classed('nope-suppressed', false).classed('nope-disabled', false); + } + } + + function allowsVertex(d) { + return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph()); + } // related code + // - `mode/drag_node.js` `doMove()` + // - `behavior/draw.js` `click()` + // - `behavior/draw_way.js` `move()` + + + function move(d3_event, datum) { + var loc = context.map().mouseCoordinates(); + if (!_drawNode) createDrawNode(loc); + context.surface().classed('nope-disabled', d3_event.altKey); + var targetLoc = datum && datum.properties && datum.properties.entity && allowsVertex(datum.properties.entity) && datum.properties.entity.loc; + var targetNodes = datum && datum.properties && datum.properties.nodes; + + if (targetLoc) { + // snap to node/vertex - a point target with `.loc` + loc = targetLoc; + } else if (targetNodes) { + // snap to way - a line target with `.nodes` + var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id); + + if (choice) { + loc = choice.loc; + } + } + + context.replace(actionMoveNode(_drawNode.id, loc), _annotation); + _drawNode = context.entity(_drawNode.id); + checkGeometry(true + /* includeDrawNode */ + ); + } // Check whether this edit causes the geometry to break. + // If so, class the surface with a nope cursor. + // `includeDrawNode` - Only check the relevant line segments if finishing drawing + + + function checkGeometry(includeDrawNode) { + var nopeDisabled = context.surface().classed('nope-disabled'); + var isInvalid = isInvalidGeometry(includeDrawNode); + + if (nopeDisabled) { + context.surface().classed('nope', false).classed('nope-suppressed', isInvalid); + } else { + context.surface().classed('nope', isInvalid).classed('nope-suppressed', false); + } + } + + function isInvalidGeometry(includeDrawNode) { + var testNode = _drawNode; // we only need to test the single way we're drawing + + var parentWay = context.graph().entity(wayID); + var nodes = context.graph().childNodes(parentWay).slice(); // shallow copy + + if (includeDrawNode) { + if (parentWay.isClosed()) { + // don't test the last segment for closed ways - #4655 + // (still test the first segment) + nodes.pop(); + } + } else { + // discount the draw node + if (parentWay.isClosed()) { + if (nodes.length < 3) return false; + if (_drawNode) nodes.splice(-2, 1); + testNode = nodes[nodes.length - 2]; + } else { + // there's nothing we need to test if we ignore the draw node on open ways + return false; + } + } + + return testNode && geoHasSelfIntersections(nodes, testNode.id); + } + + function undone() { + // undoing removed the temp edit + _didResolveTempEdit = true; + context.pauseChangeDispatch(); + var nextMode; + + if (context.graph() === startGraph) { + // We've undone back to the initial state before we started drawing. + // Just exit the draw mode without undoing whatever we did before + // we entered the draw mode. + nextMode = modeSelect(context, [wayID]); + } else { + // The `undo` only removed the temporary edit, so here we have to + // manually undo to actually remove the last node we added. We can't + // use the `undo` function since the initial "add" graph doesn't have + // an annotation and so cannot be undone to. + context.pop(1); // continue drawing + + nextMode = mode; + } // clear the redo stack by adding and removing a blank edit + + + context.perform(actionNoop()); + context.pop(1); + context.resumeChangeDispatch(); + context.enter(nextMode); + } + + function setActiveElements() { + if (!_drawNode) return; + context.surface().selectAll('.' + _drawNode.id).classed('active', true); + } + + function resetToStartGraph() { + while (context.graph() !== startGraph) { + context.pop(); + } + } + + var drawWay = function drawWay(surface) { + _drawNode = undefined; + _didResolveTempEdit = false; + _origWay = context.entity(wayID); + + if (typeof _nodeIndex === 'number') { + _headNodeID = _origWay.nodes[_nodeIndex]; + } else if (_origWay.isClosed()) { + _headNodeID = _origWay.nodes[_origWay.nodes.length - 2]; + } else { + _headNodeID = _origWay.nodes[_origWay.nodes.length - 1]; + } + + _wayGeometry = _origWay.geometry(context.graph()); + _annotation = _t((_origWay.nodes.length === (_origWay.isClosed() ? 2 : 1) ? 'operations.start.annotation.' : 'operations.continue.annotation.') + _wayGeometry); + _pointerHasMoved = false; // Push an annotated state for undo to return back to. + // We must make sure to replace or remove it later. + + context.pauseChangeDispatch(); + context.perform(actionNoop(), _annotation); + context.resumeChangeDispatch(); + behavior.hover().initialNodeID(_headNodeID); + behavior.on('move', function () { + _pointerHasMoved = true; + move.apply(this, arguments); + }).on('down', function () { + move.apply(this, arguments); + }).on('downcancel', function () { + if (_drawNode) removeDrawNode(); + }).on('click', drawWay.add).on('clickWay', drawWay.addWay).on('clickNode', drawWay.addNode).on('undo', context.undo).on('cancel', drawWay.cancel).on('finish', drawWay.finish); + select(window).on('keydown.drawWay', keydown).on('keyup.drawWay', keyup); + context.map().dblclickZoomEnable(false).on('drawn.draw', setActiveElements); + setActiveElements(); + surface.call(behavior); + context.history().on('undone.draw', undone); + }; + + drawWay.off = function (surface) { + if (!_didResolveTempEdit) { + // Drawing was interrupted unexpectedly. + // This can happen if the user changes modes, + // clicks geolocate button, a hashchange event occurs, etc. + context.pauseChangeDispatch(); + resetToStartGraph(); + context.resumeChangeDispatch(); + } + + _drawNode = undefined; + _nodeIndex = undefined; + context.map().on('drawn.draw', null); + surface.call(behavior.off).selectAll('.active').classed('active', false); + surface.classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false); + select(window).on('keydown.drawWay', null).on('keyup.drawWay', null); + context.history().on('undone.draw', null); + }; + + function attemptAdd(d, loc, doAdd) { + if (_drawNode) { + // move the node to the final loc in case move wasn't called + // consistently (e.g. on touch devices) + context.replace(actionMoveNode(_drawNode.id, loc), _annotation); + _drawNode = context.entity(_drawNode.id); + } else { + createDrawNode(loc); + } + + checkGeometry(true + /* includeDrawNode */ + ); + + if (d && d.properties && d.properties.nope || context.surface().classed('nope')) { + if (!_pointerHasMoved) { + // prevent the temporary draw node from appearing on touch devices + removeDrawNode(); + } + + dispatch.call('rejectedSelfIntersection', this); + return; // can't click here + } + + context.pauseChangeDispatch(); + doAdd(); // we just replaced the temporary edit with the real one + + _didResolveTempEdit = true; + context.resumeChangeDispatch(); + context.enter(mode); + } // Accept the current position of the drawing node + + + drawWay.add = function (loc, d) { + attemptAdd(d, loc, function () {// don't need to do anything extra + }); + }; // Connect the way to an existing way + + + drawWay.addWay = function (loc, edge, d) { + attemptAdd(d, loc, function () { + context.replace(actionAddMidpoint({ + loc: loc, + edge: edge + }, _drawNode), _annotation); + }); + }; // Connect the way to an existing node + + + drawWay.addNode = function (node, d) { + // finish drawing if the mapper targets the prior node + if (node.id === _headNodeID || // or the first node when drawing an area + _origWay.isClosed() && node.id === _origWay.first()) { + drawWay.finish(); + return; + } + + attemptAdd(d, node.loc, function () { + context.replace(function actionReplaceDrawNode(graph) { + // remove the temporary draw node and insert the existing node + // at the same index + graph = graph.replace(graph.entity(wayID).removeNode(_drawNode.id)).remove(_drawNode); + return graph.replace(graph.entity(wayID).addNode(node.id, _nodeIndex)); + }, _annotation); + }); + }; + /** + * @param {(typeof osmWay)[]} ways + * @returns {"line" | "area" | "generic"} + */ + + + function getFeatureType(ways) { + if (ways.every(function (way) { + return way.isClosed(); + })) return 'area'; + if (ways.every(function (way) { + return !way.isClosed(); + })) return 'line'; + return 'generic'; + } + /** see PR #8671 */ + + + function followMode() { + if (_didResolveTempEdit) return; + + try { + // get the last 2 added nodes. + // check if they are both part of only oneway (the same one) + // check if the ways that they're part of are the same way + // find index of the last two nodes, to determine the direction to travel around the existing way + // add the next node to the way we are drawing + // if we're drawing an area, the first node = last node. + var isDrawingArea = _origWay.nodes[0] === _origWay.nodes.slice(-1)[0]; + + var _origWay$nodes$slice = _origWay.nodes.slice(isDrawingArea ? -3 : -2), + _origWay$nodes$slice2 = _slicedToArray(_origWay$nodes$slice, 2), + secondLastNodeId = _origWay$nodes$slice2[0], + lastNodeId = _origWay$nodes$slice2[1]; // Unlike startGraph, the full history graph may contain unsaved vertices to follow. + // https://github.com/openstreetmap/iD/issues/8749 + + + var historyGraph = context.history().graph(); + + if (!lastNodeId || !secondLastNodeId || !historyGraph.hasEntity(lastNodeId) || !historyGraph.hasEntity(secondLastNodeId)) { + context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('operations.follow.error.needs_more_initial_nodes'))(); + return; + } // If the way has looped over itself, follow some other way. + + + var lastNodesParents = historyGraph.parentWays(historyGraph.entity(lastNodeId)).filter(function (w) { + return w.id !== wayID; + }); + var secondLastNodesParents = historyGraph.parentWays(historyGraph.entity(secondLastNodeId)).filter(function (w) { + return w.id !== wayID; + }); + var featureType = getFeatureType(lastNodesParents); + + if (lastNodesParents.length !== 1 || secondLastNodesParents.length === 0) { + context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t("operations.follow.error.intersection_of_multiple_ways.".concat(featureType)))(); + return; + } // Check if the last node's parent is also the parent of the second last node. + // The last node must only have one parent, but the second last node can have + // multiple parents. + + + if (!secondLastNodesParents.some(function (n) { + return n.id === lastNodesParents[0].id; + })) { + context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t("operations.follow.error.intersection_of_different_ways.".concat(featureType)))(); + return; + } + + var way = lastNodesParents[0]; + var indexOfLast = way.nodes.indexOf(lastNodeId); + var indexOfSecondLast = way.nodes.indexOf(secondLastNodeId); // for a closed way, the first/last node is the same so it appears twice in the array, + // but indexOf always finds the first occurrence. This is only an issue when following a way + // in descending order + + var isDescendingPastZero = indexOfLast === way.nodes.length - 2 && indexOfSecondLast === 0; + var nextNodeIndex = indexOfLast + (indexOfLast > indexOfSecondLast && !isDescendingPastZero ? 1 : -1); // if we're following a closed way and we pass the first/last node, the next index will be -1 + + if (nextNodeIndex === -1) nextNodeIndex = indexOfSecondLast === 1 ? way.nodes.length - 2 : 1; + var nextNode = historyGraph.entity(way.nodes[nextNodeIndex]); + drawWay.addNode(nextNode, { + geometry: { + type: 'Point', + coordinates: nextNode.loc + }, + id: nextNode.id, + properties: { + target: true, + entity: nextNode + } + }); + } catch (ex) { + context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('operations.follow.error.unknown'))(); + } + } + + keybinding.on(_t('operations.follow.key'), followMode); + select(document).call(keybinding); // Finish the draw operation, removing the temporary edit. + // If the way has enough nodes to be valid, it's selected. + // Otherwise, delete everything and return to browse mode. + + drawWay.finish = function () { + checkGeometry(false + /* includeDrawNode */ + ); + + if (context.surface().classed('nope')) { + dispatch.call('rejectedSelfIntersection', this); + return; // can't click here + } + + context.pauseChangeDispatch(); // remove the temporary edit + + context.pop(1); + _didResolveTempEdit = true; + context.resumeChangeDispatch(); + var way = context.hasEntity(wayID); + + if (!way || way.isDegenerate()) { + drawWay.cancel(); + return; + } + + window.setTimeout(function () { + context.map().dblclickZoomEnable(true); + }, 1000); + var isNewFeature = !mode.isContinuing; + context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature)); + }; // Cancel the draw operation, delete everything, and return to browse mode. + + + drawWay.cancel = function () { + context.pauseChangeDispatch(); + resetToStartGraph(); + context.resumeChangeDispatch(); + window.setTimeout(function () { + context.map().dblclickZoomEnable(true); + }, 1000); + context.surface().classed('nope', false).classed('nope-disabled', false).classed('nope-suppressed', false); + context.enter(modeBrowse(context)); + }; + + drawWay.nodeIndex = function (val) { + if (!arguments.length) return _nodeIndex; + _nodeIndex = val; + return drawWay; + }; + + drawWay.activeID = function () { + if (!arguments.length) return _drawNode && _drawNode.id; // no assign + + return drawWay; + }; + + return utilRebind(drawWay, dispatch, 'on'); + } + + function modeDrawLine(context, wayID, startGraph, button, affix, continuing) { + var mode = { + button: button, + id: 'draw-line' + }; + var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawLine', function () { + context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.lines'))(); + }); + mode.wayID = wayID; + mode.isContinuing = continuing; + + mode.enter = function () { + behavior.nodeIndex(affix === 'prefix' ? 0 : undefined); + context.install(behavior); + }; + + mode.exit = function () { + context.uninstall(behavior); + }; + + mode.selectedIDs = function () { + return [wayID]; + }; + + mode.activeID = function () { + return behavior && behavior.activeID() || []; + }; + + return mode; + } + + function validationDisconnectedWay() { + var type = 'disconnected_way'; + + function isTaggedAsHighway(entity) { + return osmRoutableHighwayTagValues[entity.tags.highway]; + } + + var validation = function checkDisconnectedWay(entity, graph) { + var routingIslandWays = routingIslandForEntity(entity); + if (!routingIslandWays) return []; + return [new validationIssue({ + type: type, + subtype: 'highway', + severity: 'warning', + message: function message(context) { + var entity = this.entityIds.length && context.hasEntity(this.entityIds[0]); + var label = entity && utilDisplayLabel(entity, context.graph()); + return _t.html('issues.disconnected_way.routable.message', { + count: this.entityIds.length, + highway: label + }); + }, + reference: showReference, + entityIds: Array.from(routingIslandWays).map(function (way) { + return way.id; + }), + dynamicFixes: makeFixes + })]; + + function makeFixes(context) { + var fixes = []; + var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]); + + if (singleEntity) { + if (singleEntity.type === 'way' && !singleEntity.isClosed()) { + var textDirection = _mainLocalizer.textDirection(); + var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start'); + if (startFix) fixes.push(startFix); + var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end'); + if (endFix) fixes.push(endFix); + } + + if (!fixes.length) { + fixes.push(new validationIssueFix({ + title: _t.html('issues.fix.connect_feature.title') + })); + } + + fixes.push(new validationIssueFix({ + icon: 'iD-operation-delete', + title: _t.html('issues.fix.delete_feature.title'), + entityIds: [singleEntity.id], + onClick: function onClick(context) { + var id = this.issue.entityIds[0]; + var operation = operationDelete(context, [id]); + + if (!operation.disabled()) { + operation(); + } + } + })); + } else { + fixes.push(new validationIssueFix({ + title: _t.html('issues.fix.connect_features.title') + })); + } + + return fixes; + } + + function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.disconnected_way.routable.reference')); + } + + function routingIslandForEntity(entity) { + var routingIsland = new Set(); // the interconnected routable features + + var waysToCheck = []; // the queue of remaining routable ways to traverse + + function queueParentWays(node) { + graph.parentWays(node).forEach(function (parentWay) { + if (!routingIsland.has(parentWay) && // only check each feature once + isRoutableWay(parentWay, false)) { + // only check routable features + routingIsland.add(parentWay); + waysToCheck.push(parentWay); + } + }); + } + + if (entity.type === 'way' && isRoutableWay(entity, true)) { + routingIsland.add(entity); + waysToCheck.push(entity); + } else if (entity.type === 'node' && isRoutableNode(entity)) { + routingIsland.add(entity); + queueParentWays(entity); + } else { + // this feature isn't routable, cannot be a routing island + return null; + } + + while (waysToCheck.length) { + var wayToCheck = waysToCheck.pop(); + var childNodes = graph.childNodes(wayToCheck); + + for (var i in childNodes) { + var vertex = childNodes[i]; + + if (isConnectedVertex(vertex)) { + // found a link to the wider network, not a routing island + return null; + } + + if (isRoutableNode(vertex)) { + routingIsland.add(vertex); + } + + queueParentWays(vertex); + } + } // no network link found, this is a routing island, return its members + + + return routingIsland; + } + + function isConnectedVertex(vertex) { + // assume ways overlapping unloaded tiles are connected to the wider road network - #5938 + var osm = services.osm; + if (osm && !osm.isDataLoaded(vertex.loc)) return true; // entrances are considered connected + + if (vertex.tags.entrance && vertex.tags.entrance !== 'no') return true; + if (vertex.tags.amenity === 'parking_entrance') return true; + return false; + } + + function isRoutableNode(node) { + // treat elevators as distinct features in the highway network + if (node.tags.highway === 'elevator') return true; + return false; + } + + function isRoutableWay(way, ignoreInnerWays) { + if (isTaggedAsHighway(way) || way.tags.route === 'ferry') return true; + return graph.parentRelations(way).some(function (parentRelation) { + if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true; + if (parentRelation.isMultipolygon() && isTaggedAsHighway(parentRelation) && (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) return true; + return false; + }); + } + + function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) { + var vertex = graph.hasEntity(vertexID); + if (!vertex || vertex.tags.noexit === 'yes') return null; + var useLeftContinue = whichEnd === 'start' && textDirection === 'ltr' || whichEnd === 'end' && textDirection === 'rtl'; + return new validationIssueFix({ + icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''), + title: _t.html('issues.fix.continue_from_' + whichEnd + '.title'), + entityIds: [vertexID], + onClick: function onClick(context) { + var wayId = this.issue.entityIds[0]; + var way = context.hasEntity(wayId); + var vertexId = this.entityIds[0]; + var vertex = context.hasEntity(vertexId); + if (!way || !vertex) return; // make sure the vertex is actually visible and editable + + var map = context.map(); + + if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) { + map.zoomToEase(vertex); + } + + context.enter(modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true)); + } + }); + } + }; + + validation.type = type; + return validation; + } + + function validationFormatting() { + var type = 'invalid_format'; + + var validation = function validation(entity) { + var issues = []; + + function isValidEmail(email) { + // Emails in OSM are going to be official so they should be pretty simple + // Using negated lists to better support all possible unicode characters (#6494) + var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i; // An empty value is also acceptable + + return !email || valid_email.test(email); + } + /* + function isSchemePresent(url) { + var valid_scheme = /^https?:\/\//i; + return (!url || valid_scheme.test(url)); + } + */ + + + function showReferenceEmail(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.invalid_format.email.reference')); + } + /* + function showReferenceWebsite(selection) { + selection.selectAll('.issue-reference') + .data([0]) + .enter() + .append('div') + .attr('class', 'issue-reference') + .html(t.html('issues.invalid_format.website.reference')); + } + if (entity.tags.website) { + // Multiple websites are possible + // If ever we support ES6, arrow functions make this nicer + var websites = entity.tags.website + .split(';') + .map(function(s) { return s.trim(); }) + .filter(function(x) { return !isSchemePresent(x); }); + if (websites.length) { + issues.push(new validationIssue({ + type: type, + subtype: 'website', + severity: 'warning', + message: function(context) { + var entity = context.hasEntity(this.entityIds[0]); + return entity ? t.html('issues.invalid_format.website.message' + this.data, + { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : ''; + }, + reference: showReferenceWebsite, + entityIds: [entity.id], + hash: websites.join(), + data: (websites.length > 1) ? '_multi' : '' + })); + } + } + */ + + + if (entity.tags.email) { + // Multiple emails are possible + var emails = entity.tags.email.split(';').map(function (s) { + return s.trim(); + }).filter(function (x) { + return !isValidEmail(x); + }); + + if (emails.length) { + issues.push(new validationIssue({ + type: type, + subtype: 'email', + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]); + return entity ? _t.html('issues.invalid_format.email.message' + this.data, { + feature: utilDisplayLabel(entity, context.graph()), + email: emails.join(', ') + }) : ''; + }, + reference: showReferenceEmail, + entityIds: [entity.id], + hash: emails.join(), + data: emails.length > 1 ? '_multi' : '' + })); + } + } + + return issues; + }; + + validation.type = type; + return validation; + } + + function validationHelpRequest(context) { + var type = 'help_request'; + + var validation = function checkFixmeTag(entity) { + if (!entity.tags.fixme) return []; // don't flag fixmes on features added by the user + + if (entity.version === undefined) return []; + + if (entity.v !== undefined) { + var baseEntity = context.history().base().hasEntity(entity.id); // don't flag fixmes added by the user on existing features + + if (!baseEntity || !baseEntity.tags.fixme) return []; + } + + return [new validationIssue({ + type: type, + subtype: 'fixme_tag', + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]); + return entity ? _t.html('issues.fixme_tag.message', { + feature: utilDisplayLabel(entity, context.graph(), true + /* verbose */ + ) + }) : ''; + }, + dynamicFixes: function dynamicFixes() { + return [new validationIssueFix({ + title: _t.html('issues.fix.address_the_concern.title') + })]; + }, + reference: showReference, + entityIds: [entity.id] + })]; + + function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.fixme_tag.reference')); + } + }; + + validation.type = type; + return validation; + } + + function validationImpossibleOneway() { + var type = 'impossible_oneway'; + + var validation = function checkImpossibleOneway(entity, graph) { + if (entity.type !== 'way' || entity.geometry(graph) !== 'line') return []; + if (entity.isClosed()) return []; + if (!typeForWay(entity)) return []; + if (!isOneway(entity)) return []; + var firstIssues = issuesForNode(entity, entity.first()); + var lastIssues = issuesForNode(entity, entity.last()); + return firstIssues.concat(lastIssues); + + function typeForWay(way) { + if (way.geometry(graph) !== 'line') return null; + if (osmRoutableHighwayTagValues[way.tags.highway]) return 'highway'; + if (osmFlowingWaterwayTagValues[way.tags.waterway]) return 'waterway'; + return null; + } + + function isOneway(way) { + if (way.tags.oneway === 'yes') return true; + if (way.tags.oneway) return false; + + for (var key in way.tags) { + if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) { + return true; + } + } + + return false; + } + + function nodeOccursMoreThanOnce(way, nodeID) { + var occurrences = 0; + + for (var index in way.nodes) { + if (way.nodes[index] === nodeID) { + occurrences += 1; + if (occurrences > 1) return true; + } + } + + return false; + } + + function isConnectedViaOtherTypes(way, node) { + var wayType = typeForWay(way); + + if (wayType === 'highway') { + // entrances are considered connected + if (node.tags.entrance && node.tags.entrance !== 'no') return true; + if (node.tags.amenity === 'parking_entrance') return true; + } else if (wayType === 'waterway') { + if (node.id === way.first()) { + // multiple waterways may start at the same spring + if (node.tags.natural === 'spring') return true; + } else { + // multiple waterways may end at the same drain + if (node.tags.manhole === 'drain') return true; + } + } + + return graph.parentWays(node).some(function (parentWay) { + if (parentWay.id === way.id) return false; + + if (wayType === 'highway') { + // allow connections to highway areas + if (parentWay.geometry(graph) === 'area' && osmRoutableHighwayTagValues[parentWay.tags.highway]) return true; // count connections to ferry routes as connected + + if (parentWay.tags.route === 'ferry') return true; + return graph.parentRelations(parentWay).some(function (parentRelation) { + if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true; // allow connections to highway multipolygons + + return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway]; + }); + } else if (wayType === 'waterway') { + // multiple waterways may start or end at a water body at the same node + if (parentWay.tags.natural === 'water' || parentWay.tags.natural === 'coastline') return true; + } + + return false; + }); + } + + function issuesForNode(way, nodeID) { + var isFirst = nodeID === way.first(); + var wayType = typeForWay(way); // ignore if this way is self-connected at this node + + if (nodeOccursMoreThanOnce(way, nodeID)) return []; + var osm = services.osm; + if (!osm) return []; + var node = graph.hasEntity(nodeID); // ignore if this node or its tile are unloaded + + if (!node || !osm.isDataLoaded(node.loc)) return []; + if (isConnectedViaOtherTypes(way, node)) return []; + var attachedWaysOfSameType = graph.parentWays(node).filter(function (parentWay) { + if (parentWay.id === way.id) return false; + return typeForWay(parentWay) === wayType; + }); // assume it's okay for waterways to start or end disconnected for now + + if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) return []; + var attachedOneways = attachedWaysOfSameType.filter(function (attachedWay) { + return isOneway(attachedWay); + }); // ignore if the way is connected to some non-oneway features + + if (attachedOneways.length < attachedWaysOfSameType.length) return []; + + if (attachedOneways.length) { + var connectedEndpointsOkay = attachedOneways.some(function (attachedOneway) { + if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) return true; + if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) return true; + return false; + }); + if (connectedEndpointsOkay) return []; + } + + var placement = isFirst ? 'start' : 'end', + messageID = wayType + '.', + referenceID = wayType + '.'; + + if (wayType === 'waterway') { + messageID += 'connected.' + placement; + referenceID += 'connected'; + } else { + messageID += placement; + referenceID += placement; + } + + return [new validationIssue({ + type: type, + subtype: wayType, + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]); + return entity ? _t.html('issues.impossible_oneway.' + messageID + '.message', { + feature: utilDisplayLabel(entity, context.graph()) + }) : ''; + }, + reference: getReference(referenceID), + entityIds: [way.id, node.id], + dynamicFixes: function dynamicFixes() { + var fixes = []; + + if (attachedOneways.length) { + fixes.push(new validationIssueFix({ + icon: 'iD-operation-reverse', + title: _t.html('issues.fix.reverse_feature.title'), + entityIds: [way.id], + onClick: function onClick(context) { + var id = this.issue.entityIds[0]; + context.perform(actionReverse(id), _t('operations.reverse.annotation.line', { + n: 1 + })); + } + })); + } + + if (node.tags.noexit !== 'yes') { + var textDirection = _mainLocalizer.textDirection(); + var useLeftContinue = isFirst && textDirection === 'ltr' || !isFirst && textDirection === 'rtl'; + fixes.push(new validationIssueFix({ + icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''), + title: _t.html('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'), + onClick: function onClick(context) { + var entityID = this.issue.entityIds[0]; + var vertexID = this.issue.entityIds[1]; + var way = context.entity(entityID); + var vertex = context.entity(vertexID); + continueDrawing(way, vertex, context); + } + })); + } + + return fixes; + }, + loc: node.loc + })]; + + function getReference(referenceID) { + return function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.impossible_oneway.' + referenceID + '.reference')); + }; + } + } + }; + + function continueDrawing(way, vertex, context) { + // make sure the vertex is actually visible and editable + var map = context.map(); + + if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) { + map.zoomToEase(vertex); + } + + context.enter(modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true)); + } + + validation.type = type; + return validation; + } + + function validationIncompatibleSource() { + var type = 'incompatible_source'; + var incompatibleRules = [{ + id: 'amap', + regex: /(amap|autonavi|mapabc|高德)/i + }, { + id: 'baidu', + regex: /(baidu|mapbar|百度)/i + }, { + id: 'google', + regex: /google/i, + exceptRegex: /((books|drive)\.google|google\s?(books|drive|plus))/i + }]; + + var validation = function checkIncompatibleSource(entity) { + var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';'); + if (!entitySources) return []; + var entityID = entity.id; + return entitySources.map(function (source) { + var matchRule = incompatibleRules.find(function (rule) { + if (!rule.regex.test(source)) return false; + if (rule.exceptRegex && rule.exceptRegex.test(source)) return false; + return true; + }); + if (!matchRule) return null; + return new validationIssue({ + type: type, + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(entityID); + return entity ? _t.html('issues.incompatible_source.feature.message', { + feature: utilDisplayLabel(entity, context.graph(), true + /* verbose */ + ), + value: source + }) : ''; + }, + reference: getReference(matchRule.id), + entityIds: [entityID], + hash: source, + dynamicFixes: function dynamicFixes() { + return [new validationIssueFix({ + title: _t.html('issues.fix.remove_proprietary_data.title') + })]; + } + }); + }).filter(Boolean); + + function getReference(id) { + return function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html("issues.incompatible_source.reference.".concat(id))); + }; + } + }; + + validation.type = type; + return validation; + } + + function validationMaprules() { + var type = 'maprules'; + + var validation = function checkMaprules(entity, graph) { + if (!services.maprules) return []; + var rules = services.maprules.validationRules(); + var issues = []; + + for (var i = 0; i < rules.length; i++) { + var rule = rules[i]; + rule.findIssues(entity, graph, issues); + } + + return issues; + }; + + validation.type = type; + return validation; + } + + function validationMismatchedGeometry() { + var type = 'mismatched_geometry'; + + function tagSuggestingLineIsArea(entity) { + if (entity.type !== 'way' || entity.isClosed()) return null; + var tagSuggestingArea = entity.tagSuggestingArea(); + + if (!tagSuggestingArea) { + return null; + } + + var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line'); + var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area'); + + if (asLine && asArea && asLine === asArea) { + // these tags also allow lines and making this an area wouldn't matter + return null; + } + + return tagSuggestingArea; + } + + function makeConnectEndpointsFixOnClick(way, graph) { + // must have at least three nodes to close this automatically + if (way.nodes.length < 3) return null; + var nodes = graph.childNodes(way), + testNodes; + var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length - 1].loc); // if the distance is very small, attempt to merge the endpoints + + if (firstToLastDistanceMeters < 0.75) { + testNodes = nodes.slice(); // shallow copy + + testNodes.pop(); + testNodes.push(testNodes[0]); // make sure this will not create a self-intersection + + if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) { + return function (context) { + var way = context.entity(this.issue.entityIds[0]); + context.perform(actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length - 1]], nodes[0].loc), _t('issues.fix.connect_endpoints.annotation')); + }; + } + } // if the points were not merged, attempt to close the way + + + testNodes = nodes.slice(); // shallow copy + + testNodes.push(testNodes[0]); // make sure this will not create a self-intersection + + if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) { + return function (context) { + var wayId = this.issue.entityIds[0]; + var way = context.entity(wayId); + var nodeId = way.nodes[0]; + var index = way.nodes.length; + context.perform(actionAddVertex(wayId, nodeId, index), _t('issues.fix.connect_endpoints.annotation')); + }; + } + } + + function lineTaggedAsAreaIssue(entity) { + var tagSuggestingArea = tagSuggestingLineIsArea(entity); + if (!tagSuggestingArea) return null; + return new validationIssue({ + type: type, + subtype: 'area_as_line', + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]); + return entity ? _t.html('issues.tag_suggests_area.message', { + feature: utilDisplayLabel(entity, 'area', true + /* verbose */ + ), + tag: utilTagText({ + tags: tagSuggestingArea + }) + }) : ''; + }, + reference: showReference, + entityIds: [entity.id], + hash: JSON.stringify(tagSuggestingArea), + dynamicFixes: function dynamicFixes(context) { + var fixes = []; + var entity = context.entity(this.entityIds[0]); + var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph()); + fixes.push(new validationIssueFix({ + title: _t.html('issues.fix.connect_endpoints.title'), + onClick: connectEndsOnClick + })); + fixes.push(new validationIssueFix({ + icon: 'iD-operation-delete', + title: _t.html('issues.fix.remove_tag.title'), + onClick: function onClick(context) { + var entityId = this.issue.entityIds[0]; + var entity = context.entity(entityId); + var tags = Object.assign({}, entity.tags); // shallow copy + + for (var key in tagSuggestingArea) { + delete tags[key]; + } + + context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_tag.annotation')); + } + })); + return fixes; + } + }); + + function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.tag_suggests_area.reference')); + } + } + + function vertexPointIssue(entity, graph) { + // we only care about nodes + if (entity.type !== 'node') return null; // ignore tagless points + + if (Object.keys(entity.tags).length === 0) return null; // address lines are special so just ignore them + + if (entity.isOnAddressLine(graph)) return null; + var geometry = entity.geometry(graph); + var allowedGeometries = osmNodeGeometriesForTags(entity.tags); + + if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) { + return new validationIssue({ + type: type, + subtype: 'vertex_as_point', + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]); + return entity ? _t.html('issues.vertex_as_point.message', { + feature: utilDisplayLabel(entity, 'vertex', true + /* verbose */ + ) + }) : ''; + }, + reference: function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.vertex_as_point.reference')); + }, + entityIds: [entity.id] + }); + } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) { + return new validationIssue({ + type: type, + subtype: 'point_as_vertex', + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]); + return entity ? _t.html('issues.point_as_vertex.message', { + feature: utilDisplayLabel(entity, 'point', true + /* verbose */ + ) + }) : ''; + }, + reference: function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.point_as_vertex.reference')); + }, + entityIds: [entity.id], + dynamicFixes: extractPointDynamicFixes + }); + } + + return null; + } + + function otherMismatchIssue(entity, graph) { + // ignore boring features + if (!entity.hasInterestingTags()) return null; + if (entity.type !== 'node' && entity.type !== 'way') return null; // address lines are special so just ignore them + + if (entity.type === 'node' && entity.isOnAddressLine(graph)) return null; + var sourceGeom = entity.geometry(graph); + var targetGeoms = entity.type === 'way' ? ['point', 'vertex'] : ['line', 'area']; + if (sourceGeom === 'area') targetGeoms.unshift('line'); + var asSource = _mainPresetIndex.match(entity, graph); + var targetGeom = targetGeoms.find(function (nodeGeom) { + var asTarget = _mainPresetIndex.matchTags(entity.tags, nodeGeom); + if (!asSource || !asTarget || asSource === asTarget || // sometimes there are two presets with the same tags for different geometries + fastDeepEqual(asSource.tags, asTarget.tags)) return false; + if (asTarget.isFallback()) return false; + var primaryKey = Object.keys(asTarget.tags)[0]; // special case: buildings-as-points are discouraged by iD, but common in OSM, so ignore them + + if (primaryKey === 'building') return false; + if (asTarget.tags[primaryKey] === '*') return false; + return asSource.isFallback() || asSource.tags[primaryKey] === '*'; + }); + if (!targetGeom) return null; + var subtype = targetGeom + '_as_' + sourceGeom; + if (targetGeom === 'vertex') targetGeom = 'point'; + if (sourceGeom === 'vertex') sourceGeom = 'point'; + var referenceId = targetGeom + '_as_' + sourceGeom; + var dynamicFixes; + + if (targetGeom === 'point') { + dynamicFixes = extractPointDynamicFixes; + } else if (sourceGeom === 'area' && targetGeom === 'line') { + dynamicFixes = lineToAreaDynamicFixes; + } + + return new validationIssue({ + type: type, + subtype: subtype, + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]); + return entity ? _t.html('issues.' + referenceId + '.message', { + feature: utilDisplayLabel(entity, targetGeom, true + /* verbose */ + ) + }) : ''; + }, + reference: function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.mismatched_geometry.reference')); + }, + entityIds: [entity.id], + dynamicFixes: dynamicFixes + }); + } + + function lineToAreaDynamicFixes(context) { + var convertOnClick; + var entityId = this.entityIds[0]; + var entity = context.entity(entityId); + var tags = Object.assign({}, entity.tags); // shallow copy + + delete tags.area; + + if (!osmTagSuggestingArea(tags)) { + // if removing the area tag would make this a line, offer that as a quick fix + convertOnClick = function convertOnClick(context) { + var entityId = this.issue.entityIds[0]; + var entity = context.entity(entityId); + var tags = Object.assign({}, entity.tags); // shallow copy + + if (tags.area) { + delete tags.area; + } + + context.perform(actionChangeTags(entityId, tags), _t('issues.fix.convert_to_line.annotation')); + }; + } + + return [new validationIssueFix({ + icon: 'iD-icon-line', + title: _t.html('issues.fix.convert_to_line.title'), + onClick: convertOnClick + })]; + } + + function extractPointDynamicFixes(context) { + var entityId = this.entityIds[0]; + var extractOnClick = null; + + if (!context.hasHiddenConnections(entityId)) { + extractOnClick = function extractOnClick(context) { + var entityId = this.issue.entityIds[0]; + var action = actionExtract(entityId, context.projection); + context.perform(action, _t('operations.extract.annotation', { + n: 1 + })); // re-enter mode to trigger updates + + context.enter(modeSelect(context, [action.getExtractedNodeID()])); + }; + } + + return [new validationIssueFix({ + icon: 'iD-operation-extract', + title: _t.html('issues.fix.extract_point.title'), + onClick: extractOnClick + })]; + } + + function unclosedMultipolygonPartIssues(entity, graph) { + if (entity.type !== 'relation' || !entity.isMultipolygon() || entity.isDegenerate() || // cannot determine issues for incompletely-downloaded relations + !entity.isComplete(graph)) return []; + var sequences = osmJoinWays(entity.members, graph); + var issues = []; + + for (var i in sequences) { + var sequence = sequences[i]; + if (!sequence.nodes) continue; + var firstNode = sequence.nodes[0]; + var lastNode = sequence.nodes[sequence.nodes.length - 1]; // part is closed if the first and last nodes are the same + + if (firstNode === lastNode) continue; + var issue = new validationIssue({ + type: type, + subtype: 'unclosed_multipolygon_part', + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]); + return entity ? _t.html('issues.unclosed_multipolygon_part.message', { + feature: utilDisplayLabel(entity, context.graph(), true + /* verbose */ + ) + }) : ''; + }, + reference: showReference, + loc: sequence.nodes[0].loc, + entityIds: [entity.id], + hash: sequence.map(function (way) { + return way.id; + }).join() + }); + issues.push(issue); + } + + return issues; + + function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unclosed_multipolygon_part.reference')); + } + } + + var validation = function checkMismatchedGeometry(entity, graph) { + var vertexPoint = vertexPointIssue(entity, graph); + if (vertexPoint) return [vertexPoint]; + var lineAsArea = lineTaggedAsAreaIssue(entity); + if (lineAsArea) return [lineAsArea]; + var mismatch = otherMismatchIssue(entity, graph); + if (mismatch) return [mismatch]; + return unclosedMultipolygonPartIssues(entity, graph); + }; + + validation.type = type; + return validation; + } + + function validationMissingRole() { + var type = 'missing_role'; + + var validation = function checkMissingRole(entity, graph) { + var issues = []; + + if (entity.type === 'way') { + graph.parentRelations(entity).forEach(function (relation) { + if (!relation.isMultipolygon()) return; + var member = relation.memberById(entity.id); + + if (member && isMissingRole(member)) { + issues.push(makeIssue(entity, relation, member)); + } + }); + } else if (entity.type === 'relation' && entity.isMultipolygon()) { + entity.indexedMembers().forEach(function (member) { + var way = graph.hasEntity(member.id); + + if (way && isMissingRole(member)) { + issues.push(makeIssue(way, entity, member)); + } + }); + } + + return issues; + }; + + function isMissingRole(member) { + return !member.role || !member.role.trim().length; + } + + function makeIssue(way, relation, member) { + return new validationIssue({ + type: type, + severity: 'warning', + message: function message(context) { + var member = context.hasEntity(this.entityIds[1]), + relation = context.hasEntity(this.entityIds[0]); + return member && relation ? _t.html('issues.missing_role.message', { + member: utilDisplayLabel(member, context.graph()), + relation: utilDisplayLabel(relation, context.graph()) + }) : ''; + }, + reference: showReference, + entityIds: [relation.id, way.id], + data: { + member: member + }, + hash: member.index.toString(), + dynamicFixes: function dynamicFixes() { + return [makeAddRoleFix('inner'), makeAddRoleFix('outer'), new validationIssueFix({ + icon: 'iD-operation-delete', + title: _t.html('issues.fix.remove_from_relation.title'), + onClick: function onClick(context) { + context.perform(actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index), _t('operations.delete_member.annotation', { + n: 1 + })); + } + })]; + } + }); + + function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.missing_role.multipolygon.reference')); + } + } + + function makeAddRoleFix(role) { + return new validationIssueFix({ + title: _t.html('issues.fix.set_as_' + role + '.title'), + onClick: function onClick(context) { + var oldMember = this.issue.data.member; + var member = { + id: this.issue.entityIds[1], + type: oldMember.type, + role: role + }; + context.perform(actionChangeMember(this.issue.entityIds[0], member, oldMember.index), _t('operations.change_role.annotation', { + n: 1 + })); + } + }); + } + + validation.type = type; + return validation; + } + + function validationMissingTag(context) { + var type = 'missing_tag'; + + function hasDescriptiveTags(entity, graph) { + var onlyAttributeKeys = ['description', 'name', 'note', 'start_date']; + var entityDescriptiveKeys = Object.keys(entity.tags).filter(function (k) { + if (k === 'area' || !osmIsInterestingTag(k)) return false; + return !onlyAttributeKeys.some(function (attributeKey) { + return k === attributeKey || k.indexOf(attributeKey + ':') === 0; + }); + }); + + if (entity.type === 'relation' && entityDescriptiveKeys.length === 1 && entity.tags.type === 'multipolygon') { + // this relation's only interesting tag just says its a multipolygon, + // which is not descriptive enough + // It's okay for a simple multipolygon to have no descriptive tags + // if its outer way has them (old model, see `outdated_tags.js`) + return osmOldMultipolygonOuterMemberOfRelation(entity, graph); + } + + return entityDescriptiveKeys.length > 0; + } + + function isUnknownRoad(entity) { + return entity.type === 'way' && entity.tags.highway === 'road'; + } + + function isUntypedRelation(entity) { + return entity.type === 'relation' && !entity.tags.type; + } + + var validation = function checkMissingTag(entity, graph) { + var subtype; + var osm = context.connection(); + var isUnloadedNode = entity.type === 'node' && osm && !osm.isDataLoaded(entity.loc); // we can't know if the node is a vertex if the tile is undownloaded + + if (!isUnloadedNode && // allow untagged nodes that are part of ways + entity.geometry(graph) !== 'vertex' && // allow untagged entities that are part of relations + !entity.hasParentRelations(graph)) { + if (Object.keys(entity.tags).length === 0) { + subtype = 'any'; + } else if (!hasDescriptiveTags(entity, graph)) { + subtype = 'descriptive'; + } else if (isUntypedRelation(entity)) { + subtype = 'relation_type'; + } + } // flag an unknown road even if it's a member of a relation + + + if (!subtype && isUnknownRoad(entity)) { + subtype = 'highway_classification'; + } + + if (!subtype) return []; + var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype; + var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag'; // can always delete if the user created it in the first place.. + + var canDelete = entity.version === undefined || entity.v !== undefined; + var severity = canDelete && subtype !== 'highway_classification' ? 'error' : 'warning'; + return [new validationIssue({ + type: type, + subtype: subtype, + severity: severity, + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]); + return entity ? _t.html('issues.' + messageID + '.message', { + feature: utilDisplayLabel(entity, context.graph()) + }) : ''; + }, + reference: showReference, + entityIds: [entity.id], + dynamicFixes: function dynamicFixes(context) { + var fixes = []; + var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset'; + fixes.push(new validationIssueFix({ + icon: 'iD-icon-search', + title: _t.html('issues.fix.' + selectFixType + '.title'), + onClick: function onClick(context) { + context.ui().sidebar.showPresetList(); + } + })); + var deleteOnClick; + var id = this.entityIds[0]; + var operation = operationDelete(context, [id]); + var disabledReasonID = operation.disabled(); + + if (!disabledReasonID) { + deleteOnClick = function deleteOnClick(context) { + var id = this.issue.entityIds[0]; + var operation = operationDelete(context, [id]); + + if (!operation.disabled()) { + operation(); + } + }; + } + + fixes.push(new validationIssueFix({ + icon: 'iD-operation-delete', + title: _t.html('issues.fix.delete_feature.title'), + disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined, + onClick: deleteOnClick + })); + return fixes; + } + })]; + + function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.' + referenceID + '.reference')); + } + }; + + validation.type = type; + return validation; + } + + function validationOutdatedTags() { + var type = 'outdated_tags'; + var _waitingForDeprecated = true; + + var _dataDeprecated; // fetch deprecated tags + + + _mainFileFetcher.get('deprecated').then(function (d) { + return _dataDeprecated = d; + })["catch"](function () { + /* ignore */ + })["finally"](function () { + return _waitingForDeprecated = false; + }); + + function oldTagIssues(entity, graph) { + var oldTags = Object.assign({}, entity.tags); // shallow copy + + var preset = _mainPresetIndex.match(entity, graph); + var subtype = 'deprecated_tags'; + if (!preset) return []; + if (!entity.hasInterestingTags()) return []; // Upgrade preset, if a replacement is available.. + + if (preset.replacement) { + var newPreset = _mainPresetIndex.item(preset.replacement); + graph = actionChangePreset(entity.id, preset, newPreset, true + /* skip field defaults */ + )(graph); + entity = graph.entity(entity.id); + preset = newPreset; + } // Upgrade deprecated tags.. + + + if (_dataDeprecated) { + var deprecatedTags = entity.deprecatedTags(_dataDeprecated); + + if (deprecatedTags.length) { + deprecatedTags.forEach(function (tag) { + graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph); + }); + entity = graph.entity(entity.id); + } + } // Add missing addTags from the detected preset + + + var newTags = Object.assign({}, entity.tags); // shallow copy + + if (preset.tags !== preset.addTags) { + Object.keys(preset.addTags).forEach(function (k) { + if (!newTags[k]) { + if (preset.addTags[k] === '*') { + newTags[k] = 'yes'; + } else { + newTags[k] = preset.addTags[k]; + } + } + }); + } // Attempt to match a canonical record in the name-suggestion-index. + + + var nsi = services.nsi; + var waitingForNsi = false; + var nsiResult; + + if (nsi) { + waitingForNsi = nsi.status() === 'loading'; + + if (!waitingForNsi) { + var loc = entity.extent(graph).center(); + nsiResult = nsi.upgradeTags(newTags, loc); + + if (nsiResult) { + newTags = nsiResult.newTags; + subtype = 'noncanonical_brand'; + } + } + } + + var issues = []; + issues.provisional = _waitingForDeprecated || waitingForNsi; // determine diff + + var tagDiff = utilTagDiff(oldTags, newTags); + if (!tagDiff.length) return issues; + var isOnlyAddingTags = tagDiff.every(function (d) { + return d.type === '+'; + }); + var prefix = ''; + + if (nsiResult) { + prefix = 'noncanonical_brand.'; + } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) { + subtype = 'incomplete_tags'; + prefix = 'incomplete.'; + } // don't allow autofixing brand tags + + + var autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null; + issues.push(new validationIssue({ + type: type, + subtype: subtype, + severity: 'warning', + message: showMessage, + reference: showReference, + entityIds: [entity.id], + hash: utilHashcode(JSON.stringify(tagDiff)), + dynamicFixes: function dynamicFixes() { + var fixes = [new validationIssueFix({ + autoArgs: autoArgs, + title: _t.html('issues.fix.upgrade_tags.title'), + onClick: function onClick(context) { + context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation')); + } + })]; + var item = nsiResult && nsiResult.matched; + + if (item) { + fixes.push(new validationIssueFix({ + title: _t.html('issues.fix.tag_as_not.title', { + name: item.displayName + }), + onClick: function onClick(context) { + context.perform(addNotTag, _t('issues.fix.tag_as_not.annotation')); + } + })); + } + + return fixes; + } + })); + return issues; + + function doUpgrade(graph) { + var currEntity = graph.hasEntity(entity.id); + if (!currEntity) return graph; + var newTags = Object.assign({}, currEntity.tags); // shallow copy + + tagDiff.forEach(function (diff) { + if (diff.type === '-') { + delete newTags[diff.key]; + } else if (diff.type === '+') { + newTags[diff.key] = diff.newVal; + } + }); + return actionChangeTags(currEntity.id, newTags)(graph); + } + + function addNotTag(graph) { + var currEntity = graph.hasEntity(entity.id); + if (!currEntity) return graph; + var item = nsiResult && nsiResult.matched; + if (!item) return graph; + var newTags = Object.assign({}, currEntity.tags); // shallow copy + + var wd = item.mainTag; // e.g. `brand:wikidata` + + var notwd = "not:".concat(wd); // e.g. `not:brand:wikidata` + + var qid = item.tags[wd]; + newTags[notwd] = qid; + + if (newTags[wd] === qid) { + // if `brand:wikidata` was set to that qid + var wp = item.mainTag.replace('wikidata', 'wikipedia'); + delete newTags[wd]; // remove `brand:wikidata` + + delete newTags[wp]; // remove `brand:wikipedia` + } + + return actionChangeTags(currEntity.id, newTags)(graph); + } + + function showMessage(context) { + var currEntity = context.hasEntity(entity.id); + if (!currEntity) return ''; + var messageID = "issues.outdated_tags.".concat(prefix, "message"); + + if (subtype === 'noncanonical_brand' && isOnlyAddingTags) { + messageID += '_incomplete'; + } + + return _t.html(messageID, { + feature: utilDisplayLabel(currEntity, context.graph(), true + /* verbose */ + ) + }); + } + + function showReference(selection) { + var enter = selection.selectAll('.issue-reference').data([0]).enter(); + enter.append('div').attr('class', 'issue-reference').html(_t.html("issues.outdated_tags.".concat(prefix, "reference"))); + enter.append('strong').html(_t.html('issues.suggested')); + enter.append('table').attr('class', 'tagDiff-table').selectAll('.tagDiff-row').data(tagDiff).enter().append('tr').attr('class', 'tagDiff-row').append('td').attr('class', function (d) { + var klass = d.type === '+' ? 'add' : 'remove'; + return "tagDiff-cell tagDiff-cell-".concat(klass); + }).html(function (d) { + return d.display; + }); + } + } + + function oldMultipolygonIssues(entity, graph) { + var multipolygon, outerWay; + + if (entity.type === 'relation') { + outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph); + multipolygon = entity; + } else if (entity.type === 'way') { + multipolygon = osmIsOldMultipolygonOuterMember(entity, graph); + outerWay = entity; + } else { + return []; + } + + if (!multipolygon || !outerWay) return []; + return [new validationIssue({ + type: type, + subtype: 'old_multipolygon', + severity: 'warning', + message: showMessage, + reference: showReference, + entityIds: [outerWay.id, multipolygon.id], + dynamicFixes: function dynamicFixes() { + return [new validationIssueFix({ + autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')], + title: _t.html('issues.fix.move_tags.title'), + onClick: function onClick(context) { + context.perform(doUpgrade, _t('issues.fix.move_tags.annotation')); + } + })]; + } + })]; + + function doUpgrade(graph) { + var currMultipolygon = graph.hasEntity(multipolygon.id); + var currOuterWay = graph.hasEntity(outerWay.id); + if (!currMultipolygon || !currOuterWay) return graph; + currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags); + graph = graph.replace(currMultipolygon); + return actionChangeTags(currOuterWay.id, {})(graph); + } + + function showMessage(context) { + var currMultipolygon = context.hasEntity(multipolygon.id); + if (!currMultipolygon) return ''; + return _t.html('issues.old_multipolygon.message', { + multipolygon: utilDisplayLabel(currMultipolygon, context.graph(), true + /* verbose */ + ) + }); + } + + function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.old_multipolygon.reference')); + } + } + + var validation = function checkOutdatedTags(entity, graph) { + var issues = oldMultipolygonIssues(entity, graph); + if (!issues.length) issues = oldTagIssues(entity, graph); + return issues; + }; + + validation.type = type; + return validation; + } + + function validationPrivateData() { + var type = 'private_data'; // assume that some buildings are private + + var privateBuildingValues = { + detached: true, + farm: true, + house: true, + houseboat: true, + residential: true, + semidetached_house: true, + static_caravan: true + }; // but they might be public if they have one of these other tags + + var publicKeys = { + amenity: true, + craft: true, + historic: true, + leisure: true, + office: true, + shop: true, + tourism: true + }; // these tags may contain personally identifying info + + var personalTags = { + 'contact:email': true, + 'contact:fax': true, + 'contact:phone': true, + email: true, + fax: true, + phone: true + }; + + var validation = function checkPrivateData(entity) { + var tags = entity.tags; + if (!tags.building || !privateBuildingValues[tags.building]) return []; + var keepTags = {}; + + for (var k in tags) { + if (publicKeys[k]) return []; // probably a public feature + + if (!personalTags[k]) { + keepTags[k] = tags[k]; + } + } + + var tagDiff = utilTagDiff(tags, keepTags); + if (!tagDiff.length) return []; + var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags'; + return [new validationIssue({ + type: type, + severity: 'warning', + message: showMessage, + reference: showReference, + entityIds: [entity.id], + dynamicFixes: function dynamicFixes() { + return [new validationIssueFix({ + icon: 'iD-operation-delete', + title: _t.html('issues.fix.' + fixID + '.title'), + onClick: function onClick(context) { + context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation')); + } + })]; + } + })]; + + function doUpgrade(graph) { + var currEntity = graph.hasEntity(entity.id); + if (!currEntity) return graph; + var newTags = Object.assign({}, currEntity.tags); // shallow copy + + tagDiff.forEach(function (diff) { + if (diff.type === '-') { + delete newTags[diff.key]; + } else if (diff.type === '+') { + newTags[diff.key] = diff.newVal; + } + }); + return actionChangeTags(currEntity.id, newTags)(graph); + } + + function showMessage(context) { + var currEntity = context.hasEntity(this.entityIds[0]); + if (!currEntity) return ''; + return _t.html('issues.private_data.contact.message', { + feature: utilDisplayLabel(currEntity, context.graph()) + }); + } + + function showReference(selection) { + var enter = selection.selectAll('.issue-reference').data([0]).enter(); + enter.append('div').attr('class', 'issue-reference').html(_t.html('issues.private_data.reference')); + enter.append('strong').html(_t.html('issues.suggested')); + enter.append('table').attr('class', 'tagDiff-table').selectAll('.tagDiff-row').data(tagDiff).enter().append('tr').attr('class', 'tagDiff-row').append('td').attr('class', function (d) { + var klass = d.type === '+' ? 'add' : 'remove'; + return 'tagDiff-cell tagDiff-cell-' + klass; + }).html(function (d) { + return d.display; + }); + } + }; + + validation.type = type; + return validation; + } + + function validationSuspiciousName() { + var type = 'suspicious_name'; + var keysToTestForGenericValues = ['aerialway', 'aeroway', 'amenity', 'building', 'craft', 'highway', 'leisure', 'railway', 'man_made', 'office', 'shop', 'tourism', 'waterway']; + var _waitingForNsi = false; // Attempt to match a generic record in the name-suggestion-index. + + function isGenericMatchInNsi(tags) { + var nsi = services.nsi; + + if (nsi) { + _waitingForNsi = nsi.status() === 'loading'; + + if (!_waitingForNsi) { + return nsi.isGenericName(tags); + } + } + + return false; + } // Test if the name is just the key or tag value (e.g. "park") + + + function nameMatchesRawTag(lowercaseName, tags) { + for (var i = 0; i < keysToTestForGenericValues.length; i++) { + var key = keysToTestForGenericValues[i]; + var val = tags[key]; + + if (val) { + val = val.toLowerCase(); + + if (key === lowercaseName || val === lowercaseName || key.replace(/\_/g, ' ') === lowercaseName || val.replace(/\_/g, ' ') === lowercaseName) { + return true; + } + } + } + + return false; + } + + function isGenericName(name, tags) { + name = name.toLowerCase(); + return nameMatchesRawTag(name, tags) || isGenericMatchInNsi(tags); + } + + function makeGenericNameIssue(entityId, nameKey, genericName, langCode) { + return new validationIssue({ + type: type, + subtype: 'generic_name', + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]); + if (!entity) return ''; + var preset = _mainPresetIndex.match(entity, context.graph()); + var langName = langCode && _mainLocalizer.languageName(langCode); + return _t.html('issues.generic_name.message' + (langName ? '_language' : ''), { + feature: preset.name(), + name: genericName, + language: langName + }); + }, + reference: showReference, + entityIds: [entityId], + hash: "".concat(nameKey, "=").concat(genericName), + dynamicFixes: function dynamicFixes() { + return [new validationIssueFix({ + icon: 'iD-operation-delete', + title: _t.html('issues.fix.remove_the_name.title'), + onClick: function onClick(context) { + var entityId = this.issue.entityIds[0]; + var entity = context.entity(entityId); + var tags = Object.assign({}, entity.tags); // shallow copy + + delete tags[nameKey]; + context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation')); + } + })]; + } + }); + + function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference')); + } + } + + function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) { + return new validationIssue({ + type: type, + subtype: 'not_name', + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]); + if (!entity) return ''; + var preset = _mainPresetIndex.match(entity, context.graph()); + var langName = langCode && _mainLocalizer.languageName(langCode); + return _t.html('issues.incorrect_name.message' + (langName ? '_language' : ''), { + feature: preset.name(), + name: incorrectName, + language: langName + }); + }, + reference: showReference, + entityIds: [entityId], + hash: "".concat(nameKey, "=").concat(incorrectName), + dynamicFixes: function dynamicFixes() { + return [new validationIssueFix({ + icon: 'iD-operation-delete', + title: _t.html('issues.fix.remove_the_name.title'), + onClick: function onClick(context) { + var entityId = this.issue.entityIds[0]; + var entity = context.entity(entityId); + var tags = Object.assign({}, entity.tags); // shallow copy + + delete tags[nameKey]; + context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation')); + } + })]; + } + }); + + function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference')); + } + } + + var validation = function checkGenericName(entity) { + var tags = entity.tags; // a generic name is allowed if it's a known brand or entity + + var hasWikidata = !!tags.wikidata || !!tags['brand:wikidata'] || !!tags['operator:wikidata']; + if (hasWikidata) return []; + var issues = []; + var notNames = (tags['not:name'] || '').split(';'); + + for (var key in tags) { + var m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/); + if (!m) continue; + var langCode = m.length >= 2 ? m[1] : null; + var value = tags[key]; + + if (notNames.length) { + for (var i in notNames) { + var notName = notNames[i]; + + if (notName && value === notName) { + issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode)); + continue; + } + } + } + + if (isGenericName(value, tags)) { + issues.provisional = _waitingForNsi; // retry later if we are waiting on NSI to finish loading + + issues.push(makeGenericNameIssue(entity.id, key, value, langCode)); + } + } + + return issues; + }; + + validation.type = type; + return validation; + } + + function validationUnsquareWay(context) { + var type = 'unsquare_way'; + var DEFAULT_DEG_THRESHOLD = 5; // see also issues.js + // use looser epsilon for detection to reduce warnings of buildings that are essentially square already + + var epsilon = 0.05; + var nodeThreshold = 10; + + function isBuilding(entity, graph) { + if (entity.type !== 'way' || entity.geometry(graph) !== 'area') return false; + return entity.tags.building && entity.tags.building !== 'no'; + } + + var validation = function checkUnsquareWay(entity, graph) { + if (!isBuilding(entity, graph)) return []; // don't flag ways marked as physically unsquare + + if (entity.tags.nonsquare === 'yes') return []; + var isClosed = entity.isClosed(); + if (!isClosed) return []; // this building has bigger problems + // don't flag ways with lots of nodes since they are likely detail-mapped + + var nodes = graph.childNodes(entity).slice(); // shallow copy + + if (nodes.length > nodeThreshold + 1) return []; // +1 because closing node appears twice + // ignore if not all nodes are fully downloaded + + var osm = services.osm; + if (!osm || nodes.some(function (node) { + return !osm.isDataLoaded(node.loc); + })) return []; // don't flag connected ways to avoid unresolvable unsquare loops + + var hasConnectedSquarableWays = nodes.some(function (node) { + return graph.parentWays(node).some(function (way) { + if (way.id === entity.id) return false; + if (isBuilding(way, graph)) return true; + return graph.parentRelations(way).some(function (parentRelation) { + return parentRelation.isMultipolygon() && parentRelation.tags.building && parentRelation.tags.building !== 'no'; + }); + }); + }); + if (hasConnectedSquarableWays) return []; // user-configurable square threshold + + var storedDegreeThreshold = corePreferences('validate-square-degrees'); + var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold); + var points = nodes.map(function (node) { + return context.projection(node.loc); + }); + if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) return []; + var autoArgs; // don't allow autosquaring features linked to wikidata + + if (!entity.tags.wikidata) { + // use same degree threshold as for detection + var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold); + autoAction.transitionable = false; // when autofixing, do it instantly + + autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature', { + n: 1 + })]; + } + + return [new validationIssue({ + type: type, + subtype: 'building', + severity: 'warning', + message: function message(context) { + var entity = context.hasEntity(this.entityIds[0]); + return entity ? _t.html('issues.unsquare_way.message', { + feature: utilDisplayLabel(entity, context.graph()) + }) : ''; + }, + reference: showReference, + entityIds: [entity.id], + hash: degreeThreshold, + dynamicFixes: function dynamicFixes() { + return [new validationIssueFix({ + icon: 'iD-operation-orthogonalize', + title: _t.html('issues.fix.square_feature.title'), + autoArgs: autoArgs, + onClick: function onClick(context, completionHandler) { + var entityId = this.issue.entityIds[0]; // use same degree threshold as for detection + + context.perform(actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold), _t('operations.orthogonalize.annotation.feature', { + n: 1 + })); // run after the squaring transition (currently 150ms) + + window.setTimeout(function () { + completionHandler(); + }, 175); + } + }) + /* + new validationIssueFix({ + title: t.html('issues.fix.tag_as_unsquare.title'), + onClick: function(context) { + var entityId = this.issue.entityIds[0]; + var entity = context.entity(entityId); + var tags = Object.assign({}, entity.tags); // shallow copy + tags.nonsquare = 'yes'; + context.perform( + actionChangeTags(entityId, tags), + t('issues.fix.tag_as_unsquare.annotation') + ); + } + }) + */ + ]; + } + })]; + + function showReference(selection) { + selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unsquare_way.buildings.reference')); + } + }; + + validation.type = type; + return validation; + } + + var Validations = /*#__PURE__*/Object.freeze({ + __proto__: null, + validationAlmostJunction: validationAlmostJunction, + validationCloseNodes: validationCloseNodes, + validationCrossingWays: validationCrossingWays, + validationDisconnectedWay: validationDisconnectedWay, + validationFormatting: validationFormatting, + validationHelpRequest: validationHelpRequest, + validationImpossibleOneway: validationImpossibleOneway, + validationIncompatibleSource: validationIncompatibleSource, + validationMaprules: validationMaprules, + validationMismatchedGeometry: validationMismatchedGeometry, + validationMissingRole: validationMissingRole, + validationMissingTag: validationMissingTag, + validationOutdatedTags: validationOutdatedTags, + validationPrivateData: validationPrivateData, + validationSuspiciousName: validationSuspiciousName, + validationUnsquareWay: validationUnsquareWay + }); + + function coreValidator(context) { + var _this = this; + + var dispatch = dispatch$8('validated', 'focusedIssue'); + var validator = utilRebind({}, dispatch, 'on'); + var _rules = {}; + var _disabledRules = {}; + + var _ignoredIssueIDs = new Set(); + + var _resolvedIssueIDs = new Set(); + + var _baseCache = validationCache('base'); // issues before any user edits + + + var _headCache = validationCache('head'); // issues after all user edits + + + var _completeDiff = {}; // complete diff base -> head of what the user changed + + var _headIsCurrent = false; + + var _deferredRIC = new Set(); // Set( RequestIdleCallback handles ) + + + var _deferredST = new Set(); // Set( SetTimeout handles ) + + + var _headPromise; // Promise fulfilled when validation is performed up to headGraph snapshot + + + var RETRY = 5000; // wait 5sec before revalidating provisional entities + // Allow validation severity to be overridden by url queryparams... + // See: https://github.com/openstreetmap/iD/pull/8243 + // + // Each param should contain a urlencoded comma separated list of + // `type/subtype` rules. `*` may be used as a wildcard.. + // Examples: + // `validationError=disconnected_way/*` + // `validationError=disconnected_way/highway` + // `validationError=crossing_ways/bridge*` + // `validationError=crossing_ways/bridge*,crossing_ways/tunnel*` + + var _errorOverrides = parseHashParam(context.initialHashParams.validationError); + + var _warningOverrides = parseHashParam(context.initialHashParams.validationWarning); + + var _disableOverrides = parseHashParam(context.initialHashParams.validationDisable); // `parseHashParam()` (private) + // Checks hash parameters for severity overrides + // Arguments + // `param` - a url hash parameter (`validationError`, `validationWarning`, or `validationDisable`) + // Returns + // Array of Objects like { type: RegExp, subtype: RegExp } + // + + + function parseHashParam(param) { + var result = []; + var rules = (param || '').split(','); + rules.forEach(function (rule) { + rule = rule.trim(); + var parts = rule.split('/', 2); // "type/subtype" + + var type = parts[0]; + var subtype = parts[1] || '*'; + if (!type || !subtype) return; + result.push({ + type: makeRegExp(type), + subtype: makeRegExp(subtype) + }); + }); + return result; + + function makeRegExp(str) { + var escaped = str.replace(/[-\/\\^$+?.()|[\]{}]/g, '\\$&') // escape all reserved chars except for the '*' + .replace(/\*/g, '.*'); // treat a '*' like '.*' + + return new RegExp('^' + escaped + '$'); + } + } // `init()` + // Initialize the validator, called once on iD startup + // + + + validator.init = function () { + Object.values(Validations).forEach(function (validation) { + if (typeof validation !== 'function') return; + var fn = validation(context); + var key = fn.type; + _rules[key] = fn; + }); + var disabledRules = corePreferences('validate-disabledRules'); + + if (disabledRules) { + disabledRules.split(',').forEach(function (k) { + return _disabledRules[k] = true; + }); + } + }; // `reset()` (private) + // Cancels deferred work and resets all caches + // + // Arguments + // `resetIgnored` - `true` to clear the list of user-ignored issues + // + + + function reset(resetIgnored) { + // cancel deferred work + _deferredRIC.forEach(window.cancelIdleCallback); + + _deferredRIC.clear(); + + _deferredST.forEach(window.clearTimeout); + + _deferredST.clear(); // empty queues and resolve any pending promise + + + _baseCache.queue = []; + _headCache.queue = []; + processQueue(_headCache); + processQueue(_baseCache); // clear caches + + if (resetIgnored) _ignoredIssueIDs.clear(); + + _resolvedIssueIDs.clear(); + + _baseCache = validationCache('base'); + _headCache = validationCache('head'); + _completeDiff = {}; + _headIsCurrent = false; + } // `reset()` + // clear caches, called whenever iD resets after a save or switches sources + // (clears out the _ignoredIssueIDs set also) + // + + + validator.reset = function () { + reset(true); + }; // `resetIgnoredIssues()` + // clears out the _ignoredIssueIDs Set + // + + + validator.resetIgnoredIssues = function () { + _ignoredIssueIDs.clear(); + + dispatch.call('validated'); // redraw UI + }; // `revalidateUnsquare()` + // Called whenever the user changes the unsquare threshold + // It reruns just the "unsquare_way" validation on all buildings. + // + + + validator.revalidateUnsquare = function () { + revalidateUnsquare(_headCache); + revalidateUnsquare(_baseCache); + dispatch.call('validated'); + }; + + function revalidateUnsquare(cache) { + var checkUnsquareWay = _rules.unsquare_way; + if (!cache.graph || typeof checkUnsquareWay !== 'function') return; // uncache existing + + cache.uncacheIssuesOfType('unsquare_way'); + var buildings = context.history().tree().intersects(geoExtent([-180, -90], [180, 90]), cache.graph) // everywhere + .filter(function (entity) { + return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no'; + }); // rerun for all buildings + + buildings.forEach(function (entity) { + var detected = checkUnsquareWay(entity, cache.graph); + if (!detected.length) return; + cache.cacheIssues(detected); + }); + } // `getIssues()` + // Gets all issues that match the given options + // This is called by many other places + // + // Arguments + // `options` Object like: + // { + // what: 'all', // 'all' or 'edited' + // where: 'all', // 'all' or 'visible' + // includeIgnored: false, // true, false, or 'only' + // includeDisabledRules: false // true, false, or 'only' + // } + // + // Returns + // An Array containing the issues + // + + + validator.getIssues = function (options) { + var opts = Object.assign({ + what: 'all', + where: 'all', + includeIgnored: false, + includeDisabledRules: false + }, options); + var view = context.map().extent(); + var seen = new Set(); + var results = []; // collect head issues - present in the user edits + + if (_headCache.graph && _headCache.graph !== _baseCache.graph) { + Object.values(_headCache.issuesByIssueID).forEach(function (issue) { + // In the head cache, only count features that the user is responsible for - #8632 + // For example, a user can undo some work and an issue will still present in the + // head graph, but we don't want to credit the user for causing that issue. + var userModified = (issue.entityIds || []).some(function (id) { + return _completeDiff.hasOwnProperty(id); + }); + if (opts.what === 'edited' && !userModified) return; // present in head but user didn't touch it + + if (!filter(issue)) return; + seen.add(issue.id); + results.push(issue); + }); + } // collect base issues - present before user edits + + + if (opts.what === 'all') { + Object.values(_baseCache.issuesByIssueID).forEach(function (issue) { + if (!filter(issue)) return; + seen.add(issue.id); + results.push(issue); + }); + } + + return results; // Filter the issue set to include only what the calling code wants to see. + // Note that we use `context.graph()`/`context.hasEntity()` here, not `cache.graph`, + // because that is the graph that the calling code will be using. + + function filter(issue) { + if (!issue) return false; + if (seen.has(issue.id)) return false; + if (_resolvedIssueIDs.has(issue.id)) return false; + if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false; + if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false; + if (opts.includeIgnored === 'only' && !_ignoredIssueIDs.has(issue.id)) return false; + if (!opts.includeIgnored && _ignoredIssueIDs.has(issue.id)) return false; // This issue may involve an entity that doesn't exist in context.graph() + // This can happen because validation is async and rendering the issue lists is async. + + if ((issue.entityIds || []).some(function (id) { + return !context.hasEntity(id); + })) return false; + + if (opts.where === 'visible') { + var extent = issue.extent(context.graph()); + if (!view.intersects(extent)) return false; + } + + return true; + } + }; // `getResolvedIssues()` + // Gets the issues that have been fixed by the user. + // + // Resolved issues are tracked in the `_resolvedIssueIDs` Set, + // and they should all be issues that exist in the _baseCache. + // + // Returns + // An Array containing the issues + // + + + validator.getResolvedIssues = function () { + return Array.from(_resolvedIssueIDs).map(function (issueID) { + return _baseCache.issuesByIssueID[issueID]; + }).filter(Boolean); + }; // `focusIssue()` + // Adjusts the map to focus on the given issue. + // (requires the issue to have a reasonable extent defined) + // + // Arguments + // `issue` - the issue to focus on + // + + + validator.focusIssue = function (issue) { + // Note that we use `context.graph()`/`context.hasEntity()` here, not `cache.graph`, + // because that is the graph that the calling code will be using. + var graph = context.graph(); + var selectID; + var focusCenter; // Try to focus the map at the center of the issue.. + + var issueExtent = issue.extent(graph); + + if (issueExtent) { + focusCenter = issueExtent.center(); + } // Try to select the first entity in the issue.. + + + if (issue.entityIds && issue.entityIds.length) { + selectID = issue.entityIds[0]; // If a relation, focus on one of its members instead. + // Otherwise we might be focusing on a part of map where the relation is not visible. + + if (selectID && selectID.charAt(0) === 'r') { + // relation + var ids = utilEntityAndDeepMemberIDs([selectID], graph); + var nodeID = ids.find(function (id) { + return id.charAt(0) === 'n' && graph.hasEntity(id); + }); + + if (!nodeID) { + // relation has no downloaded nodes to focus on + var wayID = ids.find(function (id) { + return id.charAt(0) === 'w' && graph.hasEntity(id); + }); + + if (wayID) { + nodeID = graph.entity(wayID).first(); // focus on the first node of this way + } + } + + if (nodeID) { + focusCenter = graph.entity(nodeID).loc; + } + } + } + + if (focusCenter) { + // Adjust the view + var setZoom = Math.max(context.map().zoom(), 19); + context.map().unobscuredCenterZoomEase(focusCenter, setZoom); + } + + if (selectID) { + // Enter select mode + window.setTimeout(function () { + context.enter(modeSelect(context, [selectID])); + dispatch.call('focusedIssue', _this, issue); + }, 250); // after ease + } + }; // `getIssuesBySeverity()` + // Gets the issues then groups them by error/warning + // (This just calls getIssues, then puts issues in groups) + // + // Arguments + // `options` - (see `getIssues`) + // Returns + // Object result like: + // { + // error: Array of errors, + // warning: Array of warnings + // } + // + + + validator.getIssuesBySeverity = function (options) { + var groups = utilArrayGroupBy(validator.getIssues(options), 'severity'); + groups.error = groups.error || []; + groups.warning = groups.warning || []; + return groups; + }; // `getEntityIssues()` + // Gets the issues that the given entity IDs have in common, matching the given options + // (This just calls getIssues, then filters for the given entity IDs) + // The issues are sorted for relevance + // + // Arguments + // `entityIDs` - Array or Set of entityIDs to get issues for + // `options` - (see `getIssues`) + // Returns + // An Array containing the issues + // + + + validator.getSharedEntityIssues = function (entityIDs, options) { + var orderedIssueTypes = [// Show some issue types in a particular order: + 'missing_tag', 'missing_role', // - missing data first + 'outdated_tags', 'mismatched_geometry', // - identity issues + 'crossing_ways', 'almost_junction', // - geometry issues where fixing them might solve connectivity issues + 'disconnected_way', 'impossible_oneway' // - finally connectivity issues + ]; + var allIssues = validator.getIssues(options); + var forEntityIDs = new Set(entityIDs); + return allIssues.filter(function (issue) { + return (issue.entityIds || []).some(function (entityID) { + return forEntityIDs.has(entityID); + }); + }).sort(function (issue1, issue2) { + if (issue1.type === issue2.type) { + // issues of the same type, sort deterministically + return issue1.id < issue2.id ? -1 : 1; + } + + var index1 = orderedIssueTypes.indexOf(issue1.type); + var index2 = orderedIssueTypes.indexOf(issue2.type); + + if (index1 !== -1 && index2 !== -1) { + // both issue types have explicit sort orders + return index1 - index2; + } else if (index1 === -1 && index2 === -1) { + // neither issue type has an explicit sort order, sort by type + return issue1.type < issue2.type ? -1 : 1; + } else { + // order explicit types before everything else + return index1 !== -1 ? -1 : 1; + } + }); + }; // `getEntityIssues()` + // Get an array of detected issues for the given entityID. + // (This just calls getSharedEntityIssues for a single entity) + // + // Arguments + // `entityID` - the entity ID to get the issues for + // `options` - (see `getIssues`) + // Returns + // An Array containing the issues + // + + + validator.getEntityIssues = function (entityID, options) { + return validator.getSharedEntityIssues([entityID], options); + }; // `getRuleKeys()` + // + // Returns + // An Array containing the rule keys + // + + + validator.getRuleKeys = function () { + return Object.keys(_rules); + }; // `isRuleEnabled()` + // + // Arguments + // `key` - the rule to check (e.g. 'crossing_ways') + // Returns + // `true`/`false` + // + + + validator.isRuleEnabled = function (key) { + return !_disabledRules[key]; + }; // `toggleRule()` + // Toggles a single validation rule, + // then reruns the validation so that the user sees something happen in the UI + // + // Arguments + // `key` - the rule to toggle (e.g. 'crossing_ways') + // + + + validator.toggleRule = function (key) { + if (_disabledRules[key]) { + delete _disabledRules[key]; + } else { + _disabledRules[key] = true; + } + + corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(',')); + validator.validate(); + }; // `disableRules()` + // Disables given validation rules, + // then reruns the validation so that the user sees something happen in the UI + // + // Arguments + // `keys` - Array or Set containing rule keys to disable + // + + + validator.disableRules = function (keys) { + _disabledRules = {}; + keys.forEach(function (k) { + return _disabledRules[k] = true; + }); + corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(',')); + validator.validate(); + }; // `ignoreIssue()` + // Don't show the given issue in lists + // + // Arguments + // `issueID` - the issueID + // + + + validator.ignoreIssue = function (issueID) { + _ignoredIssueIDs.add(issueID); + }; // `validate()` + // Validates anything that has changed in the head graph since the last time it was run. + // (head graph contains user's edits) + // + // Returns + // A Promise fulfilled when the validation has completed and then dispatches a `validated` event. + // This may take time but happen in the background during browser idle time. + // + + + validator.validate = function () { + // Make sure the caches have graphs assigned to them. + // (we don't do this in `reset` because context is still resetting things and `history.base()` is unstable then) + var baseGraph = context.history().base(); + if (!_headCache.graph) _headCache.graph = baseGraph; + if (!_baseCache.graph) _baseCache.graph = baseGraph; + var prevGraph = _headCache.graph; + var currGraph = context.graph(); + + if (currGraph === prevGraph) { + // _headCache.graph is current - we are caught up + _headIsCurrent = true; + dispatch.call('validated'); + return Promise.resolve(); + } + + if (_headPromise) { + // Validation already in process, but we aren't caught up to current + _headIsCurrent = false; // We will need to catch up after the validation promise fulfills + + return _headPromise; + } // If we get here, its time to start validating stuff. + + + _headCache.graph = currGraph; // take snapshot + + _completeDiff = context.history().difference().complete(); + var incrementalDiff = coreDifference(prevGraph, currGraph); + var entityIDs = Object.keys(incrementalDiff.complete()); + entityIDs = _headCache.withAllRelatedEntities(entityIDs); // expand set + + if (!entityIDs.size) { + dispatch.call('validated'); + return Promise.resolve(); + } + + _headPromise = validateEntitiesAsync(entityIDs, _headCache).then(function () { + return updateResolvedIssues(entityIDs); + }).then(function () { + return dispatch.call('validated'); + })["catch"](function () { + /* ignore */ + }).then(function () { + _headPromise = null; + + if (!_headIsCurrent) { + validator.validate(); // run it again to catch up to current graph + } + }); + return _headPromise; + }; // register event handlers: + // WHEN TO RUN VALIDATION: + // When history changes: + + + context.history().on('restore.validator', validator.validate) // on restore saved history + .on('undone.validator', validator.validate) // on undo + .on('redone.validator', validator.validate) // on redo + .on('reset.validator', function () { + // on history reset - happens after save, or enter/exit walkthrough + reset(false); // cached issues aren't valid any longer if the history has been reset + + validator.validate(); + }); // but not on 'change' (e.g. while drawing) + // When user changes editing modes (to catch recent changes e.g. drawing) + + context.on('exit.validator', validator.validate); // When merging fetched data, validate base graph: + + context.history().on('merge.validator', function (entities) { + if (!entities) return; // Make sure the caches have graphs assigned to them. + // (we don't do this in `reset` because context is still resetting things and `history.base()` is unstable then) + + var baseGraph = context.history().base(); + if (!_headCache.graph) _headCache.graph = baseGraph; + if (!_baseCache.graph) _baseCache.graph = baseGraph; + var entityIDs = entities.map(function (entity) { + return entity.id; + }); + entityIDs = _baseCache.withAllRelatedEntities(entityIDs); // expand set + + validateEntitiesAsync(entityIDs, _baseCache); + }); // `validateEntity()` (private) + // Runs all validation rules on a single entity. + // Some things to note: + // - Graph is passed in from whenever the validation was started. Validators shouldn't use + // `context.graph()` because this all happens async, and the graph might have changed + // (for example, nodes getting deleted before the validation can run) + // - Validator functions may still be waiting on something and return a "provisional" result. + // In this situation, we will schedule to revalidate the entity sometime later. + // + // Arguments + // `entity` - The entity + // `graph` - graph containing the entity + // + // Returns + // Object result like: + // { + // issues: Array of detected issues + // provisional: `true` if provisional result, `false` if final result + // } + // + + function validateEntity(entity, graph) { + var result = { + issues: [], + provisional: false + }; + Object.keys(_rules).forEach(runValidation); // run all rules + + return result; // runs validation and appends resulting issues + + function runValidation(key) { + var fn = _rules[key]; + + if (typeof fn !== 'function') { + console.error('no such validation rule = ' + key); // eslint-disable-line no-console + + return; + } + + var detected = fn(entity, graph); + + if (detected.provisional) { + // this validation should be run again later + result.provisional = true; + } + + detected = detected.filter(applySeverityOverrides); + result.issues = result.issues.concat(detected); // If there are any override rules that match the issue type/subtype, + // adjust severity (or disable it) and keep/discard as quickly as possible. + + function applySeverityOverrides(issue) { + var type = issue.type; + var subtype = issue.subtype || ''; + var i; + + for (i = 0; i < _errorOverrides.length; i++) { + if (_errorOverrides[i].type.test(type) && _errorOverrides[i].subtype.test(subtype)) { + issue.severity = 'error'; + return true; + } + } + + for (i = 0; i < _warningOverrides.length; i++) { + if (_warningOverrides[i].type.test(type) && _warningOverrides[i].subtype.test(subtype)) { + issue.severity = 'warning'; + return true; + } + } + + for (i = 0; i < _disableOverrides.length; i++) { + if (_disableOverrides[i].type.test(type) && _disableOverrides[i].subtype.test(subtype)) { + return false; + } + } + + return true; + } + } + } // `updateResolvedIssues()` (private) + // Determine if any issues were resolved for the given entities. + // This is called by `validate()` after validation of the head graph + // + // Give the user credit for fixing an issue if: + // - the issue is in the base cache + // - the issue is not in the head cache + // - the user did something to one of the entities involved in the issue + // + // Arguments + // `entityIDs` - Array or Set containing entity IDs. + // + + + function updateResolvedIssues(entityIDs) { + entityIDs.forEach(function (entityID) { + var baseIssues = _baseCache.issuesByEntityID[entityID]; + if (!baseIssues) return; + baseIssues.forEach(function (issueID) { + // Check if the user did something to one of the entities involved in this issue. + // (This issue could involve multiple entities, e.g. disconnected routable features) + var issue = _baseCache.issuesByIssueID[issueID]; + var userModified = (issue.entityIds || []).some(function (id) { + return _completeDiff.hasOwnProperty(id); + }); + + if (userModified && !_headCache.issuesByIssueID[issueID]) { + // issue seems fixed + _resolvedIssueIDs.add(issueID); + } else { + // issue still not resolved + _resolvedIssueIDs["delete"](issueID); // (did undo, or possibly fixed and then re-caused the issue) + + } + }); + }); + } // `validateEntitiesAsync()` (private) + // Schedule validation for many entities. + // + // Arguments + // `entityIDs` - Array or Set containing entityIDs. + // `graph` - the graph to validate that contains those entities + // `cache` - the cache to store results in (_headCache or _baseCache) + // + // Returns + // A Promise fulfilled when the validation has completed. + // This may take time but happen in the background during browser idle time. + // + + + function validateEntitiesAsync(entityIDs, cache) { + // Enqueue the work + var jobs = Array.from(entityIDs).map(function (entityID) { + if (cache.queuedEntityIDs.has(entityID)) return null; // queued already + + cache.queuedEntityIDs.add(entityID); // Clear caches for existing issues related to this entity + + cache.uncacheEntityID(entityID); + return function () { + cache.queuedEntityIDs["delete"](entityID); + var graph = cache.graph; + if (!graph) return; // was reset? + + var entity = graph.hasEntity(entityID); // Sanity check: don't validate deleted entities + + if (!entity) return; // detect new issues and update caches + + var result = validateEntity(entity, graph); + + if (result.provisional) { + // provisional result + cache.provisionalEntityIDs.add(entityID); // we'll need to revalidate this entity again later + } + + cache.cacheIssues(result.issues); // update cache + }; + }).filter(Boolean); // Perform the work in chunks. + // Because this will happen during idle callbacks, we want to choose a chunk size + // that won't make the browser stutter too badly. + + cache.queue = cache.queue.concat(utilArrayChunk(jobs, 100)); // Perform the work + + if (cache.queuePromise) return cache.queuePromise; + cache.queuePromise = processQueue(cache).then(function () { + return revalidateProvisionalEntities(cache); + })["catch"](function () { + /* ignore */ + })["finally"](function () { + return cache.queuePromise = null; + }); + return cache.queuePromise; + } // `revalidateProvisionalEntities()` (private) + // Sometimes a validator will return a "provisional" result. + // In this situation, we'll need to revalidate the entity later. + // This function waits a delay, then places them back into the validation queue. + // + // Arguments + // `cache` - The cache (_headCache or _baseCache) + // + + + function revalidateProvisionalEntities(cache) { + if (!cache.provisionalEntityIDs.size) return; // nothing to do + + var handle = window.setTimeout(function () { + _deferredST["delete"](handle); + + if (!cache.provisionalEntityIDs.size) return; // nothing to do + + validateEntitiesAsync(Array.from(cache.provisionalEntityIDs), cache); + }, RETRY); + + _deferredST.add(handle); + } // `processQueue(queue)` (private) + // Process the next chunk of deferred validation work + // + // Arguments + // `cache` - The cache (_headCache or _baseCache) + // + // Returns + // A Promise fulfilled when the validation has completed. + // This may take time but happen in the background during browser idle time. + // + + + function processQueue(cache) { + // console.log(`${cache.which} queue length ${cache.queue.length}`); + if (!cache.queue.length) return Promise.resolve(); // we're done + + var chunk = cache.queue.pop(); + return new Promise(function (resolvePromise) { + var handle = window.requestIdleCallback(function () { + _deferredRIC["delete"](handle); // const t0 = performance.now(); + + + chunk.forEach(function (job) { + return job(); + }); // const t1 = performance.now(); + // console.log('chunk processed in ' + (t1 - t0) + ' ms'); + + resolvePromise(); + }); + + _deferredRIC.add(handle); + }).then(function () { + // dispatch an event sometimes to redraw various UI things + if (cache.queue.length % 25 === 0) dispatch.call('validated'); + }).then(function () { + return processQueue(cache); + }); + } + + return validator; + } // `validationCache()` (private) + // Creates a cache to store validation state + // We create 2 of these: + // `_baseCache` for validation on the base graph (unedited) + // `_headCache` for validation on the head graph (user edits applied) + // + // Arguments + // `which` - just a String 'base' or 'head' to keep track of it + // + + function validationCache(which) { + var cache = { + which: which, + graph: null, + queue: [], + queuePromise: null, + queuedEntityIDs: new Set(), + provisionalEntityIDs: new Set(), + issuesByIssueID: {}, + // issue.id -> issue + issuesByEntityID: {} // entity.id -> Set(issue.id) + + }; + + cache.cacheIssue = function (issue) { + (issue.entityIds || []).forEach(function (entityID) { + if (!cache.issuesByEntityID[entityID]) { + cache.issuesByEntityID[entityID] = new Set(); + } + + cache.issuesByEntityID[entityID].add(issue.id); + }); + cache.issuesByIssueID[issue.id] = issue; + }; + + cache.uncacheIssue = function (issue) { + (issue.entityIds || []).forEach(function (entityID) { + if (cache.issuesByEntityID[entityID]) { + cache.issuesByEntityID[entityID]["delete"](issue.id); + } + }); + delete cache.issuesByIssueID[issue.id]; + }; + + cache.cacheIssues = function (issues) { + issues.forEach(cache.cacheIssue); + }; + + cache.uncacheIssues = function (issues) { + issues.forEach(cache.uncacheIssue); + }; + + cache.uncacheIssuesOfType = function (type) { + var issuesOfType = Object.values(cache.issuesByIssueID).filter(function (issue) { + return issue.type === type; + }); + cache.uncacheIssues(issuesOfType); + }; // Remove a single entity and all its related issues from the caches + + + cache.uncacheEntityID = function (entityID) { + var entityIssueIDs = cache.issuesByEntityID[entityID]; + + if (entityIssueIDs) { + entityIssueIDs.forEach(function (issueID) { + var issue = cache.issuesByIssueID[issueID]; + + if (issue) { + cache.uncacheIssue(issue); + } else { + // shouldn't happen, clean up + delete cache.issuesByIssueID[issueID]; + } + }); + } + + delete cache.issuesByEntityID[entityID]; + cache.provisionalEntityIDs["delete"](entityID); + }; // Return the expandeded set of entityIDs related to issues for the given entityIDs + // + // Arguments + // `entityIDs` - Array or Set containing entityIDs. + // + + + cache.withAllRelatedEntities = function (entityIDs) { + var result = new Set(); + (entityIDs || []).forEach(function (entityID) { + result.add(entityID); // include self + + var entityIssueIDs = cache.issuesByEntityID[entityID]; + + if (entityIssueIDs) { + entityIssueIDs.forEach(function (issueID) { + var issue = cache.issuesByIssueID[issueID]; + + if (issue) { + (issue.entityIds || []).forEach(function (relatedID) { + return result.add(relatedID); + }); + } else { + // shouldn't happen, clean up + delete cache.issuesByIssueID[issueID]; + } + }); + } + }); + return result; + }; + + return cache; + } + + function coreUploader(context) { + var dispatch = dispatch$8( // Start and end events are dispatched exactly once each per legitimate outside call to `save` + 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate + 'saveEnded', // dispatched after the result event has been dispatched + 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will + 'progressChanged', // Each save results in one of these outcomes: + 'resultNoChanges', // upload wasn't attempted since there were no edits + 'resultErrors', // upload failed due to errors + 'resultConflicts', // upload failed due to data conflicts + 'resultSuccess' // upload completed without errors + ); + var _isSaving = false; + var _conflicts = []; + var _errors = []; + + var _origChanges; + + var _discardTags = {}; + _mainFileFetcher.get('discarded').then(function (d) { + _discardTags = d; + })["catch"](function () { + /* ignore */ + }); + var uploader = utilRebind({}, dispatch, 'on'); + + uploader.isSaving = function () { + return _isSaving; + }; + + uploader.save = function (changeset, tryAgain, checkConflicts) { + // Guard against accidentally entering save code twice - #4641 + if (_isSaving && !tryAgain) { + return; + } + + var osm = context.connection(); + if (!osm) return; // If user somehow got logged out mid-save, try to reauthenticate.. + // This can happen if they were logged in from before, but the tokens are no longer valid. + + if (!osm.authenticated()) { + osm.authenticate(function (err) { + if (!err) { + uploader.save(changeset, tryAgain, checkConflicts); // continue where we left off.. + } + }); + return; + } + + if (!_isSaving) { + _isSaving = true; + dispatch.call('saveStarted', this); + } + + var history = context.history(); + _conflicts = []; + _errors = []; // Store original changes, in case user wants to download them as an .osc file + + _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags)); // First time, `history.perform` a no-op action. + // Any conflict resolutions will be done as `history.replace` + // Remember to pop this later if needed + + if (!tryAgain) { + history.perform(actionNoop()); + } // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true` + + + if (!checkConflicts) { + upload(changeset); // Do the full (slow) conflict check.. + } else { + performFullConflictCheck(changeset); + } + }; + + function performFullConflictCheck(changeset) { + var osm = context.connection(); + if (!osm) return; + var history = context.history(); + var localGraph = context.graph(); + var remoteGraph = coreGraph(history.base(), true); + var summary = history.difference().summary(); + var _toCheck = []; + + for (var i = 0; i < summary.length; i++) { + var item = summary[i]; + + if (item.changeType === 'modified') { + _toCheck.push(item.entity.id); + } + } + + var _toLoad = withChildNodes(_toCheck, localGraph); + + var _loaded = {}; + var _toLoadCount = 0; + var _toLoadTotal = _toLoad.length; + + if (_toCheck.length) { + dispatch.call('progressChanged', this, _toLoadCount, _toLoadTotal); + + _toLoad.forEach(function (id) { + _loaded[id] = false; + }); + + osm.loadMultiple(_toLoad, loaded); + } else { + upload(changeset); + } + + return; + + function withChildNodes(ids, graph) { + var s = new Set(ids); + ids.forEach(function (id) { + var entity = graph.entity(id); + if (entity.type !== 'way') return; + graph.childNodes(entity).forEach(function (child) { + if (child.version !== undefined) { + s.add(child.id); + } + }); + }); + return Array.from(s); + } // Reload modified entities into an alternate graph and check for conflicts.. + + + function loaded(err, result) { + if (_errors.length) return; + + if (err) { + _errors.push({ + msg: err.message || err.responseText, + details: [_t('save.status_code', { + code: err.status + })] + }); + + didResultInErrors(); + } else { + var loadMore = []; + result.data.forEach(function (entity) { + remoteGraph.replace(entity); + _loaded[entity.id] = true; + _toLoad = _toLoad.filter(function (val) { + return val !== entity.id; + }); + if (!entity.visible) return; // Because loadMultiple doesn't download /full like loadEntity, + // need to also load children that aren't already being checked.. + + var i, id; + + if (entity.type === 'way') { + for (i = 0; i < entity.nodes.length; i++) { + id = entity.nodes[i]; + + if (_loaded[id] === undefined) { + _loaded[id] = false; + loadMore.push(id); + } + } + } else if (entity.type === 'relation' && entity.isMultipolygon()) { + for (i = 0; i < entity.members.length; i++) { + id = entity.members[i].id; + + if (_loaded[id] === undefined) { + _loaded[id] = false; + loadMore.push(id); + } + } + } + }); + _toLoadCount += result.data.length; + _toLoadTotal += loadMore.length; + dispatch.call('progressChanged', this, _toLoadCount, _toLoadTotal); + + if (loadMore.length) { + _toLoad.push.apply(_toLoad, loadMore); + + osm.loadMultiple(loadMore, loaded); + } + + if (!_toLoad.length) { + detectConflicts(); + upload(changeset); + } + } + } + + function detectConflicts() { + function choice(id, text, _action) { + return { + id: id, + text: text, + action: function action() { + history.replace(_action); + } + }; + } + + function formatUser(d) { + return '
    ' + d + ''; + } + + function entityName(entity) { + return utilDisplayName(entity) || utilDisplayType(entity.id) + ' ' + entity.id; + } + + function sameVersions(local, remote) { + if (local.version !== remote.version) return false; + + if (local.type === 'way') { + var children = utilArrayUnion(local.nodes, remote.nodes); + + for (var i = 0; i < children.length; i++) { + var a = localGraph.hasEntity(children[i]); + var b = remoteGraph.hasEntity(children[i]); + if (a && b && a.version !== b.version) return false; + } + } + + return true; + } + + _toCheck.forEach(function (id) { + var local = localGraph.entity(id); + var remote = remoteGraph.entity(id); + if (sameVersions(local, remote)) return; + var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser); + history.replace(merge); + var mergeConflicts = merge.conflicts(); + if (!mergeConflicts.length) return; // merged safely + + var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local'); + var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote'); + var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore')); + var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete')); + + _conflicts.push({ + id: id, + name: entityName(local), + details: mergeConflicts, + chosen: 1, + choices: [choice(id, keepMine, forceLocal), choice(id, keepTheirs, forceRemote)] + }); + }); + } + } + + function upload(changeset) { + var osm = context.connection(); + + if (!osm) { + _errors.push({ + msg: 'No OSM Service' + }); + } + + if (_conflicts.length) { + didResultInConflicts(changeset); + } else if (_errors.length) { + didResultInErrors(); + } else { + var history = context.history(); + var changes = history.changes(actionDiscardTags(history.difference(), _discardTags)); + + if (changes.modified.length || changes.created.length || changes.deleted.length) { + dispatch.call('willAttemptUpload', this); + osm.putChangeset(changeset, changes, uploadCallback); + } else { + // changes were insignificant or reverted by user + didResultInNoChanges(); + } + } + } + + function uploadCallback(err, changeset) { + if (err) { + if (err.status === 409) { + // 409 Conflict + uploader.save(changeset, true, true); // tryAgain = true, checkConflicts = true + } else { + _errors.push({ + msg: err.message || err.responseText, + details: [_t('save.status_code', { + code: err.status + })] + }); + + didResultInErrors(); + } + } else { + didResultInSuccess(changeset); + } + } + + function didResultInNoChanges() { + dispatch.call('resultNoChanges', this); + endSave(); + context.flush(); // reset iD + } + + function didResultInErrors() { + context.history().pop(); + dispatch.call('resultErrors', this, _errors); + endSave(); + } + + function didResultInConflicts(changeset) { + _conflicts.sort(function (a, b) { + return b.id.localeCompare(a.id); + }); + + dispatch.call('resultConflicts', this, changeset, _conflicts, _origChanges); + endSave(); + } + + function didResultInSuccess(changeset) { + // delete the edit stack cached to local storage + context.history().clearSaved(); + dispatch.call('resultSuccess', this, changeset); // Add delay to allow for postgres replication #1646 #2678 + + window.setTimeout(function () { + endSave(); + context.flush(); // reset iD + }, 2500); + } + + function endSave() { + _isSaving = false; + dispatch.call('saveEnded', this); + } + + uploader.cancelConflictResolution = function () { + context.history().pop(); + }; + + uploader.processResolvedConflicts = function (changeset) { + var history = context.history(); + + for (var i = 0; i < _conflicts.length; i++) { + if (_conflicts[i].chosen === 1) { + // user chose "use theirs" + var entity = context.hasEntity(_conflicts[i].id); + + if (entity && entity.type === 'way') { + var children = utilArrayUniq(entity.nodes); + + for (var j = 0; j < children.length; j++) { + history.replace(actionRevert(children[j])); + } + } + + history.replace(actionRevert(_conflicts[i].id)); + } + } + + uploader.save(changeset, true, false); // tryAgain = true, checkConflicts = false + }; + + uploader.reset = function () {}; + + return uploader; + } + + var $$3 = _export; + var fails = fails$N; + var expm1 = mathExpm1; + + var abs = Math.abs; + var exp = Math.exp; + var E = Math.E; + + var FORCED = fails(function () { + // eslint-disable-next-line es/no-math-sinh -- required for testing + return Math.sinh(-2e-17) != -2e-17; + }); + + // `Math.sinh` method + // https://tc39.es/ecma262/#sec-math.sinh + // V8 near Chromium 38 has a problem with very small numbers + $$3({ target: 'Math', stat: true, forced: FORCED }, { + sinh: function sinh(x) { + return abs(x = +x) < 1 ? (expm1(x) - expm1(-x)) / 2 : (exp(x - 1) - exp(-x - 1)) * (E / 2); + } + }); + + var isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2; // listen for DPI change, e.g. when dragging a browser window from a retina to non-retina screen + + window.matchMedia("\n (-webkit-min-device-pixel-ratio: 2), /* Safari */\n (min-resolution: 2dppx), /* standard */\n (min-resolution: 192dpi) /* fallback */\n ").addListener(function () { + isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2; + }); + + function localeDateString(s) { + if (!s) return null; + var options = { + day: 'numeric', + month: 'short', + year: 'numeric' + }; + var d = new Date(s); + if (isNaN(d.getTime())) return null; + return d.toLocaleDateString(_mainLocalizer.localeCode(), options); + } + + function vintageRange(vintage) { + var s; + + if (vintage.start || vintage.end) { + s = vintage.start || '?'; + + if (vintage.start !== vintage.end) { + s += ' - ' + (vintage.end || '?'); + } + } + + return s; + } + + function rendererBackgroundSource(data) { + var source = Object.assign({}, data); // shallow copy + + var _offset = [0, 0]; + var _name = source.name; + var _description = source.description; + + var _best = !!source.best; + + var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template; + + source.tileSize = data.tileSize || 256; + source.zoomExtent = data.zoomExtent || [0, 22]; + source.overzoom = data.overzoom !== false; + + source.offset = function (val) { + if (!arguments.length) return _offset; + _offset = val; + return source; + }; + + source.nudge = function (val, zoomlevel) { + _offset[0] += val[0] / Math.pow(2, zoomlevel); + _offset[1] += val[1] / Math.pow(2, zoomlevel); + return source; + }; + + source.name = function () { + var id_safe = source.id.replace(/\./g, ''); + return _t('imagery.' + id_safe + '.name', { + "default": _name + }); + }; + + source.label = function () { + var id_safe = source.id.replace(/\./g, ''); + return _t.html('imagery.' + id_safe + '.name', { + "default": _name + }); + }; + + source.description = function () { + var id_safe = source.id.replace(/\./g, ''); + return _t.html('imagery.' + id_safe + '.description', { + "default": _description + }); + }; + + source.best = function () { + return _best; + }; + + source.area = function () { + if (!data.polygon) return Number.MAX_VALUE; // worldwide + + var area = d3_geoArea({ + type: 'MultiPolygon', + coordinates: [data.polygon] + }); + return isNaN(area) ? 0 : area; + }; + + source.imageryUsed = function () { + return _name || source.id; + }; + + source.template = function (val) { + if (!arguments.length) return _template; + + if (source.id === 'custom' || source.id === 'Bing') { + _template = val; + } + + return source; + }; + + source.url = function (coord) { + var result = _template; + if (result === '') return result; // source 'none' + // Guess a type based on the tokens present in the template + // (This is for 'custom' source, where we don't know) + + if (!source.type) { + if (/SERVICE=WMS|\{(proj|wkid|bbox)\}/.test(_template)) { + source.type = 'wms'; + source.projection = 'EPSG:3857'; // guess + } else if (/\{(x|y)\}/.test(_template)) { + source.type = 'tms'; + } else if (/\{u\}/.test(_template)) { + source.type = 'bing'; + } + } + + if (source.type === 'wms') { + var tileToProjectedCoords = function tileToProjectedCoords(x, y, z) { + //polyfill for IE11, PhantomJS + var sinh = Math.sinh || function (x) { + var y = Math.exp(x); + return (y - 1 / y) / 2; + }; + + var zoomSize = Math.pow(2, z); + var lon = x / zoomSize * Math.PI * 2 - Math.PI; + var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize))); + + switch (source.projection) { + case 'EPSG:4326': + return { + x: lon * 180 / Math.PI, + y: lat * 180 / Math.PI + }; + + default: + // EPSG:3857 and synonyms + var mercCoords = mercatorRaw(lon, lat); + return { + x: 20037508.34 / Math.PI * mercCoords[0], + y: 20037508.34 / Math.PI * mercCoords[1] + }; + } + }; + + var tileSize = source.tileSize; + var projection = source.projection; + var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]); + var maxXminY = tileToProjectedCoords(coord[0] + 1, coord[1] + 1, coord[2]); + result = result.replace(/\{(\w+)\}/g, function (token, key) { + switch (key) { + case 'width': + case 'height': + return tileSize; + + case 'proj': + return projection; + + case 'wkid': + return projection.replace(/^EPSG:/, ''); + + case 'bbox': + // WMS 1.3 flips x/y for some coordinate systems including EPSG:4326 - #7557 + if (projection === 'EPSG:4326' && // The CRS parameter implies version 1.3 (prior versions use SRS) + /VERSION=1.3|CRS={proj}/.test(source.template().toUpperCase())) { + return maxXminY.y + ',' + minXmaxY.x + ',' + minXmaxY.y + ',' + maxXminY.x; + } else { + return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y; + } + + case 'w': + return minXmaxY.x; + + case 's': + return maxXminY.y; + + case 'n': + return maxXminY.x; + + case 'e': + return minXmaxY.y; + + default: + return token; + } + }); + } else if (source.type === 'tms') { + result = result.replace('{x}', coord[0]).replace('{y}', coord[1]) // TMS-flipped y coordinate + .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1).replace(/\{z(oom)?\}/, coord[2]) // only fetch retina tiles for retina screens + .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : ''); + } else if (source.type === 'bing') { + result = result.replace('{u}', function () { + var u = ''; + + for (var zoom = coord[2]; zoom > 0; zoom--) { + var b = 0; + var mask = 1 << zoom - 1; + if ((coord[0] & mask) !== 0) b++; + if ((coord[1] & mask) !== 0) b += 2; + u += b.toString(); + } + + return u; + }); + } // these apply to any type.. + + + result = result.replace(/\{switch:([^}]+)\}/, function (s, r) { + var subdomains = r.split(','); + return subdomains[(coord[0] + coord[1]) % subdomains.length]; + }); + return result; + }; + + source.validZoom = function (z) { + return source.zoomExtent[0] <= z && (source.overzoom || source.zoomExtent[1] > z); + }; + + source.isLocatorOverlay = function () { + return source.id === 'mapbox_locator_overlay'; + }; + /* hides a source from the list, but leaves it available for use */ + + + source.isHidden = function () { + return source.id === 'DigitalGlobe-Premium-vintage' || source.id === 'DigitalGlobe-Standard-vintage'; + }; + + source.copyrightNotices = function () {}; + + source.getMetadata = function (center, tileCoord, callback) { + var vintage = { + start: localeDateString(source.startDate), + end: localeDateString(source.endDate) + }; + vintage.range = vintageRange(vintage); + var metadata = { + vintage: vintage + }; + callback(null, metadata); + }; + + return source; + } + + rendererBackgroundSource.Bing = function (data, dispatch) { + // https://docs.microsoft.com/en-us/bingmaps/rest-services/imagery/get-imagery-metadata + // https://docs.microsoft.com/en-us/bingmaps/rest-services/directly-accessing-the-bing-maps-tiles + //fallback url template + data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&n=z'; + var bing = rendererBackgroundSource(data); //var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc + + var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q'; // iD + + /* + missing tile image strictness param (n=) + • n=f -> (Fail) returns a 404 + • n=z -> (Empty) returns a 200 with 0 bytes (no content) + • n=t -> (Transparent) returns a 200 with a transparent (png) tile + */ + + var strictParam = 'n'; + var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&uriScheme=https&key=' + key; + var cache = {}; + var inflight = {}; + var providers = []; + d3_json(url).then(function (json) { + var imageryResource = json.resourceSets[0].resources[0]; //retrieve and prepare up to date imagery template + + var template = imageryResource.imageUrl; //https://ecn.{subdomain}.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=10339 + + var subDomains = imageryResource.imageUrlSubdomains; //["t0, t1, t2, t3"] + + var subDomainNumbers = subDomains.map(function (subDomain) { + return subDomain.substring(1); + }).join(','); + template = template.replace('{subdomain}', "t{switch:".concat(subDomainNumbers, "}")).replace('{quadkey}', '{u}'); + + if (!new URLSearchParams(template).has(strictParam)) { + template += "&".concat(strictParam, "=z"); + } + + bing.template(template); + providers = imageryResource.imageryProviders.map(function (provider) { + return { + attribution: provider.attribution, + areas: provider.coverageAreas.map(function (area) { + return { + zoom: [area.zoomMin, area.zoomMax], + extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]]) + }; + }) + }; + }); + dispatch.call('change'); + })["catch"](function () { + /* ignore */ + }); + + bing.copyrightNotices = function (zoom, extent) { + zoom = Math.min(zoom, 21); + return providers.filter(function (provider) { + return provider.areas.some(function (area) { + return extent.intersects(area.extent) && area.zoom[0] <= zoom && area.zoom[1] >= zoom; + }); + }).map(function (provider) { + return provider.attribution; + }).join(', '); + }; + + bing.getMetadata = function (center, tileCoord, callback) { + var tileID = tileCoord.slice(0, 3).join('/'); + var zoom = Math.min(tileCoord[2], 21); + var centerPoint = center[1] + ',' + center[0]; // lat,lng + + var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + '?zl=' + zoom + '&key=' + key; + if (inflight[tileID]) return; + + if (!cache[tileID]) { + cache[tileID] = {}; + } + + if (cache[tileID] && cache[tileID].metadata) { + return callback(null, cache[tileID].metadata); + } + + inflight[tileID] = true; + d3_json(url).then(function (result) { + delete inflight[tileID]; + + if (!result) { + throw new Error('Unknown Error'); + } + + var vintage = { + start: localeDateString(result.resourceSets[0].resources[0].vintageStart), + end: localeDateString(result.resourceSets[0].resources[0].vintageEnd) + }; + vintage.range = vintageRange(vintage); + var metadata = { + vintage: vintage + }; + cache[tileID].metadata = metadata; + if (callback) callback(null, metadata); + })["catch"](function (err) { + delete inflight[tileID]; + if (callback) callback(err.message); + }); + }; + + bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details'; + return bing; + }; + + rendererBackgroundSource.Esri = function (data) { + // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works) + if (data.template.match(/blankTile/) === null) { + data.template = data.template + '?blankTile=false'; + } + + var esri = rendererBackgroundSource(data); + var cache = {}; + var inflight = {}; + + var _prevCenter; // use a tilemap service to set maximum zoom for esri tiles dynamically + // https://developers.arcgis.com/documentation/tiled-elevation-service/ + + + esri.fetchTilemap = function (center) { + // skip if we have already fetched a tilemap within 5km + if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) return; + _prevCenter = center; // tiles are available globally to zoom level 19, afterward they may or may not be present + + var z = 20; // first generate a random url using the template + + var dummyUrl = esri.url([1, 2, 3]); // calculate url z/y/x from the lat/long of the center of the map + + var x = Math.floor((center[0] + 180) / 360 * Math.pow(2, z)); + var y = Math.floor((1 - Math.log(Math.tan(center[1] * Math.PI / 180) + 1 / Math.cos(center[1] * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, z)); // fetch an 8x8 grid to leverage cache + + var tilemapUrl = dummyUrl.replace(/tile\/[0-9]+\/[0-9]+\/[0-9]+\?blankTile=false/, 'tilemap') + '/' + z + '/' + y + '/' + x + '/8/8'; // make the request and introspect the response from the tilemap server + + d3_json(tilemapUrl).then(function (tilemap) { + if (!tilemap) { + throw new Error('Unknown Error'); + } + + var hasTiles = true; + + for (var i = 0; i < tilemap.data.length; i++) { + // 0 means an individual tile in the grid doesn't exist + if (!tilemap.data[i]) { + hasTiles = false; + break; + } + } // if any tiles are missing at level 20 we restrict maxZoom to 19 + + + esri.zoomExtent[1] = hasTiles ? 22 : 19; + })["catch"](function () { + /* ignore */ + }); + }; + + esri.getMetadata = function (center, tileCoord, callback) { + var tileID = tileCoord.slice(0, 3).join('/'); + var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]); + var centerPoint = center[0] + ',' + center[1]; // long, lat (as it should be) + + var unknown = _t('info_panels.background.unknown'); + var metadataLayer; + var vintage = {}; + var metadata = {}; + if (inflight[tileID]) return; + + switch (true) { + case zoom >= 20 && esri.id === 'EsriWorldImageryClarity': + metadataLayer = 4; + break; + + case zoom >= 19: + metadataLayer = 3; + break; + + case zoom >= 17: + metadataLayer = 2; + break; + + case zoom >= 13: + metadataLayer = 0; + break; + + default: + metadataLayer = 99; + } + + var url; // build up query using the layer appropriate to the current zoom + + if (esri.id === 'EsriWorldImagery') { + url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/'; + } else if (esri.id === 'EsriWorldImageryClarity') { + url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/'; + } + + url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json'; + + if (!cache[tileID]) { + cache[tileID] = {}; + } + + if (cache[tileID] && cache[tileID].metadata) { + return callback(null, cache[tileID].metadata); + } // accurate metadata is only available >= 13 + + + if (metadataLayer === 99) { + vintage = { + start: null, + end: null, + range: null + }; + metadata = { + vintage: null, + source: unknown, + description: unknown, + resolution: unknown, + accuracy: unknown + }; + callback(null, metadata); + } else { + inflight[tileID] = true; + d3_json(url).then(function (result) { + delete inflight[tileID]; + + if (!result) { + throw new Error('Unknown Error'); + } else if (result.features && result.features.length < 1) { + throw new Error('No Results'); + } else if (result.error && result.error.message) { + throw new Error(result.error.message); + } // pass through the discrete capture date from metadata + + + var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2); + vintage = { + start: captureDate, + end: captureDate, + range: captureDate + }; + metadata = { + vintage: vintage, + source: clean(result.features[0].attributes.NICE_NAME), + description: clean(result.features[0].attributes.NICE_DESC), + resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)), + accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4)) + }; // append units - meters + + if (isFinite(metadata.resolution)) { + metadata.resolution += ' m'; + } + + if (isFinite(metadata.accuracy)) { + metadata.accuracy += ' m'; + } + + cache[tileID].metadata = metadata; + if (callback) callback(null, metadata); + })["catch"](function (err) { + delete inflight[tileID]; + if (callback) callback(err.message); + }); + } + + function clean(val) { + return String(val).trim() || unknown; + } + }; + + return esri; + }; + + rendererBackgroundSource.None = function () { + var source = rendererBackgroundSource({ + id: 'none', + template: '' + }); + + source.name = function () { + return _t('background.none'); + }; + + source.label = function () { + return _t.html('background.none'); + }; + + source.imageryUsed = function () { + return null; + }; + + source.area = function () { + return -1; // sources in background pane are sorted by area + }; + + return source; + }; + + rendererBackgroundSource.Custom = function (template) { + var source = rendererBackgroundSource({ + id: 'custom', + template: template + }); + + source.name = function () { + return _t('background.custom'); + }; + + source.label = function () { + return _t.html('background.custom'); + }; + + source.imageryUsed = function () { + // sanitize personal connection tokens - #6801 + var cleaned = source.template(); // from query string parameters + + if (cleaned.indexOf('?') !== -1) { + var parts = cleaned.split('?', 2); + var qs = utilStringQs(parts[1]); + ['access_token', 'connectId', 'token'].forEach(function (param) { + if (qs[param]) { + qs[param] = '{apikey}'; + } + }); + cleaned = parts[0] + '?' + utilQsString(qs, true); // true = soft encode + } // from wms/wmts api path parameters + + + cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}'); + return 'Custom (' + cleaned + ' )'; + }; + + source.area = function () { + return -2; // sources in background pane are sorted by area + }; + + return source; + }; + + function rendererTileLayer(context) { + var transformProp = utilPrefixCSSProperty('Transform'); + var tiler = utilTiler(); + var _tileSize = 256; + + var _projection; + + var _cache = {}; + + var _tileOrigin; + + var _zoom; + + var _source; + + function tileSizeAtZoom(d, z) { + var EPSILON = 0.002; // close seams + + return _tileSize * Math.pow(2, z - d[2]) / _tileSize + EPSILON; + } + + function atZoom(t, distance) { + var power = Math.pow(2, distance); + return [Math.floor(t[0] * power), Math.floor(t[1] * power), t[2] + distance]; + } + + function lookUp(d) { + for (var up = -1; up > -d[2]; up--) { + var tile = atZoom(d, up); + + if (_cache[_source.url(tile)] !== false) { + return tile; + } + } + } + + function uniqueBy(a, n) { + var o = []; + var seen = {}; + + for (var i = 0; i < a.length; i++) { + if (seen[a[i][n]] === undefined) { + o.push(a[i]); + seen[a[i][n]] = true; + } + } + + return o; + } + + function addSource(d) { + d.push(_source.url(d)); + return d; + } // Update tiles based on current state of `projection`. + + + function background(selection) { + _zoom = geoScaleToZoom(_projection.scale(), _tileSize); + var pixelOffset; + + if (_source) { + pixelOffset = [_source.offset()[0] * Math.pow(2, _zoom), _source.offset()[1] * Math.pow(2, _zoom)]; + } else { + pixelOffset = [0, 0]; + } + + var translate = [_projection.translate()[0] + pixelOffset[0], _projection.translate()[1] + pixelOffset[1]]; + tiler.scale(_projection.scale() * 2 * Math.PI).translate(translate); + _tileOrigin = [_projection.scale() * Math.PI - translate[0], _projection.scale() * Math.PI - translate[1]]; + render(selection); + } // Derive the tiles onscreen, remove those offscreen and position them. + // Important that this part not depend on `_projection` because it's + // rentered when tiles load/error (see #644). + + + function render(selection) { + if (!_source) return; + var requests = []; + var showDebug = context.getDebug('tile') && !_source.overlay; + + if (_source.validZoom(_zoom)) { + tiler.skipNullIsland(!!_source.overlay); + tiler().forEach(function (d) { + addSource(d); + if (d[3] === '') return; + if (typeof d[3] !== 'string') return; // Workaround for #2295 + + requests.push(d); + + if (_cache[d[3]] === false && lookUp(d)) { + requests.push(addSource(lookUp(d))); + } + }); + requests = uniqueBy(requests, 3).filter(function (r) { + // don't re-request tiles which have failed in the past + return _cache[r[3]] !== false; + }); + } + + function load(d3_event, d) { + _cache[d[3]] = true; + select(this).on('error', null).on('load', null).classed('tile-loaded', true); + render(selection); + } + + function error(d3_event, d) { + _cache[d[3]] = false; + select(this).on('error', null).on('load', null).remove(); + render(selection); + } + + function imageTransform(d) { + var ts = _tileSize * Math.pow(2, _zoom - d[2]); + + var scale = tileSizeAtZoom(d, _zoom); + return 'translate(' + (d[0] * ts - _tileOrigin[0]) + 'px,' + (d[1] * ts - _tileOrigin[1]) + 'px) ' + 'scale(' + scale + ',' + scale + ')'; + } + + function tileCenter(d) { + var ts = _tileSize * Math.pow(2, _zoom - d[2]); + + return [d[0] * ts - _tileOrigin[0] + ts / 2, d[1] * ts - _tileOrigin[1] + ts / 2]; + } + + function debugTransform(d) { + var coord = tileCenter(d); + return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)'; + } // Pick a representative tile near the center of the viewport + // (This is useful for sampling the imagery vintage) + + + var dims = tiler.size(); + var mapCenter = [dims[0] / 2, dims[1] / 2]; + var minDist = Math.max(dims[0], dims[1]); + var nearCenter; + requests.forEach(function (d) { + var c = tileCenter(d); + var dist = geoVecLength(c, mapCenter); + + if (dist < minDist) { + minDist = dist; + nearCenter = d; + } + }); + var image = selection.selectAll('img').data(requests, function (d) { + return d[3]; + }); + image.exit().style(transformProp, imageTransform).classed('tile-removing', true).classed('tile-center', false).each(function () { + var tile = select(this); + window.setTimeout(function () { + if (tile.classed('tile-removing')) { + tile.remove(); + } + }, 300); + }); + image.enter().append('img').attr('class', 'tile').attr('draggable', 'false').style('width', _tileSize + 'px').style('height', _tileSize + 'px').attr('src', function (d) { + return d[3]; + }).on('error', error).on('load', load).merge(image).style(transformProp, imageTransform).classed('tile-debug', showDebug).classed('tile-removing', false).classed('tile-center', function (d) { + return d === nearCenter; + }); + var debug = selection.selectAll('.tile-label-debug').data(showDebug ? requests : [], function (d) { + return d[3]; + }); + debug.exit().remove(); + + if (showDebug) { + var debugEnter = debug.enter().append('div').attr('class', 'tile-label-debug'); + debugEnter.append('div').attr('class', 'tile-label-debug-coord'); + debugEnter.append('div').attr('class', 'tile-label-debug-vintage'); + debug = debug.merge(debugEnter); + debug.style(transformProp, debugTransform); + debug.selectAll('.tile-label-debug-coord').html(function (d) { + return d[2] + ' / ' + d[0] + ' / ' + d[1]; + }); + debug.selectAll('.tile-label-debug-vintage').each(function (d) { + var span = select(this); + var center = context.projection.invert(tileCenter(d)); + + _source.getMetadata(center, d, function (err, result) { + span.html(result && result.vintage && result.vintage.range || _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown')); + }); + }); + } + } + + background.projection = function (val) { + if (!arguments.length) return _projection; + _projection = val; + return background; + }; + + background.dimensions = function (val) { + if (!arguments.length) return tiler.size(); + tiler.size(val); + return background; + }; + + background.source = function (val) { + if (!arguments.length) return _source; + _source = val; + _tileSize = _source.tileSize; + _cache = {}; + tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent); + return background; + }; + + return background; + } + + var _imageryIndex = null; + function rendererBackground(context) { + var dispatch = dispatch$8('change'); + var detected = utilDetect(); + var baseLayer = rendererTileLayer(context).projection(context.projection); + var _isValid = true; + var _overlayLayers = []; + var _brightness = 1; + var _contrast = 1; + var _saturation = 1; + var _sharpness = 1; + + function ensureImageryIndex() { + return _mainFileFetcher.get('imagery').then(function (sources) { + if (_imageryIndex) return _imageryIndex; + _imageryIndex = { + imagery: sources, + features: {} + }; // use which-polygon to support efficient index and querying for imagery + + var features = sources.map(function (source) { + if (!source.polygon) return null; // workaround for editor-layer-index weirdness.. + // Add an extra array nest to each element in `source.polygon` + // so the rings are not treated as a bunch of holes: + // what we have: [ [[outer],[hole],[hole]] ] + // what we want: [ [[outer]],[[outer]],[[outer]] ] + + var rings = source.polygon.map(function (ring) { + return [ring]; + }); + var feature = { + type: 'Feature', + properties: { + id: source.id + }, + geometry: { + type: 'MultiPolygon', + coordinates: rings + } + }; + _imageryIndex.features[source.id] = feature; + return feature; + }).filter(Boolean); + _imageryIndex.query = whichPolygon_1({ + type: 'FeatureCollection', + features: features + }); // Instantiate `rendererBackgroundSource` objects for each source + + _imageryIndex.backgrounds = sources.map(function (source) { + if (source.type === 'bing') { + return rendererBackgroundSource.Bing(source, dispatch); + } else if (/^EsriWorldImagery/.test(source.id)) { + return rendererBackgroundSource.Esri(source); + } else { + return rendererBackgroundSource(source); + } + }); // Add 'None' + + _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None()); // Add 'Custom' + + + var template = corePreferences('background-custom-template') || ''; + var custom = rendererBackgroundSource.Custom(template); + + _imageryIndex.backgrounds.unshift(custom); + + return _imageryIndex; + }); + } + + function background(selection) { + var currSource = baseLayer.source(); // If we are displaying an Esri basemap at high zoom, + // check its tilemap to see how high the zoom can go + + if (context.map().zoom() > 18) { + if (currSource && /^EsriWorldImagery/.test(currSource.id)) { + var center = context.map().center(); + currSource.fetchTilemap(center); + } + } // Is the imagery valid here? - #4827 + + + var sources = background.sources(context.map().extent()); + var wasValid = _isValid; + _isValid = !!sources.filter(function (d) { + return d === currSource; + }).length; + + if (wasValid !== _isValid) { + // change in valid status + background.updateImagery(); + } + + var baseFilter = ''; + + if (detected.cssfilters) { + if (_brightness !== 1) { + baseFilter += " brightness(".concat(_brightness, ")"); + } + + if (_contrast !== 1) { + baseFilter += " contrast(".concat(_contrast, ")"); + } + + if (_saturation !== 1) { + baseFilter += " saturate(".concat(_saturation, ")"); + } + + if (_sharpness < 1) { + // gaussian blur + var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness); + baseFilter += " blur(".concat(blur, "px)"); + } + } + + var base = selection.selectAll('.layer-background').data([0]); + base = base.enter().insert('div', '.layer-data').attr('class', 'layer layer-background').merge(base); + + if (detected.cssfilters) { + base.style('filter', baseFilter || null); + } else { + base.style('opacity', _brightness); + } + + var imagery = base.selectAll('.layer-imagery').data([0]); + imagery.enter().append('div').attr('class', 'layer layer-imagery').merge(imagery).call(baseLayer); + var maskFilter = ''; + var mixBlendMode = ''; + + if (detected.cssfilters && _sharpness > 1) { + // apply unsharp mask + mixBlendMode = 'overlay'; + maskFilter = 'saturate(0) blur(3px) invert(1)'; + var contrast = _sharpness - 1; + maskFilter += " contrast(".concat(contrast, ")"); + var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1); + maskFilter += " brightness(".concat(brightness, ")"); + } + + var mask = base.selectAll('.layer-unsharp-mask').data(detected.cssfilters && _sharpness > 1 ? [0] : []); + mask.exit().remove(); + mask.enter().append('div').attr('class', 'layer layer-mask layer-unsharp-mask').merge(mask).call(baseLayer).style('filter', maskFilter || null).style('mix-blend-mode', mixBlendMode || null); + var overlays = selection.selectAll('.layer-overlay').data(_overlayLayers, function (d) { + return d.source().name(); + }); + overlays.exit().remove(); + overlays.enter().insert('div', '.layer-data').attr('class', 'layer layer-overlay').merge(overlays).each(function (layer, i, nodes) { + return select(nodes[i]).call(layer); + }); + } + + background.updateImagery = function () { + var currSource = baseLayer.source(); + if (context.inIntro() || !currSource) return; + + var o = _overlayLayers.filter(function (d) { + return !d.source().isLocatorOverlay() && !d.source().isHidden(); + }).map(function (d) { + return d.source().id; + }).join(','); + + var meters = geoOffsetToMeters(currSource.offset()); + var EPSILON = 0.01; + var x = +meters[0].toFixed(2); + var y = +meters[1].toFixed(2); + var hash = utilStringQs(window.location.hash); + var id = currSource.id; + + if (id === 'custom') { + id = "custom:".concat(currSource.template()); + } + + if (id) { + hash.background = id; + } else { + delete hash.background; + } + + if (o) { + hash.overlays = o; + } else { + delete hash.overlays; + } + + if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) { + hash.offset = "".concat(x, ",").concat(y); + } else { + delete hash.offset; + } + + if (!window.mocha) { + window.location.replace('#' + utilQsString(hash, true)); + } + + var imageryUsed = []; + var photoOverlaysUsed = []; + var currUsed = currSource.imageryUsed(); + + if (currUsed && _isValid) { + imageryUsed.push(currUsed); + } + + _overlayLayers.filter(function (d) { + return !d.source().isLocatorOverlay() && !d.source().isHidden(); + }).forEach(function (d) { + return imageryUsed.push(d.source().imageryUsed()); + }); + + var dataLayer = context.layers().layer('data'); + + if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) { + imageryUsed.push(dataLayer.getSrc()); + } + + var photoOverlayLayers = { + streetside: 'Bing Streetside', + mapillary: 'Mapillary Images', + 'mapillary-map-features': 'Mapillary Map Features', + 'mapillary-signs': 'Mapillary Signs', + openstreetcam: 'OpenStreetCam Images' + }; + + for (var layerID in photoOverlayLayers) { + var layer = context.layers().layer(layerID); + + if (layer && layer.enabled()) { + photoOverlaysUsed.push(layerID); + imageryUsed.push(photoOverlayLayers[layerID]); + } + } + + context.history().imageryUsed(imageryUsed); + context.history().photoOverlaysUsed(photoOverlaysUsed); + }; + + var _checkedBlocklists; + + background.sources = function (extent, zoom, includeCurrent) { + if (!_imageryIndex) return []; // called before init()? + + var visible = {}; + (_imageryIndex.query.bbox(extent.rectangle(), true) || []).forEach(function (d) { + return visible[d.id] = true; + }); + var currSource = baseLayer.source(); + var osm = context.connection(); + var blocklists = osm && osm.imageryBlocklists(); + + if (blocklists && blocklists !== _checkedBlocklists) { + _imageryIndex.backgrounds.forEach(function (source) { + source.isBlocked = blocklists.some(function (blocklist) { + return blocklist.test(source.template()); + }); + }); + + _checkedBlocklists = blocklists; + } + + return _imageryIndex.backgrounds.filter(function (source) { + if (includeCurrent && currSource === source) return true; // optionally always include the current imagery + + if (source.isBlocked) return false; // even bundled sources may be blocked - #7905 + + if (!source.polygon) return true; // always include imagery with worldwide coverage + + if (zoom && zoom < 6) return false; // optionally exclude local imagery at low zooms + + return visible[source.id]; // include imagery visible in given extent + }); + }; + + background.dimensions = function (val) { + if (!val) return; + baseLayer.dimensions(val); + + _overlayLayers.forEach(function (layer) { + return layer.dimensions(val); + }); + }; + + background.baseLayerSource = function (d) { + if (!arguments.length) return baseLayer.source(); // test source against OSM imagery blocklists.. + + var osm = context.connection(); + if (!osm) return background; + var blocklists = osm.imageryBlocklists(); + var template = d.template(); + var fail = false; + var tested = 0; + var regex; + + for (var i = 0; i < blocklists.length; i++) { + regex = blocklists[i]; + fail = regex.test(template); + tested++; + if (fail) break; + } // ensure at least one test was run. + + + if (!tested) { + regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/; + fail = regex.test(template); + } + + baseLayer.source(!fail ? d : background.findSource('none')); + dispatch.call('change'); + background.updateImagery(); + return background; + }; + + background.findSource = function (id) { + if (!id || !_imageryIndex) return null; // called before init()? + + return _imageryIndex.backgrounds.find(function (d) { + return d.id && d.id === id; + }); + }; + + background.bing = function () { + background.baseLayerSource(background.findSource('Bing')); + }; + + background.showsLayer = function (d) { + var currSource = baseLayer.source(); + if (!d || !currSource) return false; + return d.id === currSource.id || _overlayLayers.some(function (layer) { + return d.id === layer.source().id; + }); + }; + + background.overlayLayerSources = function () { + return _overlayLayers.map(function (layer) { + return layer.source(); + }); + }; + + background.toggleOverlayLayer = function (d) { + var layer; + + for (var i = 0; i < _overlayLayers.length; i++) { + layer = _overlayLayers[i]; + + if (layer.source() === d) { + _overlayLayers.splice(i, 1); + + dispatch.call('change'); + background.updateImagery(); + return; + } + } + + layer = rendererTileLayer(context).source(d).projection(context.projection).dimensions(baseLayer.dimensions()); + + _overlayLayers.push(layer); + + dispatch.call('change'); + background.updateImagery(); + }; + + background.nudge = function (d, zoom) { + var currSource = baseLayer.source(); + + if (currSource) { + currSource.nudge(d, zoom); + dispatch.call('change'); + background.updateImagery(); + } + + return background; + }; + + background.offset = function (d) { + var currSource = baseLayer.source(); + + if (!arguments.length) { + return currSource && currSource.offset() || [0, 0]; + } + + if (currSource) { + currSource.offset(d); + dispatch.call('change'); + background.updateImagery(); + } + + return background; + }; + + background.brightness = function (d) { + if (!arguments.length) return _brightness; + _brightness = d; + if (context.mode()) dispatch.call('change'); + return background; + }; + + background.contrast = function (d) { + if (!arguments.length) return _contrast; + _contrast = d; + if (context.mode()) dispatch.call('change'); + return background; + }; + + background.saturation = function (d) { + if (!arguments.length) return _saturation; + _saturation = d; + if (context.mode()) dispatch.call('change'); + return background; + }; + + background.sharpness = function (d) { + if (!arguments.length) return _sharpness; + _sharpness = d; + if (context.mode()) dispatch.call('change'); + return background; + }; + + var _loadPromise; + + background.ensureLoaded = function () { + if (_loadPromise) return _loadPromise; + + function parseMapParams(qmap) { + if (!qmap) return false; + var params = qmap.split('/').map(Number); + if (params.length < 3 || params.some(isNaN)) return false; + return geoExtent([params[2], params[1]]); // lon,lat + } + + var hash = utilStringQs(window.location.hash); + var requested = hash.background || hash.layer; + var extent = parseMapParams(hash.map); + return _loadPromise = ensureImageryIndex().then(function (imageryIndex) { + var first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0]; + var best; + + if (!requested && extent) { + best = background.sources(extent).find(function (s) { + return s.best(); + }); + } // Decide which background layer to display + + + if (requested && requested.indexOf('custom:') === 0) { + var template = requested.replace(/^custom:/, ''); + var custom = background.findSource('custom'); + background.baseLayerSource(custom.template(template)); + corePreferences('background-custom-template', template); + } else { + background.baseLayerSource(background.findSource(requested) || best || background.findSource(corePreferences('background-last-used')) || background.findSource('Bing') || first || background.findSource('none')); + } + + var locator = imageryIndex.backgrounds.find(function (d) { + return d.overlay && d["default"]; + }); + + if (locator) { + background.toggleOverlayLayer(locator); + } + + var overlays = (hash.overlays || '').split(','); + overlays.forEach(function (overlay) { + overlay = background.findSource(overlay); + + if (overlay) { + background.toggleOverlayLayer(overlay); + } + }); + + if (hash.gpx) { + var gpx = context.layers().layer('data'); + + if (gpx) { + gpx.url(hash.gpx, '.gpx'); + } + } + + if (hash.offset) { + var offset = hash.offset.replace(/;/g, ',').split(',').map(function (n) { + return !isNaN(n) && n; + }); + + if (offset.length === 2) { + background.offset(geoMetersToOffset(offset)); + } + } + })["catch"](function () { + /* ignore */ + }); + }; + + return utilRebind(background, dispatch, 'on'); + } + + function rendererFeatures(context) { + var dispatch = dispatch$8('change', 'redraw'); + var features = utilRebind({}, dispatch, 'on'); + + var _deferred = new Set(); + + var traffic_roads = { + 'motorway': true, + 'motorway_link': true, + 'trunk': true, + 'trunk_link': true, + 'primary': true, + 'primary_link': true, + 'secondary': true, + 'secondary_link': true, + 'tertiary': true, + 'tertiary_link': true, + 'residential': true, + 'unclassified': true, + 'living_street': true + }; + var service_roads = { + 'service': true, + 'road': true, + 'track': true + }; + var paths = { + 'path': true, + 'footway': true, + 'cycleway': true, + 'bridleway': true, + 'steps': true, + 'pedestrian': true + }; + var past_futures = { + 'proposed': true, + 'construction': true, + 'abandoned': true, + 'dismantled': true, + 'disused': true, + 'razed': true, + 'demolished': true, + 'obliterated': true + }; + var _cullFactor = 1; + var _cache = {}; + var _rules = {}; + var _stats = {}; + var _keys = []; + var _hidden = []; + var _forceVisible = {}; + + function update() { + if (!window.mocha) { + var hash = utilStringQs(window.location.hash); + var disabled = features.disabled(); + + if (disabled.length) { + hash.disable_features = disabled.join(','); + } else { + delete hash.disable_features; + } + + window.location.replace('#' + utilQsString(hash, true)); + corePreferences('disabled-features', disabled.join(',')); + } + + _hidden = features.hidden(); + dispatch.call('change'); + dispatch.call('redraw'); + } + + function defineRule(k, filter, max) { + var isEnabled = true; + + _keys.push(k); + + _rules[k] = { + filter: filter, + enabled: isEnabled, + // whether the user wants it enabled.. + count: 0, + currentMax: max || Infinity, + defaultMax: max || Infinity, + enable: function enable() { + this.enabled = true; + this.currentMax = this.defaultMax; + }, + disable: function disable() { + this.enabled = false; + this.currentMax = 0; + }, + hidden: function hidden() { + return this.count === 0 && !this.enabled || this.count > this.currentMax * _cullFactor; + }, + autoHidden: function autoHidden() { + return this.hidden() && this.currentMax > 0; + } + }; + } + + defineRule('points', function isPoint(tags, geometry) { + return geometry === 'point'; + }, 200); + defineRule('traffic_roads', function isTrafficRoad(tags) { + return traffic_roads[tags.highway]; + }); + defineRule('service_roads', function isServiceRoad(tags) { + return service_roads[tags.highway]; + }); + defineRule('paths', function isPath(tags) { + return paths[tags.highway]; + }); + defineRule('buildings', function isBuilding(tags) { + return !!tags.building && tags.building !== 'no' || tags.parking === 'multi-storey' || tags.parking === 'sheds' || tags.parking === 'carports' || tags.parking === 'garage_boxes'; + }, 250); + defineRule('building_parts', function isBuildingPart(tags) { + return tags['building:part']; + }); + defineRule('indoor', function isIndoor(tags) { + return tags.indoor; + }); + defineRule('landuse', function isLanduse(tags, geometry) { + return geometry === 'area' && !_rules.buildings.filter(tags) && !_rules.building_parts.filter(tags) && !_rules.indoor.filter(tags) && !_rules.water.filter(tags) && !_rules.pistes.filter(tags); + }); + defineRule('boundaries', function isBoundary(tags) { + return !!tags.boundary && !(traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway] || tags.waterway || tags.railway || tags.landuse || tags.natural || tags.building || tags.power); + }); + defineRule('water', function isWater(tags) { + return !!tags.waterway || tags.natural === 'water' || tags.natural === 'coastline' || tags.natural === 'bay' || tags.landuse === 'pond' || tags.landuse === 'basin' || tags.landuse === 'reservoir' || tags.landuse === 'salt_pond'; + }); + defineRule('rail', function isRail(tags) { + return (!!tags.railway || tags.landuse === 'railway') && !(traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]); + }); + defineRule('pistes', function isPiste(tags) { + return tags['piste:type']; + }); + defineRule('aerialways', function isPiste(tags) { + return tags.aerialway && tags.aerialway !== 'yes' && tags.aerialway !== 'station'; + }); + defineRule('power', function isPower(tags) { + return !!tags.power; + }); // contains a past/future tag, but not in active use as a road/path/cycleway/etc.. + + defineRule('past_future', function isPastFuture(tags) { + if (traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]) { + return false; + } + + var strings = Object.keys(tags); + + for (var i = 0; i < strings.length; i++) { + var s = strings[i]; + + if (past_futures[s] || past_futures[tags[s]]) { + return true; + } + } + + return false; + }); // Lines or areas that don't match another feature filter. + // IMPORTANT: The 'others' feature must be the last one defined, + // so that code in getMatches can skip this test if `hasMatch = true` + + defineRule('others', function isOther(tags, geometry) { + return geometry === 'line' || geometry === 'area'; + }); + + features.features = function () { + return _rules; + }; + + features.keys = function () { + return _keys; + }; + + features.enabled = function (k) { + if (!arguments.length) { + return _keys.filter(function (k) { + return _rules[k].enabled; + }); + } + + return _rules[k] && _rules[k].enabled; + }; + + features.disabled = function (k) { + if (!arguments.length) { + return _keys.filter(function (k) { + return !_rules[k].enabled; + }); + } + + return _rules[k] && !_rules[k].enabled; + }; + + features.hidden = function (k) { + if (!arguments.length) { + return _keys.filter(function (k) { + return _rules[k].hidden(); + }); + } + + return _rules[k] && _rules[k].hidden(); + }; + + features.autoHidden = function (k) { + if (!arguments.length) { + return _keys.filter(function (k) { + return _rules[k].autoHidden(); + }); + } + + return _rules[k] && _rules[k].autoHidden(); + }; + + features.enable = function (k) { + if (_rules[k] && !_rules[k].enabled) { + _rules[k].enable(); + + update(); + } + }; + + features.enableAll = function () { + var didEnable = false; + + for (var k in _rules) { + if (!_rules[k].enabled) { + didEnable = true; + + _rules[k].enable(); + } + } + + if (didEnable) update(); + }; + + features.disable = function (k) { + if (_rules[k] && _rules[k].enabled) { + _rules[k].disable(); + + update(); + } + }; + + features.disableAll = function () { + var didDisable = false; + + for (var k in _rules) { + if (_rules[k].enabled) { + didDisable = true; + + _rules[k].disable(); + } + } + + if (didDisable) update(); + }; + + features.toggle = function (k) { + if (_rules[k]) { + (function (f) { + return f.enabled ? f.disable() : f.enable(); + })(_rules[k]); + + update(); + } + }; + + features.resetStats = function () { + for (var i = 0; i < _keys.length; i++) { + _rules[_keys[i]].count = 0; + } + + dispatch.call('change'); + }; + + features.gatherStats = function (d, resolver, dimensions) { + var needsRedraw = false; + var types = utilArrayGroupBy(d, 'type'); + var entities = [].concat(types.relation || [], types.way || [], types.node || []); + var currHidden, geometry, matches, i, j; + + for (i = 0; i < _keys.length; i++) { + _rules[_keys[i]].count = 0; + } // adjust the threshold for point/building culling based on viewport size.. + // a _cullFactor of 1 corresponds to a 1000x1000px viewport.. + + + _cullFactor = dimensions[0] * dimensions[1] / 1000000; + + for (i = 0; i < entities.length; i++) { + geometry = entities[i].geometry(resolver); + matches = Object.keys(features.getMatches(entities[i], resolver, geometry)); + + for (j = 0; j < matches.length; j++) { + _rules[matches[j]].count++; + } + } + + currHidden = features.hidden(); + + if (currHidden !== _hidden) { + _hidden = currHidden; + needsRedraw = true; + dispatch.call('change'); + } + + return needsRedraw; + }; + + features.stats = function () { + for (var i = 0; i < _keys.length; i++) { + _stats[_keys[i]] = _rules[_keys[i]].count; + } + + return _stats; + }; + + features.clear = function (d) { + for (var i = 0; i < d.length; i++) { + features.clearEntity(d[i]); + } + }; + + features.clearEntity = function (entity) { + delete _cache[osmEntity.key(entity)]; + }; + + features.reset = function () { + Array.from(_deferred).forEach(function (handle) { + window.cancelIdleCallback(handle); + + _deferred["delete"](handle); + }); + _cache = {}; + }; // only certain relations are worth checking + + + function relationShouldBeChecked(relation) { + // multipolygon features have `area` geometry and aren't checked here + return relation.tags.type === 'boundary'; + } + + features.getMatches = function (entity, resolver, geometry) { + if (geometry === 'vertex' || geometry === 'relation' && !relationShouldBeChecked(entity)) return {}; + var ent = osmEntity.key(entity); + + if (!_cache[ent]) { + _cache[ent] = {}; + } + + if (!_cache[ent].matches) { + var matches = {}; + var hasMatch = false; + + for (var i = 0; i < _keys.length; i++) { + if (_keys[i] === 'others') { + if (hasMatch) continue; // If an entity... + // 1. is a way that hasn't matched other 'interesting' feature rules, + + if (entity.type === 'way') { + var parents = features.getParents(entity, resolver, geometry); // 2a. belongs only to a single multipolygon relation + + if (parents.length === 1 && parents[0].isMultipolygon() || // 2b. or belongs only to boundary relations + parents.length > 0 && parents.every(function (parent) { + return parent.tags.type === 'boundary'; + })) { + // ...then match whatever feature rules the parent relation has matched. + // see #2548, #2887 + // + // IMPORTANT: + // For this to work, getMatches must be called on relations before ways. + // + var pkey = osmEntity.key(parents[0]); + + if (_cache[pkey] && _cache[pkey].matches) { + matches = Object.assign({}, _cache[pkey].matches); // shallow copy + + continue; + } + } + } + } + + if (_rules[_keys[i]].filter(entity.tags, geometry)) { + matches[_keys[i]] = hasMatch = true; + } + } + + _cache[ent].matches = matches; + } + + return _cache[ent].matches; + }; + + features.getParents = function (entity, resolver, geometry) { + if (geometry === 'point') return []; + var ent = osmEntity.key(entity); + + if (!_cache[ent]) { + _cache[ent] = {}; + } + + if (!_cache[ent].parents) { + var parents = []; + + if (geometry === 'vertex') { + parents = resolver.parentWays(entity); + } else { + // 'line', 'area', 'relation' + parents = resolver.parentRelations(entity); + } + + _cache[ent].parents = parents; + } + + return _cache[ent].parents; + }; + + features.isHiddenPreset = function (preset, geometry) { + if (!_hidden.length) return false; + if (!preset.tags) return false; + var test = preset.setTags({}, geometry); + + for (var key in _rules) { + if (_rules[key].filter(test, geometry)) { + if (_hidden.indexOf(key) !== -1) { + return key; + } + + return false; + } + } + + return false; + }; + + features.isHiddenFeature = function (entity, resolver, geometry) { + if (!_hidden.length) return false; + if (!entity.version) return false; + if (_forceVisible[entity.id]) return false; + var matches = Object.keys(features.getMatches(entity, resolver, geometry)); + return matches.length && matches.every(function (k) { + return features.hidden(k); + }); + }; + + features.isHiddenChild = function (entity, resolver, geometry) { + if (!_hidden.length) return false; + if (!entity.version || geometry === 'point') return false; + if (_forceVisible[entity.id]) return false; + var parents = features.getParents(entity, resolver, geometry); + if (!parents.length) return false; + + for (var i = 0; i < parents.length; i++) { + if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) { + return false; + } + } + + return true; + }; + + features.hasHiddenConnections = function (entity, resolver) { + if (!_hidden.length) return false; + var childNodes, connections; + + if (entity.type === 'midpoint') { + childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])]; + connections = []; + } else { + childNodes = entity.nodes ? resolver.childNodes(entity) : []; + connections = features.getParents(entity, resolver, entity.geometry(resolver)); + } // gather ways connected to child nodes.. + + + connections = childNodes.reduce(function (result, e) { + return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result; + }, connections); + return connections.some(function (e) { + return features.isHidden(e, resolver, e.geometry(resolver)); + }); + }; + + features.isHidden = function (entity, resolver, geometry) { + if (!_hidden.length) return false; + if (!entity.version) return false; + var fn = geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature; + return fn(entity, resolver, geometry); + }; + + features.filter = function (d, resolver) { + if (!_hidden.length) return d; + var result = []; + + for (var i = 0; i < d.length; i++) { + var entity = d[i]; + + if (!features.isHidden(entity, resolver, entity.geometry(resolver))) { + result.push(entity); + } + } + + return result; + }; + + features.forceVisible = function (entityIDs) { + if (!arguments.length) return Object.keys(_forceVisible); + _forceVisible = {}; + + for (var i = 0; i < entityIDs.length; i++) { + _forceVisible[entityIDs[i]] = true; + var entity = context.hasEntity(entityIDs[i]); + + if (entity && entity.type === 'relation') { + // also show relation members (one level deep) + for (var j in entity.members) { + _forceVisible[entity.members[j].id] = true; + } + } + } + + return features; + }; + + features.init = function () { + var storage = corePreferences('disabled-features'); + + if (storage) { + var storageDisabled = storage.replace(/;/g, ',').split(','); + storageDisabled.forEach(features.disable); + } + + var hash = utilStringQs(window.location.hash); + + if (hash.disable_features) { + var hashDisabled = hash.disable_features.replace(/;/g, ',').split(','); + hashDisabled.forEach(features.disable); + } + }; // warm up the feature matching cache upon merging fetched data + + + context.history().on('merge.features', function (newEntities) { + if (!newEntities) return; + var handle = window.requestIdleCallback(function () { + var graph = context.graph(); + var types = utilArrayGroupBy(newEntities, 'type'); // ensure that getMatches is called on relations before ways + + var entities = [].concat(types.relation || [], types.way || [], types.node || []); + + for (var i = 0; i < entities.length; i++) { + var geometry = entities[i].geometry(graph); + features.getMatches(entities[i], graph, geometry); + } + }); + + _deferred.add(handle); + }); + return features; + } + + /** Error message constants. */ + + var FUNC_ERROR_TEXT = 'Expected a function'; + /** + * Creates a throttled function that only invokes `func` at most once per + * every `wait` milliseconds. The throttled function comes with a `cancel` + * method to cancel delayed `func` invocations and a `flush` method to + * immediately invoke them. Provide `options` to indicate whether `func` + * should be invoked on the leading and/or trailing edge of the `wait` + * timeout. The `func` is invoked with the last arguments provided to the + * throttled function. Subsequent calls to the throttled function return the + * result of the last `func` invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the throttled function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.throttle` and `_.debounce`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to throttle. + * @param {number} [wait=0] The number of milliseconds to throttle invocations to. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=true] + * Specify invoking on the leading edge of the timeout. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new throttled function. + * @example + * + * // Avoid excessively updating the position while scrolling. + * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); + * + * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. + * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); + * jQuery(element).on('click', throttled); + * + * // Cancel the trailing throttled invocation. + * jQuery(window).on('popstate', throttled.cancel); + */ + + function throttle(func, wait, options) { + var leading = true, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + + if (isObject$2(options)) { + leading = 'leading' in options ? !!options.leading : leading; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + return debounce(func, wait, { + 'leading': leading, + 'maxWait': wait, + 'trailing': trailing + }); + } + + // + // - the activeID - nope + // - 1 away (adjacent) to the activeID - yes (vertices will be merged) + // - 2 away from the activeID - nope (would create a self intersecting segment) + // - all others on a linear way - yes + // - all others on a closed way - nope (would create a self intersecting polygon) + // + // returns + // 0 = active vertex - no touch/connect + // 1 = passive vertex - yes touch/connect + // 2 = adjacent vertex - yes but pay attention segmenting a line here + // + + function svgPassiveVertex(node, graph, activeID) { + if (!activeID) return 1; + if (activeID === node.id) return 0; + var parents = graph.parentWays(node); + var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max; + + for (i = 0; i < parents.length; i++) { + nodes = parents[i].nodes; + isClosed = parents[i].isClosed(); + + for (j = 0; j < nodes.length; j++) { + // find this vertex, look nearby + if (nodes[j] === node.id) { + ix1 = j - 2; + ix2 = j - 1; + ix3 = j + 1; + ix4 = j + 2; + + if (isClosed) { + // wraparound if needed + max = nodes.length - 1; + if (ix1 < 0) ix1 = max + ix1; + if (ix2 < 0) ix2 = max + ix2; + if (ix3 > max) ix3 = ix3 - max; + if (ix4 > max) ix4 = ix4 - max; + } + + if (nodes[ix1] === activeID) return 0; // no - prevent self intersect + else if (nodes[ix2] === activeID) return 2; // ok - adjacent + else if (nodes[ix3] === activeID) return 2; // ok - adjacent + else if (nodes[ix4] === activeID) return 0; // no - prevent self intersect + else if (isClosed && nodes.indexOf(activeID) !== -1) return 0; // no - prevent self intersect + } + } + } + + return 1; // ok + } + function svgMarkerSegments(projection, graph, dt, shouldReverse, bothDirections) { + return function (entity) { + var i = 0; + var offset = dt; + var segments = []; + var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream; + var coordinates = graph.childNodes(entity).map(function (n) { + return n.loc; + }); + var a, b; + + if (shouldReverse(entity)) { + coordinates.reverse(); + } + + d3_geoStream({ + type: 'LineString', + coordinates: coordinates + }, projection.stream(clip({ + lineStart: function lineStart() {}, + lineEnd: function lineEnd() { + a = null; + }, + point: function point(x, y) { + b = [x, y]; + + if (a) { + var span = geoVecLength(a, b) - offset; + + if (span >= 0) { + var heading = geoVecAngle(a, b); + var dx = dt * Math.cos(heading); + var dy = dt * Math.sin(heading); + var p = [a[0] + offset * Math.cos(heading), a[1] + offset * Math.sin(heading)]; // gather coordinates + + var coord = [a, p]; + + for (span -= dt; span >= 0; span -= dt) { + p = geoVecAdd(p, [dx, dy]); + coord.push(p); + } + + coord.push(b); // generate svg paths + + var segment = ''; + var j; + + for (j = 0; j < coord.length; j++) { + segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1]; + } + + segments.push({ + id: entity.id, + index: i++, + d: segment + }); + + if (bothDirections(entity)) { + segment = ''; + + for (j = coord.length - 1; j >= 0; j--) { + segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1]; + } + + segments.push({ + id: entity.id, + index: i++, + d: segment + }); + } + } + + offset = -span; + } + + a = b; + } + }))); + return segments; + }; + } + function svgPath(projection, graph, isArea) { + // Explanation of magic numbers: + // "padding" here allows space for strokes to extend beyond the viewport, + // so that the stroke isn't drawn along the edge of the viewport when + // the shape is clipped. + // + // When drawing lines, pad viewport by 5px. + // When drawing areas, pad viewport by 65px in each direction to allow + // for 60px area fill stroke (see ".fill-partial path.fill" css rule) + var cache = {}; + var padding = isArea ? 65 : 5; + var viewport = projection.clipExtent(); + var paddedExtent = [[viewport[0][0] - padding, viewport[0][1] - padding], [viewport[1][0] + padding, viewport[1][1] + padding]]; + var clip = d3_geoIdentity().clipExtent(paddedExtent).stream; + var project = projection.stream; + var path = d3_geoPath().projection({ + stream: function stream(output) { + return project(clip(output)); + } + }); + + var svgpath = function svgpath(entity) { + if (entity.id in cache) { + return cache[entity.id]; + } else { + return cache[entity.id] = path(entity.asGeoJSON(graph)); + } + }; + + svgpath.geojson = function (d) { + if (d.__featurehash__ !== undefined) { + if (d.__featurehash__ in cache) { + return cache[d.__featurehash__]; + } else { + return cache[d.__featurehash__] = path(d); + } + } else { + return path(d); + } + }; + + return svgpath; + } + function svgPointTransform(projection) { + var svgpoint = function svgpoint(entity) { + // http://jsperf.com/short-array-join + var pt = projection(entity.loc); + return 'translate(' + pt[0] + ',' + pt[1] + ')'; + }; + + svgpoint.geojson = function (d) { + return svgpoint(d.properties.entity); + }; + + return svgpoint; + } + function svgRelationMemberTags(graph) { + return function (entity) { + var tags = entity.tags; + var shouldCopyMultipolygonTags = !entity.hasInterestingTags(); + graph.parentRelations(entity).forEach(function (relation) { + var type = relation.tags.type; + + if (type === 'multipolygon' && shouldCopyMultipolygonTags || type === 'boundary') { + tags = Object.assign({}, relation.tags, tags); + } + }); + return tags; + }; + } + function svgSegmentWay(way, graph, activeID) { + // When there is no activeID, we can memoize this expensive computation + if (activeID === undefined) { + return graph["transient"](way, 'waySegments', getWaySegments); + } else { + return getWaySegments(); + } + + function getWaySegments() { + var isActiveWay = way.nodes.indexOf(activeID) !== -1; + var features = { + passive: [], + active: [] + }; + var start = {}; + var end = {}; + var node, type; + + for (var i = 0; i < way.nodes.length; i++) { + node = graph.entity(way.nodes[i]); + type = svgPassiveVertex(node, graph, activeID); + end = { + node: node, + type: type + }; + + if (start.type !== undefined) { + if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) { + // one adjacent vertex + pushActive(start, end, i); + } else if (start.type === 0 && end.type === 0) { + // both active vertices + pushActive(start, end, i); + } else { + pushPassive(start, end, i); + } + } + + start = end; + } + + return features; + + function pushActive(start, end, index) { + features.active.push({ + type: 'Feature', + id: way.id + '-' + index + '-nope', + properties: { + nope: true, + target: true, + entity: way, + nodes: [start.node, end.node], + index: index + }, + geometry: { + type: 'LineString', + coordinates: [start.node.loc, end.node.loc] + } + }); + } + + function pushPassive(start, end, index) { + features.passive.push({ + type: 'Feature', + id: way.id + '-' + index, + properties: { + target: true, + entity: way, + nodes: [start.node, end.node], + index: index + }, + geometry: { + type: 'LineString', + coordinates: [start.node.loc, end.node.loc] + } + }); + } + } + } + + function svgTagClasses() { + var primaries = ['building', 'highway', 'railway', 'waterway', 'aeroway', 'aerialway', 'piste:type', 'boundary', 'power', 'amenity', 'natural', 'landuse', 'leisure', 'military', 'place', 'man_made', 'route', 'attraction', 'building:part', 'indoor']; + var statuses = [// nonexistent, might be built + 'proposed', 'planned', // under maintentance or between groundbreaking and opening + 'construction', // existent but not functional + 'disused', // dilapidated to nonexistent + 'abandoned', // nonexistent, still may appear in imagery + 'dismantled', 'razed', 'demolished', 'obliterated', // existent occasionally, e.g. stormwater drainage basin + 'intermittent']; + var secondaries = ['oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier', 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport', 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure', 'man_made', 'indoor']; + + var _tags = function _tags(entity) { + return entity.tags; + }; + + var tagClasses = function tagClasses(selection) { + selection.each(function tagClassesEach(entity) { + var value = this.className; + + if (value.baseVal !== undefined) { + value = value.baseVal; + } + + var t = _tags(entity); + + var computed = tagClasses.getClassesString(t, value); + + if (computed !== value) { + select(this).attr('class', computed); + } + }); + }; + + tagClasses.getClassesString = function (t, value) { + var primary, status; + var i, j, k, v; // in some situations we want to render perimeter strokes a certain way + + var overrideGeometry; + + if (/\bstroke\b/.test(value)) { + if (!!t.barrier && t.barrier !== 'no') { + overrideGeometry = 'line'; + } + } // preserve base classes (nothing with `tag-`) + + + var classes = value.trim().split(/\s+/).filter(function (klass) { + return klass.length && !/^tag-/.test(klass); + }).map(function (klass) { + // special overrides for some perimeter strokes + return klass === 'line' || klass === 'area' ? overrideGeometry || klass : klass; + }); // pick at most one primary classification tag.. + + for (i = 0; i < primaries.length; i++) { + k = primaries[i]; + v = t[k]; + if (!v || v === 'no') continue; + + if (k === 'piste:type') { + // avoid a ':' in the class name + k = 'piste'; + } else if (k === 'building:part') { + // avoid a ':' in the class name + k = 'building_part'; + } + + primary = k; + + if (statuses.indexOf(v) !== -1) { + // e.g. `railway=abandoned` + status = v; + classes.push('tag-' + k); + } else { + classes.push('tag-' + k); + classes.push('tag-' + k + '-' + v); + } + + break; + } + + if (!primary) { + for (i = 0; i < statuses.length; i++) { + for (j = 0; j < primaries.length; j++) { + k = statuses[i] + ':' + primaries[j]; // e.g. `demolished:building=yes` + + v = t[k]; + if (!v || v === 'no') continue; + status = statuses[i]; + break; + } + } + } // add at most one status tag, only if relates to primary tag.. + + + if (!status) { + for (i = 0; i < statuses.length; i++) { + k = statuses[i]; + v = t[k]; + if (!v || v === 'no') continue; + + if (v === 'yes') { + // e.g. `railway=rail + abandoned=yes` + status = k; + } else if (primary && primary === v) { + // e.g. `railway=rail + abandoned=railway` + status = k; + } else if (!primary && primaries.indexOf(v) !== -1) { + // e.g. `abandoned=railway` + status = k; + primary = v; + classes.push('tag-' + v); + } // else ignore e.g. `highway=path + abandoned=railway` + + + if (status) break; + } + } + + if (status) { + classes.push('tag-status'); + classes.push('tag-status-' + status); + } // add any secondary tags + + + for (i = 0; i < secondaries.length; i++) { + k = secondaries[i]; + v = t[k]; + if (!v || v === 'no' || k === primary) continue; + classes.push('tag-' + k); + classes.push('tag-' + k + '-' + v); + } // For highways, look for surface tagging.. + + + if (primary === 'highway' && !osmPathHighwayTagValues[t.highway] || primary === 'aeroway') { + var surface = t.highway === 'track' ? 'unpaved' : 'paved'; + + for (k in t) { + v = t[k]; + + if (k in osmPavedTags) { + surface = osmPavedTags[k][v] ? 'paved' : 'unpaved'; + } + + if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) { + surface = 'semipaved'; + } + } + + classes.push('tag-' + surface); + } // If this is a wikidata-tagged item, add a class for that.. + + + var qid = t.wikidata || t['flag:wikidata'] || t['brand:wikidata'] || t['network:wikidata'] || t['operator:wikidata']; + + if (qid) { + classes.push('tag-wikidata'); + } + + return classes.join(' ').trim(); + }; + + tagClasses.tags = function (val) { + if (!arguments.length) return _tags; + _tags = val; + return tagClasses; + }; + + return tagClasses; + } + + // Patterns only work in Firefox when set directly on element. + // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632) + var patterns = { + // tag - pattern name + // -or- + // tag - value - pattern name + // -or- + // tag - value - rules (optional tag-values, pattern name) + // (matches earlier rules first, so fallback should be last entry) + amenity: { + grave_yard: 'cemetery', + fountain: 'water_standing' + }, + landuse: { + cemetery: [{ + religion: 'christian', + pattern: 'cemetery_christian' + }, { + religion: 'buddhist', + pattern: 'cemetery_buddhist' + }, { + religion: 'muslim', + pattern: 'cemetery_muslim' + }, { + religion: 'jewish', + pattern: 'cemetery_jewish' + }, { + pattern: 'cemetery' + }], + construction: 'construction', + farmland: 'farmland', + farmyard: 'farmyard', + forest: [{ + leaf_type: 'broadleaved', + pattern: 'forest_broadleaved' + }, { + leaf_type: 'needleleaved', + pattern: 'forest_needleleaved' + }, { + leaf_type: 'leafless', + pattern: 'forest_leafless' + }, { + pattern: 'forest' + } // same as 'leaf_type:mixed' + ], + grave_yard: 'cemetery', + grass: [{ + golf: 'green', + pattern: 'golf_green' + }, { + pattern: 'grass' + }], + landfill: 'landfill', + meadow: 'meadow', + military: 'construction', + orchard: 'orchard', + quarry: 'quarry', + vineyard: 'vineyard' + }, + natural: { + beach: 'beach', + grassland: 'grass', + sand: 'beach', + scrub: 'scrub', + water: [{ + water: 'pond', + pattern: 'pond' + }, { + water: 'reservoir', + pattern: 'water_standing' + }, { + pattern: 'waves' + }], + wetland: [{ + wetland: 'marsh', + pattern: 'wetland_marsh' + }, { + wetland: 'swamp', + pattern: 'wetland_swamp' + }, { + wetland: 'bog', + pattern: 'wetland_bog' + }, { + wetland: 'reedbed', + pattern: 'wetland_reedbed' + }, { + pattern: 'wetland' + }], + wood: [{ + leaf_type: 'broadleaved', + pattern: 'forest_broadleaved' + }, { + leaf_type: 'needleleaved', + pattern: 'forest_needleleaved' + }, { + leaf_type: 'leafless', + pattern: 'forest_leafless' + }, { + pattern: 'forest' + } // same as 'leaf_type:mixed' + ] + }, + traffic_calming: { + island: [{ + surface: 'grass', + pattern: 'grass' + }], + chicane: [{ + surface: 'grass', + pattern: 'grass' + }], + choker: [{ + surface: 'grass', + pattern: 'grass' + }] + } + }; + function svgTagPattern(tags) { + // Skip pattern filling if this is a building (buildings don't get patterns applied) + if (tags.building && tags.building !== 'no') { + return null; + } + + for (var tag in patterns) { + var entityValue = tags[tag]; + if (!entityValue) continue; + + if (typeof patterns[tag] === 'string') { + // extra short syntax (just tag) - pattern name + return 'pattern-' + patterns[tag]; + } else { + var values = patterns[tag]; + + for (var value in values) { + if (entityValue !== value) continue; + var rules = values[value]; + + if (typeof rules === 'string') { + // short syntax - pattern name + return 'pattern-' + rules; + } // long syntax - rule array + + + for (var ruleKey in rules) { + var rule = rules[ruleKey]; + var pass = true; + + for (var criterion in rule) { + if (criterion !== 'pattern') { + // reserved for pattern name + // The only rule is a required tag-value pair + var v = tags[criterion]; + + if (!v || v !== rule[criterion]) { + pass = false; + break; + } + } + } + + if (pass) { + return 'pattern-' + rule.pattern; + } + } + } + } + } + + return null; + } + + function svgAreas(projection, context) { + function getPatternStyle(tags) { + var imageID = svgTagPattern(tags); + + if (imageID) { + return 'url("#ideditor-' + imageID + '")'; + } + + return ''; + } + + function drawTargets(selection, graph, entities, filter) { + var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor '; + var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor '; + var getPath = svgPath(projection).geojson; + var activeID = context.activeID(); + var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways + + var data = { + targets: [], + nopes: [] + }; + entities.forEach(function (way) { + var features = svgSegmentWay(way, graph, activeID); + data.targets.push.apply(data.targets, features.passive); + data.nopes.push.apply(data.nopes, features.active); + }); // Targets allow hover and vertex snapping + + var targetData = data.targets.filter(getPath); + var targets = selection.selectAll('.area.target-allowed').filter(function (d) { + return filter(d.properties.entity); + }).data(targetData, function key(d) { + return d.id; + }); // exit + + targets.exit().remove(); + + var segmentWasEdited = function segmentWasEdited(d) { + var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes + + if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) { + return false; + } + + return d.properties.nodes.some(function (n) { + return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc); + }); + }; // enter/update + + + targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) { + return 'way area target target-allowed ' + targetClass + d.id; + }).classed('segment-edited', segmentWasEdited); // NOPE + + var nopeData = data.nopes.filter(getPath); + var nopes = selection.selectAll('.area.target-nope').filter(function (d) { + return filter(d.properties.entity); + }).data(nopeData, function key(d) { + return d.id; + }); // exit + + nopes.exit().remove(); // enter/update + + nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) { + return 'way area target target-nope ' + nopeClass + d.id; + }).classed('segment-edited', segmentWasEdited); + } + + function drawAreas(selection, graph, entities, filter) { + var path = svgPath(projection, graph, true); + var areas = {}; + var multipolygon; + var base = context.history().base(); + + for (var i = 0; i < entities.length; i++) { + var entity = entities[i]; + if (entity.geometry(graph) !== 'area') continue; + multipolygon = osmIsOldMultipolygonOuterMember(entity, graph); + + if (multipolygon) { + areas[multipolygon.id] = { + entity: multipolygon.mergeTags(entity.tags), + area: Math.abs(entity.area(graph)) + }; + } else if (!areas[entity.id]) { + areas[entity.id] = { + entity: entity, + area: Math.abs(entity.area(graph)) + }; + } + } + + var fills = Object.values(areas).filter(function hasPath(a) { + return path(a.entity); + }); + fills.sort(function areaSort(a, b) { + return b.area - a.area; + }); + fills = fills.map(function (a) { + return a.entity; + }); + var strokes = fills.filter(function (area) { + return area.type === 'way'; + }); + var data = { + clip: fills, + shadow: strokes, + stroke: strokes, + fill: fills + }; + var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm').filter(filter).data(data.clip, osmEntity.key); + clipPaths.exit().remove(); + var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-osm').attr('id', function (entity) { + return 'ideditor-' + entity.id + '-clippath'; + }); + clipPathsEnter.append('path'); + clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', path); + var drawLayer = selection.selectAll('.layer-osm.areas'); + var touchLayer = selection.selectAll('.layer-touch.areas'); // Draw areas.. + + var areagroup = drawLayer.selectAll('g.areagroup').data(['fill', 'shadow', 'stroke']); + areagroup = areagroup.enter().append('g').attr('class', function (d) { + return 'areagroup area-' + d; + }).merge(areagroup); + var paths = areagroup.selectAll('path').filter(filter).data(function (layer) { + return data[layer]; + }, osmEntity.key); + paths.exit().remove(); + var fillpaths = selection.selectAll('.area-fill path.area').nodes(); + var bisect = d3_bisector(function (node) { + return -node.__data__.area(graph); + }).left; + + function sortedByArea(entity) { + if (this._parent.__data__ === 'fill') { + return fillpaths[bisect(fillpaths, -entity.area(graph))]; + } + } + + paths = paths.enter().insert('path', sortedByArea).merge(paths).each(function (entity) { + var layer = this.parentNode.__data__; + this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id); + + if (layer === 'fill') { + this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)'); + this.style.fill = this.style.stroke = getPatternStyle(entity.tags); + } + }).classed('added', function (d) { + return !base.entities[d.id]; + }).classed('geometry-edited', function (d) { + return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes); + }).classed('retagged', function (d) { + return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags); + }).call(svgTagClasses()).attr('d', path); // Draw touch targets.. + + touchLayer.call(drawTargets, graph, data.stroke, filter); + } + + return drawAreas; + } + + var fastJsonStableStringify = function fastJsonStableStringify(data, opts) { + if (!opts) opts = {}; + if (typeof opts === 'function') opts = { + cmp: opts + }; + var cycles = typeof opts.cycles === 'boolean' ? opts.cycles : false; + + var cmp = opts.cmp && function (f) { + return function (node) { + return function (a, b) { + var aobj = { + key: a, + value: node[a] + }; + var bobj = { + key: b, + value: node[b] + }; + return f(aobj, bobj); + }; + }; + }(opts.cmp); + + var seen = []; + return function stringify(node) { + if (node && node.toJSON && typeof node.toJSON === 'function') { + node = node.toJSON(); + } + + if (node === undefined) return; + if (typeof node == 'number') return isFinite(node) ? '' + node : 'null'; + if (_typeof(node) !== 'object') return JSON.stringify(node); + var i, out; + + if (Array.isArray(node)) { + out = '['; + + for (i = 0; i < node.length; i++) { + if (i) out += ','; + out += stringify(node[i]) || 'null'; + } + + return out + ']'; + } + + if (node === null) return 'null'; + + if (seen.indexOf(node) !== -1) { + if (cycles) return JSON.stringify('__cycle__'); + throw new TypeError('Converting circular structure to JSON'); + } + + var seenIndex = seen.push(node) - 1; + var keys = Object.keys(node).sort(cmp && cmp(node)); + out = ''; + + for (i = 0; i < keys.length; i++) { + var key = keys[i]; + var value = stringify(node[key]); + if (!value) continue; + if (out) out += ','; + out += JSON.stringify(key) + ':' + value; + } + + seen.splice(seenIndex, 1); + return '{' + out + '}'; + }(data); + }; + + var $$2 = _export; + var $entries = objectToArray.entries; + + // `Object.entries` method + // https://tc39.es/ecma262/#sec-object.entries + $$2({ target: 'Object', stat: true }, { + entries: function entries(O) { + return $entries(O); + } + }); + + var _marked = /*#__PURE__*/regeneratorRuntime.mark(gpxGen), + _marked3 = /*#__PURE__*/regeneratorRuntime.mark(kmlGen); + + // cast array x into numbers + // get the content of a text node, if any + function nodeVal(x) { + if (x && x.normalize) { + x.normalize(); + } + + return x && x.textContent || ""; + } // one Y child of X, if any, otherwise null + + + function get1(x, y) { + var n = x.getElementsByTagName(y); + return n.length ? n[0] : null; + } + + function getLineStyle(extensions) { + var style = {}; + + if (extensions) { + var lineStyle = get1(extensions, "line"); + + if (lineStyle) { + var color = nodeVal(get1(lineStyle, "color")), + opacity = parseFloat(nodeVal(get1(lineStyle, "opacity"))), + width = parseFloat(nodeVal(get1(lineStyle, "width"))); + if (color) style.stroke = color; + if (!isNaN(opacity)) style["stroke-opacity"] = opacity; // GPX width is in mm, convert to px with 96 px per inch + + if (!isNaN(width)) style["stroke-width"] = width * 96 / 25.4; + } + } + + return style; + } // get the contents of multiple text nodes, if present + + + function getMulti(x, ys) { + var o = {}; + var n; + var k; + + for (k = 0; k < ys.length; k++) { + n = get1(x, ys[k]); + if (n) o[ys[k]] = nodeVal(n); + } + + return o; + } + + function getProperties$1(node) { + var prop = getMulti(node, ["name", "cmt", "desc", "type", "time", "keywords"]); // Parse additional data from our Garmin extension(s) + + var extensions = node.getElementsByTagNameNS("http://www.garmin.com/xmlschemas/GpxExtensions/v3", "*"); + + for (var i = 0; i < extensions.length; i++) { + var extension = extensions[i]; // Ignore nested extensions, like those on routepoints or trackpoints + + if (extension.parentNode.parentNode === node) { + prop[extension.tagName.replace(":", "_")] = nodeVal(extension); + } + } + + var links = node.getElementsByTagName("link"); + if (links.length) prop.links = []; + + for (var _i = 0; _i < links.length; _i++) { + prop.links.push(Object.assign({ + href: links[_i].getAttribute("href") + }, getMulti(links[_i], ["text", "type"]))); + } + + return prop; + } + + function coordPair$1(x) { + var ll = [parseFloat(x.getAttribute("lon")), parseFloat(x.getAttribute("lat"))]; + var ele = get1(x, "ele"); // handle namespaced attribute in browser + + var heart = get1(x, "gpxtpx:hr") || get1(x, "hr"); + var time = get1(x, "time"); + var e; + + if (ele) { + e = parseFloat(nodeVal(ele)); + + if (!isNaN(e)) { + ll.push(e); + } + } + + var result = { + coordinates: ll, + time: time ? nodeVal(time) : null, + extendedValues: [] + }; + + if (heart) { + result.extendedValues.push(["heart", parseFloat(nodeVal(heart))]); + } + + var extensions = get1(x, "extensions"); + + if (extensions !== null) { + for (var _i2 = 0, _arr = ["speed", "course", "hAcc", "vAcc"]; _i2 < _arr.length; _i2++) { + var name = _arr[_i2]; + var v = parseFloat(nodeVal(get1(extensions, name))); + + if (!isNaN(v)) { + result.extendedValues.push([name, v]); + } + } + } + + return result; + } + + function getRoute(node) { + var line = getPoints$1(node, "rtept"); + if (!line) return; + return { + type: "Feature", + properties: Object.assign(getProperties$1(node), getLineStyle(get1(node, "extensions")), { + _gpxType: "rte" + }), + geometry: { + type: "LineString", + coordinates: line.line + } + }; + } + + function getPoints$1(node, pointname) { + var pts = node.getElementsByTagName(pointname); + if (pts.length < 2) return; // Invalid line in GeoJSON + + var line = []; + var times = []; + var extendedValues = {}; + + for (var i = 0; i < pts.length; i++) { + var c = coordPair$1(pts[i]); + line.push(c.coordinates); + if (c.time) times.push(c.time); + + for (var j = 0; j < c.extendedValues.length; j++) { + var _c$extendedValues$j = _slicedToArray(c.extendedValues[j], 2), + name = _c$extendedValues$j[0], + val = _c$extendedValues$j[1]; + + var plural = name === "heart" ? name : name + "s"; + + if (!extendedValues[plural]) { + extendedValues[plural] = Array(pts.length).fill(null); + } + + extendedValues[plural][i] = val; + } + } + + return { + line: line, + times: times, + extendedValues: extendedValues + }; + } + + function getTrack(node) { + var segments = node.getElementsByTagName("trkseg"); + var track = []; + var times = []; + var extractedLines = []; + + for (var i = 0; i < segments.length; i++) { + var line = getPoints$1(segments[i], "trkpt"); + + if (line) { + extractedLines.push(line); + if (line.times && line.times.length) times.push(line.times); + } + } + + if (extractedLines.length === 0) return; + var multi = extractedLines.length > 1; + var properties = Object.assign(getProperties$1(node), getLineStyle(get1(node, "extensions")), { + _gpxType: "trk" + }, times.length ? { + coordinateProperties: { + times: multi ? times : times[0] + } + } : {}); + + for (var _i3 = 0; _i3 < extractedLines.length; _i3++) { + var _line = extractedLines[_i3]; + track.push(_line.line); + + for (var _i4 = 0, _Object$entries = Object.entries(_line.extendedValues); _i4 < _Object$entries.length; _i4++) { + var _Object$entries$_i = _slicedToArray(_Object$entries[_i4], 2), + name = _Object$entries$_i[0], + val = _Object$entries$_i[1]; + + var props = properties; + + if (name === "heart") { + if (!properties.coordinateProperties) { + properties.coordinateProperties = {}; + } + + props = properties.coordinateProperties; + } + + if (multi) { + if (!props[name]) props[name] = extractedLines.map(function (line) { + return new Array(line.line.length).fill(null); + }); + props[name][_i3] = val; + } else { + props[name] = val; + } + } + } + + return { + type: "Feature", + properties: properties, + geometry: multi ? { + type: "MultiLineString", + coordinates: track + } : { + type: "LineString", + coordinates: track[0] + } + }; + } + + function getPoint(node) { + return { + type: "Feature", + properties: Object.assign(getProperties$1(node), getMulti(node, ["sym"])), + geometry: { + type: "Point", + coordinates: coordPair$1(node).coordinates + } + }; + } + + function gpxGen(doc) { + var tracks, routes, waypoints, i, feature, _i5, _feature, _i6; + + return regeneratorRuntime.wrap(function gpxGen$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + tracks = doc.getElementsByTagName("trk"); + routes = doc.getElementsByTagName("rte"); + waypoints = doc.getElementsByTagName("wpt"); + i = 0; + + case 4: + if (!(i < tracks.length)) { + _context.next = 12; + break; + } + + feature = getTrack(tracks[i]); + + if (!feature) { + _context.next = 9; + break; + } + + _context.next = 9; + return feature; + + case 9: + i++; + _context.next = 4; + break; + + case 12: + _i5 = 0; + + case 13: + if (!(_i5 < routes.length)) { + _context.next = 21; + break; + } + + _feature = getRoute(routes[_i5]); + + if (!_feature) { + _context.next = 18; + break; + } + + _context.next = 18; + return _feature; + + case 18: + _i5++; + _context.next = 13; + break; + + case 21: + _i6 = 0; + + case 22: + if (!(_i6 < waypoints.length)) { + _context.next = 28; + break; + } + + _context.next = 25; + return getPoint(waypoints[_i6]); + + case 25: + _i6++; + _context.next = 22; + break; + + case 28: + case "end": + return _context.stop(); + } + } + }, _marked); + } + + function gpx(doc) { + return { + type: "FeatureCollection", + features: Array.from(gpxGen(doc)) + }; + } + + var removeSpace = /\s*/g; + var trimSpace = /^\s*|\s*$/g; + var splitSpace = /\s+/; // generate a short, numeric hash of a string + + function okhash(x) { + if (!x || !x.length) return 0; + var h = 0; + + for (var i = 0; i < x.length; i++) { + h = (h << 5) - h + x.charCodeAt(i) | 0; + } + + return h; + } // get one coordinate from a coordinate array, if any + + + function coord1(v) { + return v.replace(removeSpace, "").split(",").map(parseFloat); + } // get all coordinates from a coordinate array as [[],[]] + + + function coord(v) { + return v.replace(trimSpace, "").split(splitSpace).map(coord1); + } + + function xml2str(node) { + if (node.xml !== undefined) return node.xml; + + if (node.tagName) { + var output = node.tagName; + + for (var i = 0; i < node.attributes.length; i++) { + output += node.attributes[i].name + node.attributes[i].value; + } + + for (var _i9 = 0; _i9 < node.childNodes.length; _i9++) { + output += xml2str(node.childNodes[_i9]); + } + + return output; + } + + if (node.nodeName === "#text") { + return (node.nodeValue || node.value || "").trim(); + } + + if (node.nodeName === "#cdata-section") { + return node.nodeValue; + } + + return ""; + } + + var geotypes = ["Polygon", "LineString", "Point", "Track", "gx:Track"]; + + function kmlColor(properties, elem, prefix) { + var v = nodeVal(get1(elem, "color")) || ""; + var colorProp = prefix == "stroke" || prefix === "fill" ? prefix : prefix + "-color"; + + if (v.substr(0, 1) === "#") { + v = v.substr(1); + } + + if (v.length === 6 || v.length === 3) { + properties[colorProp] = v; + } else if (v.length === 8) { + properties[prefix + "-opacity"] = parseInt(v.substr(0, 2), 16) / 255; + properties[colorProp] = "#" + v.substr(6, 2) + v.substr(4, 2) + v.substr(2, 2); + } + } + + function numericProperty(properties, elem, source, target) { + var val = parseFloat(nodeVal(get1(elem, source))); + if (!isNaN(val)) properties[target] = val; + } + + function gxCoords(root) { + var elems = root.getElementsByTagName("coord"); + var coords = []; + var times = []; + if (elems.length === 0) elems = root.getElementsByTagName("gx:coord"); + + for (var i = 0; i < elems.length; i++) { + coords.push(nodeVal(elems[i]).split(" ").map(parseFloat)); + } + + var timeElems = root.getElementsByTagName("when"); + + for (var j = 0; j < timeElems.length; j++) { + times.push(nodeVal(timeElems[j])); + } + + return { + coords: coords, + times: times + }; + } + + function getGeometry(root) { + var geomNode; + var geomNodes; + var i; + var j; + var k; + var geoms = []; + var coordTimes = []; + + if (get1(root, "MultiGeometry")) { + return getGeometry(get1(root, "MultiGeometry")); + } + + if (get1(root, "MultiTrack")) { + return getGeometry(get1(root, "MultiTrack")); + } + + if (get1(root, "gx:MultiTrack")) { + return getGeometry(get1(root, "gx:MultiTrack")); + } + + for (i = 0; i < geotypes.length; i++) { + geomNodes = root.getElementsByTagName(geotypes[i]); + + if (geomNodes) { + for (j = 0; j < geomNodes.length; j++) { + geomNode = geomNodes[j]; + + if (geotypes[i] === "Point") { + geoms.push({ + type: "Point", + coordinates: coord1(nodeVal(get1(geomNode, "coordinates"))) + }); + } else if (geotypes[i] === "LineString") { + geoms.push({ + type: "LineString", + coordinates: coord(nodeVal(get1(geomNode, "coordinates"))) + }); + } else if (geotypes[i] === "Polygon") { + var rings = geomNode.getElementsByTagName("LinearRing"), + coords = []; + + for (k = 0; k < rings.length; k++) { + coords.push(coord(nodeVal(get1(rings[k], "coordinates")))); + } + + geoms.push({ + type: "Polygon", + coordinates: coords + }); + } else if (geotypes[i] === "Track" || geotypes[i] === "gx:Track") { + var track = gxCoords(geomNode); + geoms.push({ + type: "LineString", + coordinates: track.coords + }); + if (track.times.length) coordTimes.push(track.times); + } + } + } + } + + return { + geoms: geoms, + coordTimes: coordTimes + }; + } + + function getPlacemark(root, styleIndex, styleMapIndex, styleByHash) { + var geomsAndTimes = getGeometry(root); + var i; + var properties = {}; + var name = nodeVal(get1(root, "name")); + var address = nodeVal(get1(root, "address")); + var styleUrl = nodeVal(get1(root, "styleUrl")); + var description = nodeVal(get1(root, "description")); + var timeSpan = get1(root, "TimeSpan"); + var timeStamp = get1(root, "TimeStamp"); + var extendedData = get1(root, "ExtendedData"); + var iconStyle = get1(root, "IconStyle"); + var labelStyle = get1(root, "LabelStyle"); + var lineStyle = get1(root, "LineStyle"); + var polyStyle = get1(root, "PolyStyle"); + var visibility = get1(root, "visibility"); + if (name) properties.name = name; + if (address) properties.address = address; + + if (styleUrl) { + if (styleUrl[0] !== "#") { + styleUrl = "#" + styleUrl; + } + + properties.styleUrl = styleUrl; + + if (styleIndex[styleUrl]) { + properties.styleHash = styleIndex[styleUrl]; + } + + if (styleMapIndex[styleUrl]) { + properties.styleMapHash = styleMapIndex[styleUrl]; + properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal]; + } // Try to populate the lineStyle or polyStyle since we got the style hash + + + var style = styleByHash[properties.styleHash]; + + if (style) { + if (!iconStyle) iconStyle = get1(style, "IconStyle"); + if (!labelStyle) labelStyle = get1(style, "LabelStyle"); + if (!lineStyle) lineStyle = get1(style, "LineStyle"); + if (!polyStyle) polyStyle = get1(style, "PolyStyle"); + } + } + + if (description) properties.description = description; + + if (timeSpan) { + var begin = nodeVal(get1(timeSpan, "begin")); + var end = nodeVal(get1(timeSpan, "end")); + properties.timespan = { + begin: begin, + end: end + }; + } + + if (timeStamp) { + properties.timestamp = nodeVal(get1(timeStamp, "when")); + } + + if (iconStyle) { + kmlColor(properties, iconStyle, "icon"); + numericProperty(properties, iconStyle, "scale", "icon-scale"); + numericProperty(properties, iconStyle, "heading", "icon-heading"); + var hotspot = get1(iconStyle, "hotSpot"); + + if (hotspot) { + var left = parseFloat(hotspot.getAttribute("x")); + var top = parseFloat(hotspot.getAttribute("y")); + if (!isNaN(left) && !isNaN(top)) properties["icon-offset"] = [left, top]; + } + + var icon = get1(iconStyle, "Icon"); + + if (icon) { + var href = nodeVal(get1(icon, "href")); + if (href) properties.icon = href; + } + } + + if (labelStyle) { + kmlColor(properties, labelStyle, "label"); + numericProperty(properties, labelStyle, "scale", "label-scale"); + } + + if (lineStyle) { + kmlColor(properties, lineStyle, "stroke"); + numericProperty(properties, lineStyle, "width", "stroke-width"); + } + + if (polyStyle) { + kmlColor(properties, polyStyle, "fill"); + var fill = nodeVal(get1(polyStyle, "fill")); + var outline = nodeVal(get1(polyStyle, "outline")); + if (fill) properties["fill-opacity"] = fill === "1" ? properties["fill-opacity"] || 1 : 0; + if (outline) properties["stroke-opacity"] = outline === "1" ? properties["stroke-opacity"] || 1 : 0; + } + + if (extendedData) { + var datas = extendedData.getElementsByTagName("Data"), + simpleDatas = extendedData.getElementsByTagName("SimpleData"); + + for (i = 0; i < datas.length; i++) { + properties[datas[i].getAttribute("name")] = nodeVal(get1(datas[i], "value")); + } + + for (i = 0; i < simpleDatas.length; i++) { + properties[simpleDatas[i].getAttribute("name")] = nodeVal(simpleDatas[i]); + } + } + + if (visibility) { + properties.visibility = nodeVal(visibility); + } + + if (geomsAndTimes.coordTimes.length) { + properties.coordinateProperties = { + times: geomsAndTimes.coordTimes.length === 1 ? geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes + }; + } + + var feature = { + type: "Feature", + geometry: geomsAndTimes.geoms.length === 0 ? null : geomsAndTimes.geoms.length === 1 ? geomsAndTimes.geoms[0] : { + type: "GeometryCollection", + geometries: geomsAndTimes.geoms + }, + properties: properties + }; + if (root.getAttribute("id")) feature.id = root.getAttribute("id"); + return feature; + } + + function kmlGen(doc) { + var styleIndex, styleByHash, styleMapIndex, placemarks, styles, styleMaps, k, hash, l, pairs, pairsMap, m, j, feature; + return regeneratorRuntime.wrap(function kmlGen$(_context3) { + while (1) { + switch (_context3.prev = _context3.next) { + case 0: + // styleindex keeps track of hashed styles in order to match feature + styleIndex = {}; + styleByHash = {}; // stylemapindex keeps track of style maps to expose in properties + + styleMapIndex = {}; // atomic geospatial types supported by KML - MultiGeometry is + // handled separately + // all root placemarks in the file + + placemarks = doc.getElementsByTagName("Placemark"); + styles = doc.getElementsByTagName("Style"); + styleMaps = doc.getElementsByTagName("StyleMap"); + + for (k = 0; k < styles.length; k++) { + hash = okhash(xml2str(styles[k])).toString(16); + styleIndex["#" + styles[k].getAttribute("id")] = hash; + styleByHash[hash] = styles[k]; + } + + for (l = 0; l < styleMaps.length; l++) { + styleIndex["#" + styleMaps[l].getAttribute("id")] = okhash(xml2str(styleMaps[l])).toString(16); + pairs = styleMaps[l].getElementsByTagName("Pair"); + pairsMap = {}; + + for (m = 0; m < pairs.length; m++) { + pairsMap[nodeVal(get1(pairs[m], "key"))] = nodeVal(get1(pairs[m], "styleUrl")); + } + + styleMapIndex["#" + styleMaps[l].getAttribute("id")] = pairsMap; + } + + j = 0; + + case 9: + if (!(j < placemarks.length)) { + _context3.next = 17; + break; + } + + feature = getPlacemark(placemarks[j], styleIndex, styleMapIndex, styleByHash); + + if (!feature) { + _context3.next = 14; + break; + } + + _context3.next = 14; + return feature; + + case 14: + j++; + _context3.next = 9; + break; + + case 17: + case "end": + return _context3.stop(); + } + } + }, _marked3); + } + + function kml(doc) { + return { + type: "FeatureCollection", + features: Array.from(kmlGen(doc)) + }; + } + + var _initialized = false; + var _enabled = false; + + var _geojson; + + function svgData(projection, context, dispatch) { + var throttledRedraw = throttle(function () { + dispatch.call('change'); + }, 1000); + + var _showLabels = true; + var detected = utilDetect(); + var layer = select(null); + + var _vtService; + + var _fileList; + + var _template; + + var _src; + + function init() { + if (_initialized) return; // run once + + _geojson = {}; + _enabled = true; + + function over(d3_event) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + d3_event.dataTransfer.dropEffect = 'copy'; + } + + context.container().attr('dropzone', 'copy').on('drop.svgData', function (d3_event) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + if (!detected.filedrop) return; + drawData.fileList(d3_event.dataTransfer.files); + }).on('dragenter.svgData', over).on('dragexit.svgData', over).on('dragover.svgData', over); + _initialized = true; + } + + function getService() { + if (services.vectorTile && !_vtService) { + _vtService = services.vectorTile; + + _vtService.event.on('loadedData', throttledRedraw); + } else if (!services.vectorTile && _vtService) { + _vtService = null; + } + + return _vtService; + } + + function showLayer() { + layerOn(); + layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () { + dispatch.call('change'); + }); + } + + function hideLayer() { + throttledRedraw.cancel(); + layer.transition().duration(250).style('opacity', 0).on('end', layerOff); + } + + function layerOn() { + layer.style('display', 'block'); + } + + function layerOff() { + layer.selectAll('.viewfield-group').remove(); + layer.style('display', 'none'); + } // ensure that all geojson features in a collection have IDs + + + function ensureIDs(gj) { + if (!gj) return null; + + if (gj.type === 'FeatureCollection') { + for (var i = 0; i < gj.features.length; i++) { + ensureFeatureID(gj.features[i]); + } + } else { + ensureFeatureID(gj); + } + + return gj; + } // ensure that each single Feature object has a unique ID + + + function ensureFeatureID(feature) { + if (!feature) return; + feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature)); + return feature; + } // Prefer an array of Features instead of a FeatureCollection + + + function getFeatures(gj) { + if (!gj) return []; + + if (gj.type === 'FeatureCollection') { + return gj.features; + } else { + return [gj]; + } + } + + function featureKey(d) { + return d.__featurehash__; + } + + function isPolygon(d) { + return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon'; + } + + function clipPathID(d) { + return 'ideditor-data-' + d.__featurehash__ + '-clippath'; + } + + function featureClasses(d) { + return ['data' + d.__featurehash__, d.geometry.type, isPolygon(d) ? 'area' : '', d.__layerID__ || ''].filter(Boolean).join(' '); + } + + function drawData(selection) { + var vtService = getService(); + var getPath = svgPath(projection).geojson; + var getAreaPath = svgPath(projection, null, true).geojson; + var hasData = drawData.hasData(); + layer = selection.selectAll('.layer-mapdata').data(_enabled && hasData ? [0] : []); + layer.exit().remove(); + layer = layer.enter().append('g').attr('class', 'layer-mapdata').merge(layer); + var surface = context.surface(); + if (!surface || surface.empty()) return; // not ready to draw yet, starting up + // Gather data + + var geoData, polygonData; + + if (_template && vtService) { + // fetch data from vector tile service + var sourceID = _template; + vtService.loadTiles(sourceID, _template, projection); + geoData = vtService.data(sourceID, projection); + } else { + geoData = getFeatures(_geojson); + } + + geoData = geoData.filter(getPath); + polygonData = geoData.filter(isPolygon); // Draw clip paths for polygons + + var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data').data(polygonData, featureKey); + clipPaths.exit().remove(); + var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-data').attr('id', clipPathID); + clipPathsEnter.append('path'); + clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', getAreaPath); // Draw fill, shadow, stroke layers + + var datagroups = layer.selectAll('g.datagroup').data(['fill', 'shadow', 'stroke']); + datagroups = datagroups.enter().append('g').attr('class', function (d) { + return 'datagroup datagroup-' + d; + }).merge(datagroups); // Draw paths + + var pathData = { + fill: polygonData, + shadow: geoData, + stroke: geoData + }; + var paths = datagroups.selectAll('path').data(function (layer) { + return pathData[layer]; + }, featureKey); // exit + + paths.exit().remove(); // enter/update + + paths = paths.enter().append('path').attr('class', function (d) { + var datagroup = this.parentNode.__data__; + return 'pathdata ' + datagroup + ' ' + featureClasses(d); + }).attr('clip-path', function (d) { + var datagroup = this.parentNode.__data__; + return datagroup === 'fill' ? 'url(#' + clipPathID(d) + ')' : null; + }).merge(paths).attr('d', function (d) { + var datagroup = this.parentNode.__data__; + return datagroup === 'fill' ? getAreaPath(d) : getPath(d); + }); // Draw labels + + layer.call(drawLabels, 'label-halo', geoData).call(drawLabels, 'label', geoData); + + function drawLabels(selection, textClass, data) { + var labelPath = d3_geoPath(projection); + var labelData = data.filter(function (d) { + return _showLabels && d.properties && (d.properties.desc || d.properties.name); + }); + var labels = selection.selectAll('text.' + textClass).data(labelData, featureKey); // exit + + labels.exit().remove(); // enter/update + + labels = labels.enter().append('text').attr('class', function (d) { + return textClass + ' ' + featureClasses(d); + }).merge(labels).text(function (d) { + return d.properties.desc || d.properties.name; + }).attr('x', function (d) { + var centroid = labelPath.centroid(d); + return centroid[0] + 11; + }).attr('y', function (d) { + var centroid = labelPath.centroid(d); + return centroid[1]; + }); + } + } + + function getExtension(fileName) { + if (!fileName) return; + var re = /\.(gpx|kml|(geo)?json)$/i; + var match = fileName.toLowerCase().match(re); + return match && match.length && match[0]; + } + + function xmlToDom(textdata) { + return new DOMParser().parseFromString(textdata, 'text/xml'); + } + + drawData.setFile = function (extension, data) { + _template = null; + _fileList = null; + _geojson = null; + _src = null; + var gj; + + switch (extension) { + case '.gpx': + gj = gpx(xmlToDom(data)); + break; + + case '.kml': + gj = kml(xmlToDom(data)); + break; + + case '.geojson': + case '.json': + gj = JSON.parse(data); + break; + } + + gj = gj || {}; + + if (Object.keys(gj).length) { + _geojson = ensureIDs(gj); + _src = extension + ' data file'; + this.fitZoom(); + } + + dispatch.call('change'); + return this; + }; + + drawData.showLabels = function (val) { + if (!arguments.length) return _showLabels; + _showLabels = val; + return this; + }; + + drawData.enabled = function (val) { + if (!arguments.length) return _enabled; + _enabled = val; + + if (_enabled) { + showLayer(); + } else { + hideLayer(); + } + + dispatch.call('change'); + return this; + }; + + drawData.hasData = function () { + var gj = _geojson || {}; + return !!(_template || Object.keys(gj).length); + }; + + drawData.template = function (val, src) { + if (!arguments.length) return _template; // test source against OSM imagery blocklists.. + + var osm = context.connection(); + + if (osm) { + var blocklists = osm.imageryBlocklists(); + var fail = false; + var tested = 0; + var regex; + + for (var i = 0; i < blocklists.length; i++) { + regex = blocklists[i]; + fail = regex.test(val); + tested++; + if (fail) break; + } // ensure at least one test was run. + + + if (!tested) { + regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/; + fail = regex.test(val); + } + } + + _template = val; + _fileList = null; + _geojson = null; // strip off the querystring/hash from the template, + // it often includes the access token + + _src = src || 'vectortile:' + val.split(/[?#]/)[0]; + dispatch.call('change'); + return this; + }; + + drawData.geojson = function (gj, src) { + if (!arguments.length) return _geojson; + _template = null; + _fileList = null; + _geojson = null; + _src = null; + gj = gj || {}; + + if (Object.keys(gj).length) { + _geojson = ensureIDs(gj); + _src = src || 'unknown.geojson'; + } + + dispatch.call('change'); + return this; + }; + + drawData.fileList = function (fileList) { + if (!arguments.length) return _fileList; + _template = null; + _fileList = fileList; + _geojson = null; + _src = null; + if (!fileList || !fileList.length) return this; + var f = fileList[0]; + var extension = getExtension(f.name); + var reader = new FileReader(); + + reader.onload = function () { + return function (e) { + drawData.setFile(extension, e.target.result); + }; + }(); + + reader.readAsText(f); + return this; + }; + + drawData.url = function (url, defaultExtension) { + _template = null; + _fileList = null; + _geojson = null; + _src = null; // strip off any querystring/hash from the url before checking extension + + var testUrl = url.split(/[?#]/)[0]; + var extension = getExtension(testUrl) || defaultExtension; + + if (extension) { + _template = null; + d3_text(url).then(function (data) { + drawData.setFile(extension, data); + })["catch"](function () { + /* ignore */ + }); + } else { + drawData.template(url); + } + + return this; + }; + + drawData.getSrc = function () { + return _src || ''; + }; + + drawData.fitZoom = function () { + var features = getFeatures(_geojson); + if (!features.length) return; + var map = context.map(); + var viewport = map.trimmedExtent().polygon(); + var coords = features.reduce(function (coords, feature) { + var geom = feature.geometry; + if (!geom) return coords; + var c = geom.coordinates; + /* eslint-disable no-fallthrough */ + + switch (geom.type) { + case 'Point': + c = [c]; + + case 'MultiPoint': + case 'LineString': + break; + + case 'MultiPolygon': + c = utilArrayFlatten(c); + + case 'Polygon': + case 'MultiLineString': + c = utilArrayFlatten(c); + break; + } + /* eslint-enable no-fallthrough */ + + + return utilArrayUnion(coords, c); + }, []); + + if (!geoPolygonIntersectsPolygon(viewport, coords, true)) { + var extent = geoExtent(d3_geoBounds({ + type: 'LineString', + coordinates: coords + })); + map.centerZoom(extent.center(), map.trimmedExtentZoom(extent)); + } + + return this; + }; + + init(); + return drawData; + } + + function svgDebug(projection, context) { + function drawDebug(selection) { + var showTile = context.getDebug('tile'); + var showCollision = context.getDebug('collision'); + var showImagery = context.getDebug('imagery'); + var showTouchTargets = context.getDebug('target'); + var showDownloaded = context.getDebug('downloaded'); + var debugData = []; + + if (showTile) { + debugData.push({ + "class": 'red', + label: 'tile' + }); + } + + if (showCollision) { + debugData.push({ + "class": 'yellow', + label: 'collision' + }); + } + + if (showImagery) { + debugData.push({ + "class": 'orange', + label: 'imagery' + }); + } + + if (showTouchTargets) { + debugData.push({ + "class": 'pink', + label: 'touchTargets' + }); + } + + if (showDownloaded) { + debugData.push({ + "class": 'purple', + label: 'downloaded' + }); + } + + var legend = context.container().select('.main-content').selectAll('.debug-legend').data(debugData.length ? [0] : []); + legend.exit().remove(); + legend = legend.enter().append('div').attr('class', 'fillD debug-legend').merge(legend); + var legendItems = legend.selectAll('.debug-legend-item').data(debugData, function (d) { + return d.label; + }); + legendItems.exit().remove(); + legendItems.enter().append('span').attr('class', function (d) { + return "debug-legend-item ".concat(d["class"]); + }).text(function (d) { + return d.label; + }); + var layer = selection.selectAll('.layer-debug').data(showImagery || showDownloaded ? [0] : []); + layer.exit().remove(); + layer = layer.enter().append('g').attr('class', 'layer-debug').merge(layer); // imagery + + var extent = context.map().extent(); + _mainFileFetcher.get('imagery').then(function (d) { + var hits = showImagery && d.query.bbox(extent.rectangle(), true) || []; + var features = hits.map(function (d) { + return d.features[d.id]; + }); + var imagery = layer.selectAll('path.debug-imagery').data(features); + imagery.exit().remove(); + imagery.enter().append('path').attr('class', 'debug-imagery debug orange'); + })["catch"](function () { + /* ignore */ + }); // downloaded + + var osm = context.connection(); + var dataDownloaded = []; + + if (osm && showDownloaded) { + var rtree = osm.caches('get').tile.rtree; + dataDownloaded = rtree.all().map(function (bbox) { + return { + type: 'Feature', + properties: { + id: bbox.id + }, + geometry: { + type: 'Polygon', + coordinates: [[[bbox.minX, bbox.minY], [bbox.minX, bbox.maxY], [bbox.maxX, bbox.maxY], [bbox.maxX, bbox.minY], [bbox.minX, bbox.minY]]] + } + }; + }); + } + + var downloaded = layer.selectAll('path.debug-downloaded').data(showDownloaded ? dataDownloaded : []); + downloaded.exit().remove(); + downloaded.enter().append('path').attr('class', 'debug-downloaded debug purple'); // update + + layer.selectAll('path').attr('d', svgPath(projection).geojson); + } // This looks strange because `enabled` methods on other layers are + // chainable getter/setters, and this one is just a getter. + + + drawDebug.enabled = function () { + if (!arguments.length) { + return context.getDebug('tile') || context.getDebug('collision') || context.getDebug('imagery') || context.getDebug('target') || context.getDebug('downloaded'); + } else { + return this; + } + }; + + return drawDebug; + } + + /* + A standalone SVG element that contains only a `defs` sub-element. To be + used once globally, since defs IDs must be unique within a document. + */ + + function svgDefs(context) { + var _defsSelection = select(null); + + var _spritesheetIds = ['iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'community-sprite']; + + function drawDefs(selection) { + _defsSelection = selection.append('defs'); // add markers + + _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 + // (they can't inherit it from the line they're attached to), + // so we need to manually define markers for each color of tag + // (also, it's slightly nicer if we can control the + // positioning for different tags) + + + function addSidedMarker(name, color, offset) { + _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); + } + + addSidedMarker('natural', 'rgb(170, 170, 170)', 0); // for a coastline, the arrows are (somewhat unintuitively) on + // the water side, so let's color them blue (with a gap) to + // give a stronger indication + + addSidedMarker('coastline', '#77dede', 1); + addSidedMarker('waterway', '#77dede', 1); // barriers have a dashed line, and separating the triangle + // from the line visually suits that + + addSidedMarker('barrier', '#ddd', 1); + addSidedMarker('man_made', '#fff', 0); + + _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'); + + _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 + + + var patterns = _defsSelection.selectAll('pattern').data([// pattern name, pattern image name + ['beach', 'dots'], ['construction', 'construction'], ['cemetery', 'cemetery'], ['cemetery_christian', 'cemetery_christian'], ['cemetery_buddhist', 'cemetery_buddhist'], ['cemetery_muslim', 'cemetery_muslim'], ['cemetery_jewish', 'cemetery_jewish'], ['farmland', 'farmland'], ['farmyard', 'farmyard'], ['forest', 'forest'], ['forest_broadleaved', 'forest_broadleaved'], ['forest_needleleaved', 'forest_needleleaved'], ['forest_leafless', 'forest_leafless'], ['golf_green', 'grass'], ['grass', 'grass'], ['landfill', 'landfill'], ['meadow', 'grass'], ['orchard', 'orchard'], ['pond', 'pond'], ['quarry', 'quarry'], ['scrub', 'bushes'], ['vineyard', 'vineyard'], ['water_standing', 'lines'], ['waves', 'waves'], ['wetland', 'wetland'], ['wetland_marsh', 'wetland_marsh'], ['wetland_swamp', 'wetland_swamp'], ['wetland_bog', 'wetland_bog'], ['wetland_reedbed', 'wetland_reedbed']]).enter().append('pattern').attr('id', function (d) { + return 'ideditor-pattern-' + d[0]; + }).attr('width', 32).attr('height', 32).attr('patternUnits', 'userSpaceOnUse'); + + patterns.append('rect').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('class', function (d) { + return 'pattern-color-' + d[0]; + }); + patterns.append('image').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('xlink:href', function (d) { + return context.imagePath('pattern/' + d[1] + '.png'); + }); // add clip paths + + _defsSelection.selectAll('clipPath').data([12, 18, 20, 32, 45]).enter().append('clipPath').attr('id', function (d) { + return 'ideditor-clip-square-' + d; + }).append('rect').attr('x', 0).attr('y', 0).attr('width', function (d) { + return d; + }).attr('height', function (d) { + return d; + }); // add symbol spritesheets + + + addSprites(_spritesheetIds, true); + } + + function addSprites(ids, overrideColors) { + _spritesheetIds = utilArrayUniq(_spritesheetIds.concat(ids)); + + var spritesheets = _defsSelection.selectAll('.spritesheet').data(_spritesheetIds); + + spritesheets.enter().append('g').attr('class', function (d) { + return 'spritesheet spritesheet-' + d; + }).each(function (d) { + var url = context.imagePath(d + '.svg'); + var node = select(this).node(); + svg(url).then(function (svg) { + node.appendChild(select(svg.documentElement).attr('id', 'ideditor-' + d).node()); + + if (overrideColors && d !== 'iD-sprite') { + // allow icon colors to be overridden.. + select(node).selectAll('path').attr('fill', 'currentColor'); + } + })["catch"](function () { + /* ignore */ + }); + }); + spritesheets.exit().remove(); + } + + drawDefs.addSprites = addSprites; + return drawDefs; + } + + var _layerEnabled$2 = false; + + var _qaService$2; + + function svgKeepRight(projection, context, dispatch) { + var throttledRedraw = throttle(function () { + return dispatch.call('change'); + }, 1000); + + var minZoom = 12; + var touchLayer = select(null); + var drawLayer = select(null); + var layerVisible = false; + + function markerPath(selection, klass) { + selection.attr('class', klass).attr('transform', 'translate(-4, -24)').attr('d', 'M11.6,6.2H7.1l1.4-5.1C8.6,0.6,8.1,0,7.5,0H2.2C1.7,0,1.3,0.3,1.3,0.8L0,10.2c-0.1,0.6,0.4,1.1,0.9,1.1h4.6l-1.8,7.6C3.6,19.4,4.1,20,4.7,20c0.3,0,0.6-0.2,0.8-0.5l6.9-11.9C12.7,7,12.3,6.2,11.6,6.2z'); + } // Loosely-coupled keepRight service for fetching issues. + + + function getService() { + if (services.keepRight && !_qaService$2) { + _qaService$2 = services.keepRight; + + _qaService$2.on('loaded', throttledRedraw); + } else if (!services.keepRight && _qaService$2) { + _qaService$2 = null; + } + + return _qaService$2; + } // Show the markers + + + function editOn() { + if (!layerVisible) { + layerVisible = true; + drawLayer.style('display', 'block'); + } + } // Immediately remove the markers and their touch targets + + + function editOff() { + if (layerVisible) { + layerVisible = false; + drawLayer.style('display', 'none'); + drawLayer.selectAll('.qaItem.keepRight').remove(); + touchLayer.selectAll('.qaItem.keepRight').remove(); + } + } // Enable the layer. This shows the markers and transitions them to visible. + + + function layerOn() { + editOn(); + drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () { + return dispatch.call('change'); + }); + } // Disable the layer. This transitions the layer invisible and then hides the markers. + + + function layerOff() { + throttledRedraw.cancel(); + drawLayer.interrupt(); + touchLayer.selectAll('.qaItem.keepRight').remove(); + drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () { + editOff(); + dispatch.call('change'); + }); + } // Update the issue markers + + + function updateMarkers() { + if (!layerVisible || !_layerEnabled$2) return; + var service = getService(); + var selectedID = context.selectedErrorID(); + var data = service ? service.getItems(projection) : []; + var getTransform = svgPointTransform(projection); // Draw markers.. + + var markers = drawLayer.selectAll('.qaItem.keepRight').data(data, function (d) { + return d.id; + }); // exit + + markers.exit().remove(); // enter + + var markersEnter = markers.enter().append('g').attr('class', function (d) { + return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType); + }); + markersEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke'); + markersEnter.append('path').call(markerPath, 'shadow'); + markersEnter.append('use').attr('class', 'qaItem-fill').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').attr('xlink:href', '#iD-icon-bolt'); // update + + markers.merge(markersEnter).sort(sortY).classed('selected', function (d) { + return d.id === selectedID; + }).attr('transform', getTransform); // Draw targets.. + + if (touchLayer.empty()) return; + var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; + var targets = touchLayer.selectAll('.qaItem.keepRight').data(data, function (d) { + return d.id; + }); // exit + + targets.exit().remove(); // enter/update + + targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) { + return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id); + }).attr('transform', getTransform); + + function sortY(a, b) { + return a.id === selectedID ? 1 : b.id === selectedID ? -1 : a.severity === 'error' && b.severity !== 'error' ? 1 : b.severity === 'error' && a.severity !== 'error' ? -1 : b.loc[1] - a.loc[1]; + } + } // Draw the keepRight layer and schedule loading issues and updating markers. + + + function drawKeepRight(selection) { + var service = getService(); + var surface = context.surface(); + + if (surface && !surface.empty()) { + touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers'); + } + + drawLayer = selection.selectAll('.layer-keepRight').data(service ? [0] : []); + drawLayer.exit().remove(); + drawLayer = drawLayer.enter().append('g').attr('class', 'layer-keepRight').style('display', _layerEnabled$2 ? 'block' : 'none').merge(drawLayer); + + if (_layerEnabled$2) { + if (service && ~~context.map().zoom() >= minZoom) { + editOn(); + service.loadIssues(projection); + updateMarkers(); + } else { + editOff(); + } + } + } // Toggles the layer on and off + + + drawKeepRight.enabled = function (val) { + if (!arguments.length) return _layerEnabled$2; + _layerEnabled$2 = val; + + if (_layerEnabled$2) { + layerOn(); + } else { + layerOff(); + + if (context.selectedErrorID()) { + context.enter(modeBrowse(context)); + } + } + + dispatch.call('change'); + return this; + }; + + drawKeepRight.supported = function () { + return !!getService(); + }; + + return drawKeepRight; + } + + function svgGeolocate(projection) { + var layer = select(null); + + var _position; + + function init() { + if (svgGeolocate.initialized) return; // run once + + svgGeolocate.enabled = false; + svgGeolocate.initialized = true; + } + + function showLayer() { + layer.style('display', 'block'); + } + + function hideLayer() { + layer.transition().duration(250).style('opacity', 0); + } + + function layerOn() { + layer.style('opacity', 0).transition().duration(250).style('opacity', 1); + } + + function layerOff() { + layer.style('display', 'none'); + } + + function transform(d) { + return svgPointTransform(projection)(d); + } + + function accuracy(accuracy, loc) { + // converts accuracy to pixels... + var degreesRadius = geoMetersToLat(accuracy), + tangentLoc = [loc[0], loc[1] + degreesRadius], + projectedTangent = projection(tangentLoc), + projectedLoc = projection([loc[0], loc[1]]); // southern most point will have higher pixel value... + + return Math.round(projectedLoc[1] - projectedTangent[1]).toString(); + } + + function update() { + var geolocation = { + loc: [_position.coords.longitude, _position.coords.latitude] + }; + var groups = layer.selectAll('.geolocations').selectAll('.geolocation').data([geolocation]); + groups.exit().remove(); + var pointsEnter = groups.enter().append('g').attr('class', 'geolocation'); + pointsEnter.append('circle').attr('class', 'geolocate-radius').attr('dx', '0').attr('dy', '0').attr('fill', 'rgb(15,128,225)').attr('fill-opacity', '0.3').attr('r', '0'); + pointsEnter.append('circle').attr('dx', '0').attr('dy', '0').attr('fill', 'rgb(15,128,225)').attr('stroke', 'white').attr('stroke-width', '1.5').attr('r', '6'); + groups.merge(pointsEnter).attr('transform', transform); + layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc)); + } + + function drawLocation(selection) { + var enabled = svgGeolocate.enabled; + layer = selection.selectAll('.layer-geolocate').data([0]); + layer.exit().remove(); + var layerEnter = layer.enter().append('g').attr('class', 'layer-geolocate').style('display', enabled ? 'block' : 'none'); + layerEnter.append('g').attr('class', 'geolocations'); + layer = layerEnter.merge(layer); + + if (enabled) { + update(); + } else { + layerOff(); + } + } + + drawLocation.enabled = function (position, enabled) { + if (!arguments.length) return svgGeolocate.enabled; + _position = position; + svgGeolocate.enabled = enabled; + + if (svgGeolocate.enabled) { + showLayer(); + layerOn(); + } else { + hideLayer(); + } + + return this; + }; + + init(); + return drawLocation; + } + + function svgLabels(projection, context) { + var path = d3_geoPath(projection); + var detected = utilDetect(); + var baselineHack = detected.ie || detected.browser.toLowerCase() === 'edge' || detected.browser.toLowerCase() === 'firefox' && detected.version >= 70; + + var _rdrawn = new RBush(); + + var _rskipped = new RBush(); + + var _textWidthCache = {}; + var _entitybboxes = {}; // Listed from highest to lowest priority + + var labelStack = [['line', 'aeroway', '*', 12], ['line', 'highway', 'motorway', 12], ['line', 'highway', 'trunk', 12], ['line', 'highway', 'primary', 12], ['line', 'highway', 'secondary', 12], ['line', 'highway', 'tertiary', 12], ['line', 'highway', '*', 12], ['line', 'railway', '*', 12], ['line', 'waterway', '*', 12], ['area', 'aeroway', '*', 12], ['area', 'amenity', '*', 12], ['area', 'building', '*', 12], ['area', 'historic', '*', 12], ['area', 'leisure', '*', 12], ['area', 'man_made', '*', 12], ['area', 'natural', '*', 12], ['area', 'shop', '*', 12], ['area', 'tourism', '*', 12], ['area', 'camp_site', '*', 12], ['point', 'aeroway', '*', 10], ['point', 'amenity', '*', 10], ['point', 'building', '*', 10], ['point', 'historic', '*', 10], ['point', 'leisure', '*', 10], ['point', 'man_made', '*', 10], ['point', 'natural', '*', 10], ['point', 'shop', '*', 10], ['point', 'tourism', '*', 10], ['point', 'camp_site', '*', 10], ['line', 'name', '*', 12], ['area', 'name', '*', 12], ['point', 'name', '*', 10]]; + + function shouldSkipIcon(preset) { + var noIcons = ['building', 'landuse', 'natural']; + return noIcons.some(function (s) { + return preset.id.indexOf(s) >= 0; + }); + } + + function get(array, prop) { + return function (d, i) { + return array[i][prop]; + }; + } + + function textWidth(text, size, elem) { + var c = _textWidthCache[size]; + if (!c) c = _textWidthCache[size] = {}; + + if (c[text]) { + return c[text]; + } else if (elem) { + c[text] = elem.getComputedTextLength(); + return c[text]; + } else { + var str = encodeURIComponent(text).match(/%[CDEFcdef]/g); + + if (str === null) { + return size / 3 * 2 * text.length; + } else { + return size / 3 * (2 * text.length + str.length); + } + } + } + + function drawLinePaths(selection, entities, filter, classes, labels) { + var paths = selection.selectAll('path').filter(filter).data(entities, osmEntity.key); // exit + + paths.exit().remove(); // enter/update + + paths.enter().append('path').style('stroke-width', get(labels, 'font-size')).attr('id', function (d) { + return 'ideditor-labelpath-' + d.id; + }).attr('class', classes).merge(paths).attr('d', get(labels, 'lineString')); + } + + function drawLineLabels(selection, entities, filter, classes, labels) { + var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit + + texts.exit().remove(); // enter + + texts.enter().append('text').attr('class', function (d, i) { + return classes + ' ' + labels[i].classes + ' ' + d.id; + }).attr('dy', baselineHack ? '0.35em' : null).append('textPath').attr('class', 'textpath'); // update + + selection.selectAll('text.' + classes).selectAll('.textpath').filter(filter).data(entities, osmEntity.key).attr('startOffset', '50%').attr('xlink:href', function (d) { + return '#ideditor-labelpath-' + d.id; + }).text(utilDisplayNameForPath); + } + + function drawPointLabels(selection, entities, filter, classes, labels) { + var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit + + texts.exit().remove(); // enter/update + + texts.enter().append('text').attr('class', function (d, i) { + return classes + ' ' + labels[i].classes + ' ' + d.id; + }).merge(texts).attr('x', get(labels, 'x')).attr('y', get(labels, 'y')).style('text-anchor', get(labels, 'textAnchor')).text(utilDisplayName).each(function (d, i) { + textWidth(utilDisplayName(d), labels[i].height, this); + }); + } + + function drawAreaLabels(selection, entities, filter, classes, labels) { + entities = entities.filter(hasText); + labels = labels.filter(hasText); + drawPointLabels(selection, entities, filter, classes, labels); + + function hasText(d, i) { + return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y'); + } + } + + function drawAreaIcons(selection, entities, filter, classes, labels) { + var icons = selection.selectAll('use.' + classes).filter(filter).data(entities, osmEntity.key); // exit + + icons.exit().remove(); // enter/update + + icons.enter().append('use').attr('class', 'icon ' + classes).attr('width', '17px').attr('height', '17px').merge(icons).attr('transform', get(labels, 'transform')).attr('xlink:href', function (d) { + var preset = _mainPresetIndex.match(d, context.graph()); + var picon = preset && preset.icon; + + if (!picon) { + return ''; + } else { + var isMaki = /^maki-/.test(picon); + return '#' + picon + (isMaki ? '-15' : ''); + } + }); + } + + function drawCollisionBoxes(selection, rtree, which) { + var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow'); + var gj = []; + + if (context.getDebug('collision')) { + gj = rtree.all().map(function (d) { + return { + type: 'Polygon', + coordinates: [[[d.minX, d.minY], [d.maxX, d.minY], [d.maxX, d.maxY], [d.minX, d.maxY], [d.minX, d.minY]]] + }; + }); + } + + var boxes = selection.selectAll('.' + which).data(gj); // exit + + boxes.exit().remove(); // enter/update + + boxes.enter().append('path').attr('class', classes).merge(boxes).attr('d', d3_geoPath()); + } + + function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) { + var wireframe = context.surface().classed('fill-wireframe'); + var zoom = geoScaleToZoom(projection.scale()); + var labelable = []; + var renderNodeAs = {}; + var i, j, k, entity, geometry; + + for (i = 0; i < labelStack.length; i++) { + labelable.push([]); + } + + if (fullRedraw) { + _rdrawn.clear(); + + _rskipped.clear(); + + _entitybboxes = {}; + } else { + for (i = 0; i < entities.length; i++) { + entity = entities[i]; + var toRemove = [].concat(_entitybboxes[entity.id] || []).concat(_entitybboxes[entity.id + 'I'] || []); + + for (j = 0; j < toRemove.length; j++) { + _rdrawn.remove(toRemove[j]); + + _rskipped.remove(toRemove[j]); + } + } + } // Loop through all the entities to do some preprocessing + + + for (i = 0; i < entities.length; i++) { + entity = entities[i]; + geometry = entity.geometry(graph); // Insert collision boxes around interesting points/vertices + + if (geometry === 'point' || geometry === 'vertex' && isInterestingVertex(entity)) { + var hasDirections = entity.directions(graph, projection).length; + var markerPadding; + + if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) { + renderNodeAs[entity.id] = 'point'; + markerPadding = 20; // extra y for marker height + } else { + renderNodeAs[entity.id] = 'vertex'; + markerPadding = 0; + } + + var coord = projection(entity.loc); + var nodePadding = 10; + var bbox = { + minX: coord[0] - nodePadding, + minY: coord[1] - nodePadding - markerPadding, + maxX: coord[0] + nodePadding, + maxY: coord[1] + nodePadding + }; + doInsert(bbox, entity.id + 'P'); + } // From here on, treat vertices like points + + + if (geometry === 'vertex') { + geometry = 'point'; + } // Determine which entities are label-able + + + var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph); + var icon = preset && !shouldSkipIcon(preset) && preset.icon; + if (!icon && !utilDisplayName(entity)) continue; + + for (k = 0; k < labelStack.length; k++) { + var matchGeom = labelStack[k][0]; + var matchKey = labelStack[k][1]; + var matchVal = labelStack[k][2]; + var hasVal = entity.tags[matchKey]; + + if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) { + labelable[k].push(entity); + break; + } + } + } + + var positions = { + point: [], + line: [], + area: [] + }; + var labelled = { + point: [], + line: [], + area: [] + }; // Try and find a valid label for labellable entities + + for (k = 0; k < labelable.length; k++) { + var fontSize = labelStack[k][3]; + + for (i = 0; i < labelable[k].length; i++) { + entity = labelable[k][i]; + geometry = entity.geometry(graph); + var getName = geometry === 'line' ? utilDisplayNameForPath : utilDisplayName; + var name = getName(entity); + var width = name && textWidth(name, fontSize); + var p = null; + + if (geometry === 'point' || geometry === 'vertex') { + // no point or vertex labels in wireframe mode + // no vertex labels at low zooms (vertices have no icons) + if (wireframe) continue; + var renderAs = renderNodeAs[entity.id]; + if (renderAs === 'vertex' && zoom < 17) continue; + p = getPointLabel(entity, width, fontSize, renderAs); + } else if (geometry === 'line') { + p = getLineLabel(entity, width, fontSize); + } else if (geometry === 'area') { + p = getAreaLabel(entity, width, fontSize); + } + + if (p) { + if (geometry === 'vertex') { + geometry = 'point'; + } // treat vertex like point + + + p.classes = geometry + ' tag-' + labelStack[k][1]; + positions[geometry].push(p); + labelled[geometry].push(entity); + } + } + } + + function isInterestingVertex(entity) { + var selectedIDs = context.selectedIDs(); + return entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || selectedIDs.indexOf(entity.id) !== -1 || graph.parentWays(entity).some(function (parent) { + return selectedIDs.indexOf(parent.id) !== -1; + }); + } + + function getPointLabel(entity, width, height, geometry) { + var y = geometry === 'point' ? -12 : 0; + var pointOffsets = { + ltr: [15, y, 'start'], + rtl: [-15, y, 'end'] + }; + var textDirection = _mainLocalizer.textDirection(); + var coord = projection(entity.loc); + var textPadding = 2; + var offset = pointOffsets[textDirection]; + var p = { + height: height, + width: width, + x: coord[0] + offset[0], + y: coord[1] + offset[1], + textAnchor: offset[2] + }; // insert a collision box for the text label.. + + var bbox; + + if (textDirection === 'rtl') { + bbox = { + minX: p.x - width - textPadding, + minY: p.y - height / 2 - textPadding, + maxX: p.x + textPadding, + maxY: p.y + height / 2 + textPadding + }; + } else { + bbox = { + minX: p.x - textPadding, + minY: p.y - height / 2 - textPadding, + maxX: p.x + width + textPadding, + maxY: p.y + height / 2 + textPadding + }; + } + + if (tryInsert([bbox], entity.id, true)) { + return p; + } + } + + function getLineLabel(entity, width, height) { + var viewport = geoExtent(context.projection.clipExtent()).polygon(); + var points = graph.childNodes(entity).map(function (node) { + return projection(node.loc); + }); + var length = geoPathLength(points); + if (length < width + 20) return; // % along the line to attempt to place the label + + var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25, 75, 20, 80, 15, 95, 10, 90, 5, 95]; + var padding = 3; + + for (var i = 0; i < lineOffsets.length; i++) { + var offset = lineOffsets[i]; + var middle = offset / 100 * length; + var start = middle - width / 2; + if (start < 0 || start + width > length) continue; // generate subpath and ignore paths that are invalid or don't cross viewport. + + var sub = subpath(points, start, start + width); + + if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) { + continue; + } + + var isReverse = reverse(sub); + + if (isReverse) { + sub = sub.reverse(); + } + + var bboxes = []; + var boxsize = (height + 2) / 2; + + for (var j = 0; j < sub.length - 1; j++) { + var a = sub[j]; + var b = sub[j + 1]; // split up the text into small collision boxes + + var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2)); + + for (var box = 0; box < num; box++) { + var p = geoVecInterp(a, b, box / num); + var x0 = p[0] - boxsize - padding; + var y0 = p[1] - boxsize - padding; + var x1 = p[0] + boxsize + padding; + var y1 = p[1] + boxsize + padding; + bboxes.push({ + minX: Math.min(x0, x1), + minY: Math.min(y0, y1), + maxX: Math.max(x0, x1), + maxY: Math.max(y0, y1) + }); + } + } + + if (tryInsert(bboxes, entity.id, false)) { + // accept this one + return { + 'font-size': height + 2, + lineString: lineString(sub), + startOffset: offset + '%' + }; + } + } + + function reverse(p) { + var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]); + return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI / 2 && angle > -Math.PI / 2); + } + + function lineString(points) { + return 'M' + points.join('L'); + } + + function subpath(points, from, to) { + var sofar = 0; + var start, end, i0, i1; + + for (var i = 0; i < points.length - 1; i++) { + var a = points[i]; + var b = points[i + 1]; + var current = geoVecLength(a, b); + var portion; + + if (!start && sofar + current >= from) { + portion = (from - sofar) / current; + start = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])]; + i0 = i + 1; + } + + if (!end && sofar + current >= to) { + portion = (to - sofar) / current; + end = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])]; + i1 = i + 1; + } + + sofar += current; + } + + var result = points.slice(i0, i1); + result.unshift(start); + result.push(end); + return result; + } + } + + function getAreaLabel(entity, width, height) { + var centroid = path.centroid(entity.asGeoJSON(graph)); + var extent = entity.extent(graph); + var areaWidth = projection(extent[1])[0] - projection(extent[0])[0]; + if (isNaN(centroid[0]) || areaWidth < 20) return; + var preset = _mainPresetIndex.match(entity, context.graph()); + var picon = preset && preset.icon; + var iconSize = 17; + var padding = 2; + var p = {}; + + if (picon) { + // icon and label.. + if (addIcon()) { + addLabel(iconSize + padding); + return p; + } + } else { + // label only.. + if (addLabel(0)) { + return p; + } + } + + function addIcon() { + var iconX = centroid[0] - iconSize / 2; + var iconY = centroid[1] - iconSize / 2; + var bbox = { + minX: iconX, + minY: iconY, + maxX: iconX + iconSize, + maxY: iconY + iconSize + }; + + if (tryInsert([bbox], entity.id + 'I', true)) { + p.transform = 'translate(' + iconX + ',' + iconY + ')'; + return true; + } + + return false; + } + + function addLabel(yOffset) { + if (width && areaWidth >= width + 20) { + var labelX = centroid[0]; + var labelY = centroid[1] + yOffset; + var bbox = { + minX: labelX - width / 2 - padding, + minY: labelY - height / 2 - padding, + maxX: labelX + width / 2 + padding, + maxY: labelY + height / 2 + padding + }; + + if (tryInsert([bbox], entity.id, true)) { + p.x = labelX; + p.y = labelY; + p.textAnchor = 'middle'; + p.height = height; + return true; + } + } + + return false; + } + } // force insert a singular bounding box + // singular box only, no array, id better be unique + + + function doInsert(bbox, id) { + bbox.id = id; + var oldbox = _entitybboxes[id]; + + if (oldbox) { + _rdrawn.remove(oldbox); + } + + _entitybboxes[id] = bbox; + + _rdrawn.insert(bbox); + } + + function tryInsert(bboxes, id, saveSkipped) { + var skipped = false; + + for (var i = 0; i < bboxes.length; i++) { + var bbox = bboxes[i]; + bbox.id = id; // Check that label is visible + + if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) { + skipped = true; + break; + } + + if (_rdrawn.collides(bbox)) { + skipped = true; + break; + } + } + + _entitybboxes[id] = bboxes; + + if (skipped) { + if (saveSkipped) { + _rskipped.load(bboxes); + } + } else { + _rdrawn.load(bboxes); + } + + return !skipped; + } + + var layer = selection.selectAll('.layer-osm.labels'); + layer.selectAll('.labels-group').data(['halo', 'label', 'debug']).enter().append('g').attr('class', function (d) { + return 'labels-group ' + d; + }); + var halo = layer.selectAll('.labels-group.halo'); + var label = layer.selectAll('.labels-group.label'); + var debug = layer.selectAll('.labels-group.debug'); // points + + drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point); + drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point); // lines + + drawLinePaths(layer, labelled.line, filter, '', positions.line); + drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line); + drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line); // areas + + drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area); + drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area); + drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area); + drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area); // debug + + drawCollisionBoxes(debug, _rskipped, 'debug-skipped'); + drawCollisionBoxes(debug, _rdrawn, 'debug-drawn'); + layer.call(filterLabels); + } + + function filterLabels(selection) { + var drawLayer = selection.selectAll('.layer-osm.labels'); + var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label'); + layers.selectAll('.nolabel').classed('nolabel', false); + var mouse = context.map().mouse(); + var graph = context.graph(); + var selectedIDs = context.selectedIDs(); + var ids = []; + var pad, bbox; // hide labels near the mouse + + if (mouse) { + pad = 20; + bbox = { + minX: mouse[0] - pad, + minY: mouse[1] - pad, + maxX: mouse[0] + pad, + maxY: mouse[1] + pad + }; + + var nearMouse = _rdrawn.search(bbox).map(function (entity) { + return entity.id; + }); + + ids.push.apply(ids, nearMouse); + } // hide labels on selected nodes (they look weird when dragging / haloed) + + + for (var i = 0; i < selectedIDs.length; i++) { + var entity = graph.hasEntity(selectedIDs[i]); + + if (entity && entity.type === 'node') { + ids.push(selectedIDs[i]); + } + } + + layers.selectAll(utilEntitySelector(ids)).classed('nolabel', true); // draw the mouse bbox if debugging is on.. + + var debug = selection.selectAll('.labels-group.debug'); + var gj = []; + + if (context.getDebug('collision')) { + gj = bbox ? [{ + type: 'Polygon', + coordinates: [[[bbox.minX, bbox.minY], [bbox.maxX, bbox.minY], [bbox.maxX, bbox.maxY], [bbox.minX, bbox.maxY], [bbox.minX, bbox.minY]]] + }] : []; + } + + var box = debug.selectAll('.debug-mouse').data(gj); // exit + + box.exit().remove(); // enter/update + + box.enter().append('path').attr('class', 'debug debug-mouse yellow').merge(box).attr('d', d3_geoPath()); + } + + var throttleFilterLabels = throttle(filterLabels, 100); + + drawLabels.observe = function (selection) { + var listener = function listener() { + throttleFilterLabels(selection); + }; + + selection.on('mousemove.hidelabels', listener); + context.on('enter.hidelabels', listener); + }; + + drawLabels.off = function (selection) { + throttleFilterLabels.cancel(); + selection.on('mousemove.hidelabels', null); + context.on('enter.hidelabels', null); + }; + + return drawLabels; + } + + var _layerEnabled$1 = false; + + var _qaService$1; + + function svgImproveOSM(projection, context, dispatch) { + var throttledRedraw = throttle(function () { + return dispatch.call('change'); + }, 1000); + + var minZoom = 12; + var touchLayer = select(null); + var drawLayer = select(null); + var layerVisible = false; + + function markerPath(selection, klass) { + selection.attr('class', klass).attr('transform', 'translate(-10, -28)').attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6'); + } // Loosely-coupled improveOSM service for fetching issues + + + function getService() { + if (services.improveOSM && !_qaService$1) { + _qaService$1 = services.improveOSM; + + _qaService$1.on('loaded', throttledRedraw); + } else if (!services.improveOSM && _qaService$1) { + _qaService$1 = null; + } + + return _qaService$1; + } // Show the markers + + + function editOn() { + if (!layerVisible) { + layerVisible = true; + drawLayer.style('display', 'block'); + } + } // Immediately remove the markers and their touch targets + + + function editOff() { + if (layerVisible) { + layerVisible = false; + drawLayer.style('display', 'none'); + drawLayer.selectAll('.qaItem.improveOSM').remove(); + touchLayer.selectAll('.qaItem.improveOSM').remove(); + } + } // Enable the layer. This shows the markers and transitions them to visible. + + + function layerOn() { + editOn(); + drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () { + return dispatch.call('change'); + }); + } // Disable the layer. This transitions the layer invisible and then hides the markers. + + + function layerOff() { + throttledRedraw.cancel(); + drawLayer.interrupt(); + touchLayer.selectAll('.qaItem.improveOSM').remove(); + drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () { + editOff(); + dispatch.call('change'); + }); + } // Update the issue markers + + + function updateMarkers() { + if (!layerVisible || !_layerEnabled$1) return; + var service = getService(); + var selectedID = context.selectedErrorID(); + var data = service ? service.getItems(projection) : []; + var getTransform = svgPointTransform(projection); // Draw markers.. + + var markers = drawLayer.selectAll('.qaItem.improveOSM').data(data, function (d) { + return d.id; + }); // exit + + markers.exit().remove(); // enter + + var markersEnter = markers.enter().append('g').attr('class', function (d) { + return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType); + }); + markersEnter.append('polygon').call(markerPath, 'shadow'); + markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke'); + markersEnter.append('polygon').attr('fill', 'currentColor').call(markerPath, 'qaItem-fill'); + markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) { + var picon = d.icon; + + if (!picon) { + return ''; + } else { + var isMaki = /^maki-/.test(picon); + return "#".concat(picon).concat(isMaki ? '-11' : ''); + } + }); // update + + markers.merge(markersEnter).sort(sortY).classed('selected', function (d) { + return d.id === selectedID; + }).attr('transform', getTransform); // Draw targets.. + + if (touchLayer.empty()) return; + var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; + var targets = touchLayer.selectAll('.qaItem.improveOSM').data(data, function (d) { + return d.id; + }); // exit + + targets.exit().remove(); // enter/update + + targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) { + return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id); + }).attr('transform', getTransform); + + function sortY(a, b) { + return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1]; + } + } // Draw the ImproveOSM layer and schedule loading issues and updating markers. + + + function drawImproveOSM(selection) { + var service = getService(); + var surface = context.surface(); + + if (surface && !surface.empty()) { + touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers'); + } + + drawLayer = selection.selectAll('.layer-improveOSM').data(service ? [0] : []); + drawLayer.exit().remove(); + drawLayer = drawLayer.enter().append('g').attr('class', 'layer-improveOSM').style('display', _layerEnabled$1 ? 'block' : 'none').merge(drawLayer); + + if (_layerEnabled$1) { + if (service && ~~context.map().zoom() >= minZoom) { + editOn(); + service.loadIssues(projection); + updateMarkers(); + } else { + editOff(); + } + } + } // Toggles the layer on and off + + + drawImproveOSM.enabled = function (val) { + if (!arguments.length) return _layerEnabled$1; + _layerEnabled$1 = val; + + if (_layerEnabled$1) { + layerOn(); + } else { + layerOff(); + + if (context.selectedErrorID()) { + context.enter(modeBrowse(context)); + } + } + + dispatch.call('change'); + return this; + }; + + drawImproveOSM.supported = function () { + return !!getService(); + }; + + return drawImproveOSM; + } + + var _layerEnabled = false; + + var _qaService; + + function svgOsmose(projection, context, dispatch) { + var throttledRedraw = throttle(function () { + return dispatch.call('change'); + }, 1000); + + var minZoom = 12; + var touchLayer = select(null); + var drawLayer = select(null); + var layerVisible = false; + + function markerPath(selection, klass) { + selection.attr('class', klass).attr('transform', 'translate(-10, -28)').attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6'); + } // Loosely-coupled osmose service for fetching issues + + + function getService() { + if (services.osmose && !_qaService) { + _qaService = services.osmose; + + _qaService.on('loaded', throttledRedraw); + } else if (!services.osmose && _qaService) { + _qaService = null; + } + + return _qaService; + } // Show the markers + + + function editOn() { + if (!layerVisible) { + layerVisible = true; + drawLayer.style('display', 'block'); + } + } // Immediately remove the markers and their touch targets + + + function editOff() { + if (layerVisible) { + layerVisible = false; + drawLayer.style('display', 'none'); + drawLayer.selectAll('.qaItem.osmose').remove(); + touchLayer.selectAll('.qaItem.osmose').remove(); + } + } // Enable the layer. This shows the markers and transitions them to visible. + + + function layerOn() { + editOn(); + drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () { + return dispatch.call('change'); + }); + } // Disable the layer. This transitions the layer invisible and then hides the markers. + + + function layerOff() { + throttledRedraw.cancel(); + drawLayer.interrupt(); + touchLayer.selectAll('.qaItem.osmose').remove(); + drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () { + editOff(); + dispatch.call('change'); + }); + } // Update the issue markers + + + function updateMarkers() { + if (!layerVisible || !_layerEnabled) return; + var service = getService(); + var selectedID = context.selectedErrorID(); + var data = service ? service.getItems(projection) : []; + var getTransform = svgPointTransform(projection); // Draw markers.. + + var markers = drawLayer.selectAll('.qaItem.osmose').data(data, function (d) { + return d.id; + }); // exit + + markers.exit().remove(); // enter + + var markersEnter = markers.enter().append('g').attr('class', function (d) { + return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType); + }); + markersEnter.append('polygon').call(markerPath, 'shadow'); + markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke'); + markersEnter.append('polygon').attr('fill', function (d) { + return service.getColor(d.item); + }).call(markerPath, 'qaItem-fill'); + markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) { + var picon = d.icon; + + if (!picon) { + return ''; + } else { + var isMaki = /^maki-/.test(picon); + return "#".concat(picon).concat(isMaki ? '-11' : ''); + } + }); // update + + markers.merge(markersEnter).sort(sortY).classed('selected', function (d) { + return d.id === selectedID; + }).attr('transform', getTransform); // Draw targets.. + + if (touchLayer.empty()) return; + var fillClass = context.getDebug('target') ? 'pink' : 'nocolor'; + var targets = touchLayer.selectAll('.qaItem.osmose').data(data, function (d) { + return d.id; + }); // exit + + targets.exit().remove(); // enter/update + + targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) { + return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id); + }).attr('transform', getTransform); + + function sortY(a, b) { + return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1]; + } + } // Draw the Osmose layer and schedule loading issues and updating markers. + + + function drawOsmose(selection) { + var service = getService(); + var surface = context.surface(); + + if (surface && !surface.empty()) { + touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers'); + } + + drawLayer = selection.selectAll('.layer-osmose').data(service ? [0] : []); + drawLayer.exit().remove(); + drawLayer = drawLayer.enter().append('g').attr('class', 'layer-osmose').style('display', _layerEnabled ? 'block' : 'none').merge(drawLayer); + + if (_layerEnabled) { + if (service && ~~context.map().zoom() >= minZoom) { + editOn(); + service.loadIssues(projection); + updateMarkers(); + } else { + editOff(); + } + } + } // Toggles the layer on and off + + + drawOsmose.enabled = function (val) { + if (!arguments.length) return _layerEnabled; + _layerEnabled = val; + + if (_layerEnabled) { + // Strings supplied by Osmose fetched before showing layer for first time + // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented + // Also, If layer is toggled quickly multiple requests are sent + getService().loadStrings().then(layerOn)["catch"](function (err) { + console.log(err); // eslint-disable-line no-console + }); + } else { + layerOff(); + + if (context.selectedErrorID()) { + context.enter(modeBrowse(context)); + } + } + + dispatch.call('change'); + return this; + }; + + drawOsmose.supported = function () { + return !!getService(); + }; + + return drawOsmose; + } + + function svgStreetside(projection, context, dispatch) { + var throttledRedraw = throttle(function () { + dispatch.call('change'); + }, 1000); + + var minZoom = 14; + var minMarkerZoom = 16; + var minViewfieldZoom = 18; + var layer = select(null); + var _viewerYaw = 0; + var _selectedSequence = null; + + var _streetside; + /** + * init(). + */ + + + function init() { + if (svgStreetside.initialized) return; // run once + + svgStreetside.enabled = false; + svgStreetside.initialized = true; + } + /** + * getService(). + */ + + + function getService() { + if (services.streetside && !_streetside) { + _streetside = services.streetside; + + _streetside.event.on('viewerChanged.svgStreetside', viewerChanged).on('loadedImages.svgStreetside', throttledRedraw); + } else if (!services.streetside && _streetside) { + _streetside = null; + } + + return _streetside; + } + /** + * showLayer(). + */ + + + function showLayer() { + var service = getService(); + if (!service) return; + editOn(); + layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () { + dispatch.call('change'); + }); + } + /** + * hideLayer(). + */ + + + function hideLayer() { + throttledRedraw.cancel(); + layer.transition().duration(250).style('opacity', 0).on('end', editOff); + } + /** + * editOn(). + */ + + + function editOn() { + layer.style('display', 'block'); + } + /** + * editOff(). + */ + + + function editOff() { + layer.selectAll('.viewfield-group').remove(); + layer.style('display', 'none'); + } + /** + * click() Handles 'bubble' point click event. + */ + + + function click(d3_event, d) { + var service = getService(); + if (!service) return; // try to preserve the viewer rotation when staying on the same sequence + + if (d.sequenceKey !== _selectedSequence) { + _viewerYaw = 0; // reset + } + + _selectedSequence = d.sequenceKey; + service.ensureViewerLoaded(context).then(function () { + service.selectImage(context, d.key).yaw(_viewerYaw).showViewer(context); + }); + context.map().centerEase(d.loc); + } + /** + * mouseover(). + */ + + + function mouseover(d3_event, d) { + var service = getService(); + if (service) service.setStyles(context, d); + } + /** + * mouseout(). + */ + + + function mouseout() { + var service = getService(); + if (service) service.setStyles(context, null); + } + /** + * transform(). + */ + + + function transform(d) { + var t = svgPointTransform(projection)(d); + var rot = d.ca + _viewerYaw; + + if (rot) { + t += ' rotate(' + Math.floor(rot) + ',0,0)'; + } + + return t; + } + + function viewerChanged() { + var service = getService(); + if (!service) return; + var viewer = service.viewer(); + if (!viewer) return; // update viewfield rotation + + _viewerYaw = viewer.getYaw(); // avoid updating if the map is currently transformed + // e.g. during drags or easing. + + if (context.map().isTransformed()) return; + layer.selectAll('.viewfield-group.currentView').attr('transform', transform); + } + + function filterBubbles(bubbles) { + var fromDate = context.photos().fromDate(); + var toDate = context.photos().toDate(); + var usernames = context.photos().usernames(); + + if (fromDate) { + var fromTimestamp = new Date(fromDate).getTime(); + bubbles = bubbles.filter(function (bubble) { + return new Date(bubble.captured_at).getTime() >= fromTimestamp; + }); + } + + if (toDate) { + var toTimestamp = new Date(toDate).getTime(); + bubbles = bubbles.filter(function (bubble) { + return new Date(bubble.captured_at).getTime() <= toTimestamp; + }); + } + + if (usernames) { + bubbles = bubbles.filter(function (bubble) { + return usernames.indexOf(bubble.captured_by) !== -1; + }); + } + + return bubbles; + } + + function filterSequences(sequences) { + var fromDate = context.photos().fromDate(); + var toDate = context.photos().toDate(); + var usernames = context.photos().usernames(); + + if (fromDate) { + var fromTimestamp = new Date(fromDate).getTime(); + sequences = sequences.filter(function (sequences) { + return new Date(sequences.properties.captured_at).getTime() >= fromTimestamp; + }); + } + + if (toDate) { + var toTimestamp = new Date(toDate).getTime(); + sequences = sequences.filter(function (sequences) { + return new Date(sequences.properties.captured_at).getTime() <= toTimestamp; + }); + } + + if (usernames) { + sequences = sequences.filter(function (sequences) { + return usernames.indexOf(sequences.properties.captured_by) !== -1; + }); + } + + return sequences; + } + /** + * update(). + */ + + + function update() { + var viewer = context.container().select('.photoviewer'); + var selected = viewer.empty() ? undefined : viewer.datum(); + var z = ~~context.map().zoom(); + var showMarkers = z >= minMarkerZoom; + var showViewfields = z >= minViewfieldZoom; + var service = getService(); + var sequences = []; + var bubbles = []; + + if (context.photos().showsPanoramic()) { + sequences = service ? service.sequences(projection) : []; + bubbles = service && showMarkers ? service.bubbles(projection) : []; + sequences = filterSequences(sequences); + bubbles = filterBubbles(bubbles); + } + + var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) { + return d.properties.key; + }); // exit + + traces.exit().remove(); // enter/update + + traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson); + var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(bubbles, function (d) { + // force reenter once bubbles are attached to a sequence + return d.key + (d.sequenceKey ? 'v1' : 'v0'); + }); // exit + + groups.exit().remove(); // enter + + var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click); + groupsEnter.append('g').attr('class', 'viewfield-scale'); // update + + var markers = groups.merge(groupsEnter).sort(function (a, b) { + return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1]; + }).attr('transform', transform).select('.viewfield-scale'); + markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6'); + var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []); + viewfields.exit().remove(); // viewfields may or may not be drawn... + // but if they are, draw below the circles + + viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath); + + function viewfieldPath() { + var d = this.parentNode.__data__; + + if (d.pano) { + return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0'; + } else { + return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'; + } + } + } + /** + * drawImages() + * drawImages is the method that is returned (and that runs) every time 'svgStreetside()' is called. + * 'svgStreetside()' is called from index.js + */ + + + function drawImages(selection) { + var enabled = svgStreetside.enabled; + var service = getService(); + layer = selection.selectAll('.layer-streetside-images').data(service ? [0] : []); + layer.exit().remove(); + var layerEnter = layer.enter().append('g').attr('class', 'layer-streetside-images').style('display', enabled ? 'block' : 'none'); + layerEnter.append('g').attr('class', 'sequences'); + layerEnter.append('g').attr('class', 'markers'); + layer = layerEnter.merge(layer); + + if (enabled) { + if (service && ~~context.map().zoom() >= minZoom) { + editOn(); + update(); + service.loadBubbles(projection); + } else { + editOff(); + } + } + } + /** + * drawImages.enabled(). + */ + + + drawImages.enabled = function (_) { + if (!arguments.length) return svgStreetside.enabled; + svgStreetside.enabled = _; + + if (svgStreetside.enabled) { + showLayer(); + context.photos().on('change.streetside', update); + } else { + hideLayer(); + context.photos().on('change.streetside', null); + } + + dispatch.call('change'); + return this; + }; + /** + * drawImages.supported(). + */ + + + drawImages.supported = function () { + return !!getService(); + }; + + init(); + return drawImages; + } + + function svgMapillaryImages(projection, context, dispatch) { + var throttledRedraw = throttle(function () { + dispatch.call('change'); + }, 1000); + + var minZoom = 12; + var minMarkerZoom = 16; + var minViewfieldZoom = 18; + var layer = select(null); + + var _mapillary; + + function init() { + if (svgMapillaryImages.initialized) return; // run once + + svgMapillaryImages.enabled = false; + svgMapillaryImages.initialized = true; + } + + function getService() { + if (services.mapillary && !_mapillary) { + _mapillary = services.mapillary; + + _mapillary.event.on('loadedImages', throttledRedraw); + } else if (!services.mapillary && _mapillary) { + _mapillary = null; + } + + return _mapillary; + } + + function showLayer() { + var service = getService(); + if (!service) return; + editOn(); + layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () { + dispatch.call('change'); + }); + } + + function hideLayer() { + throttledRedraw.cancel(); + layer.transition().duration(250).style('opacity', 0).on('end', editOff); + } + + function editOn() { + layer.style('display', 'block'); + } + + function editOff() { + layer.selectAll('.viewfield-group').remove(); + layer.style('display', 'none'); + } + + function click(d3_event, image) { + var service = getService(); + if (!service) return; + service.ensureViewerLoaded(context).then(function () { + service.selectImage(context, image.id).showViewer(context); + }); + context.map().centerEase(image.loc); + } + + function mouseover(d3_event, image) { + var service = getService(); + if (service) service.setStyles(context, image); + } + + function mouseout() { + var service = getService(); + if (service) service.setStyles(context, null); + } + + function transform(d) { + var t = svgPointTransform(projection)(d); + + if (d.ca) { + t += ' rotate(' + Math.floor(d.ca) + ',0,0)'; + } + + return t; + } + + function filterImages(images) { + var showsPano = context.photos().showsPanoramic(); + var showsFlat = context.photos().showsFlat(); + var fromDate = context.photos().fromDate(); + var toDate = context.photos().toDate(); + + if (!showsPano || !showsFlat) { + images = images.filter(function (image) { + if (image.is_pano) return showsPano; + return showsFlat; + }); + } + + if (fromDate) { + images = images.filter(function (image) { + return new Date(image.captured_at).getTime() >= new Date(fromDate).getTime(); + }); + } + + if (toDate) { + images = images.filter(function (image) { + return new Date(image.captured_at).getTime() <= new Date(toDate).getTime(); + }); + } + + return images; + } + + function filterSequences(sequences) { + var showsPano = context.photos().showsPanoramic(); + var showsFlat = context.photos().showsFlat(); + var fromDate = context.photos().fromDate(); + var toDate = context.photos().toDate(); + + if (!showsPano || !showsFlat) { + sequences = sequences.filter(function (sequence) { + if (sequence.properties.hasOwnProperty('is_pano')) { + if (sequence.properties.is_pano) return showsPano; + return showsFlat; + } + + return false; + }); + } + + if (fromDate) { + sequences = sequences.filter(function (sequence) { + return new Date(sequence.properties.captured_at).getTime() >= new Date(fromDate).getTime().toString(); + }); + } + + if (toDate) { + sequences = sequences.filter(function (sequence) { + return new Date(sequence.properties.captured_at).getTime() <= new Date(toDate).getTime().toString(); + }); + } + + return sequences; + } + + function update() { + var z = ~~context.map().zoom(); + var showMarkers = z >= minMarkerZoom; + var showViewfields = z >= minViewfieldZoom; + var service = getService(); + var sequences = service ? service.sequences(projection) : []; + var images = service && showMarkers ? service.images(projection) : []; + images = filterImages(images); + sequences = filterSequences(sequences); + service.filterViewer(context); + var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) { + return d.properties.id; + }); // exit + + traces.exit().remove(); // enter/update + + traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson); + var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) { + return d.id; + }); // exit + + groups.exit().remove(); // enter + + var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click); + groupsEnter.append('g').attr('class', 'viewfield-scale'); // update + + var markers = groups.merge(groupsEnter).sort(function (a, b) { + return b.loc[1] - a.loc[1]; // sort Y + }).attr('transform', transform).select('.viewfield-scale'); + markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6'); + var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []); + viewfields.exit().remove(); + viewfields.enter() // viewfields may or may not be drawn... + .insert('path', 'circle') // but if they are, draw below the circles + .attr('class', 'viewfield').classed('pano', function () { + return this.parentNode.__data__.is_pano; + }).attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath); + + function viewfieldPath() { + if (this.parentNode.__data__.is_pano) { + return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0'; + } else { + return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'; + } + } + } + + function drawImages(selection) { + var enabled = svgMapillaryImages.enabled; + var service = getService(); + layer = selection.selectAll('.layer-mapillary').data(service ? [0] : []); + layer.exit().remove(); + var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary').style('display', enabled ? 'block' : 'none'); + layerEnter.append('g').attr('class', 'sequences'); + layerEnter.append('g').attr('class', 'markers'); + layer = layerEnter.merge(layer); + + if (enabled) { + if (service && ~~context.map().zoom() >= minZoom) { + editOn(); + update(); + service.loadImages(projection); + } else { + editOff(); + } + } + } + + drawImages.enabled = function (_) { + if (!arguments.length) return svgMapillaryImages.enabled; + svgMapillaryImages.enabled = _; + + if (svgMapillaryImages.enabled) { + showLayer(); + context.photos().on('change.mapillary_images', update); + } else { + hideLayer(); + context.photos().on('change.mapillary_images', null); + } + + dispatch.call('change'); + return this; + }; + + drawImages.supported = function () { + return !!getService(); + }; + + init(); + return drawImages; + } + + function svgMapillaryPosition(projection, context) { + var throttledRedraw = throttle(function () { + update(); + }, 1000); + + var minZoom = 12; + var minViewfieldZoom = 18; + var layer = select(null); + + var _mapillary; + + var viewerCompassAngle; + + function init() { + if (svgMapillaryPosition.initialized) return; // run once + + svgMapillaryPosition.initialized = true; + } + + function getService() { + if (services.mapillary && !_mapillary) { + _mapillary = services.mapillary; + + _mapillary.event.on('imageChanged', throttledRedraw); + + _mapillary.event.on('bearingChanged', function (e) { + viewerCompassAngle = e.bearing; + if (context.map().isTransformed()) return; + layer.selectAll('.viewfield-group.currentView').filter(function (d) { + return d.is_pano; + }).attr('transform', transform); + }); + } else if (!services.mapillary && _mapillary) { + _mapillary = null; + } + + return _mapillary; + } + + function editOn() { + layer.style('display', 'block'); + } + + function editOff() { + layer.selectAll('.viewfield-group').remove(); + layer.style('display', 'none'); + } + + function transform(d) { + var t = svgPointTransform(projection)(d); + + if (d.is_pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) { + t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)'; + } else if (d.ca) { + t += ' rotate(' + Math.floor(d.ca) + ',0,0)'; + } + + return t; + } + + function update() { + var z = ~~context.map().zoom(); + var showViewfields = z >= minViewfieldZoom; + var service = getService(); + var image = service && service.getActiveImage(); + var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(image ? [image] : [], function (d) { + return d.id; + }); // exit + + groups.exit().remove(); // enter + + var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group currentView highlighted'); + groupsEnter.append('g').attr('class', 'viewfield-scale'); // update + + var markers = groups.merge(groupsEnter).attr('transform', transform).select('.viewfield-scale'); + markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6'); + var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []); + viewfields.exit().remove(); + viewfields.enter().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'); + } + + function drawImages(selection) { + var service = getService(); + layer = selection.selectAll('.layer-mapillary-position').data(service ? [0] : []); + layer.exit().remove(); + var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary-position'); + layerEnter.append('g').attr('class', 'markers'); + layer = layerEnter.merge(layer); + + if (service && ~~context.map().zoom() >= minZoom) { + editOn(); + update(); + } else { + editOff(); + } + } + + drawImages.enabled = function () { + update(); + return this; + }; + + drawImages.supported = function () { + return !!getService(); + }; + + init(); + return drawImages; + } + + function svgMapillarySigns(projection, context, dispatch) { + var throttledRedraw = throttle(function () { + dispatch.call('change'); + }, 1000); + + var minZoom = 12; + var layer = select(null); + + var _mapillary; + + function init() { + if (svgMapillarySigns.initialized) return; // run once + + svgMapillarySigns.enabled = false; + svgMapillarySigns.initialized = true; + } + + function getService() { + if (services.mapillary && !_mapillary) { + _mapillary = services.mapillary; + + _mapillary.event.on('loadedSigns', throttledRedraw); + } else if (!services.mapillary && _mapillary) { + _mapillary = null; + } + + return _mapillary; + } + + function showLayer() { + var service = getService(); + if (!service) return; + service.loadSignResources(context); + editOn(); + } + + function hideLayer() { + throttledRedraw.cancel(); + editOff(); + } + + function editOn() { + layer.style('display', 'block'); + } + + function editOff() { + layer.selectAll('.icon-sign').remove(); + layer.style('display', 'none'); + } + + function click(d3_event, d) { + var service = getService(); + if (!service) return; + context.map().centerEase(d.loc); + var selectedImageId = service.getActiveImage() && service.getActiveImage().id; + service.getDetections(d.id).then(function (detections) { + if (detections.length) { + var imageId = detections[0].image.id; + + if (imageId === selectedImageId) { + service.highlightDetection(detections[0]).selectImage(context, imageId); + } else { + service.ensureViewerLoaded(context).then(function () { + service.highlightDetection(detections[0]).selectImage(context, imageId).showViewer(context); + }); + } + } + }); + } + + function filterData(detectedFeatures) { + var fromDate = context.photos().fromDate(); + var toDate = context.photos().toDate(); + + if (fromDate) { + var fromTimestamp = new Date(fromDate).getTime(); + detectedFeatures = detectedFeatures.filter(function (feature) { + return new Date(feature.last_seen_at).getTime() >= fromTimestamp; + }); + } + + if (toDate) { + var toTimestamp = new Date(toDate).getTime(); + detectedFeatures = detectedFeatures.filter(function (feature) { + return new Date(feature.first_seen_at).getTime() <= toTimestamp; + }); + } + + return detectedFeatures; + } + + function update() { + var service = getService(); + var data = service ? service.signs(projection) : []; + data = filterData(data); + var transform = svgPointTransform(projection); + var signs = layer.selectAll('.icon-sign').data(data, function (d) { + return d.id; + }); // exit + + signs.exit().remove(); // enter + + var enter = signs.enter().append('g').attr('class', 'icon-sign icon-detected').on('click', click); + enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) { + return '#' + d.value; + }); + enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update + + signs.merge(enter).attr('transform', transform); + } + + function drawSigns(selection) { + var enabled = svgMapillarySigns.enabled; + var service = getService(); + layer = selection.selectAll('.layer-mapillary-signs').data(service ? [0] : []); + layer.exit().remove(); + layer = layer.enter().append('g').attr('class', 'layer-mapillary-signs layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer); + + if (enabled) { + if (service && ~~context.map().zoom() >= minZoom) { + editOn(); + update(); + service.loadSigns(projection); + service.showSignDetections(true); + } else { + editOff(); + } + } else if (service) { + service.showSignDetections(false); + } + } + + drawSigns.enabled = function (_) { + if (!arguments.length) return svgMapillarySigns.enabled; + svgMapillarySigns.enabled = _; + + if (svgMapillarySigns.enabled) { + showLayer(); + context.photos().on('change.mapillary_signs', update); + } else { + hideLayer(); + context.photos().on('change.mapillary_signs', null); + } + + dispatch.call('change'); + return this; + }; + + drawSigns.supported = function () { + return !!getService(); + }; + + init(); + return drawSigns; + } + + function svgMapillaryMapFeatures(projection, context, dispatch) { + var throttledRedraw = throttle(function () { + dispatch.call('change'); + }, 1000); + + var minZoom = 12; + var layer = select(null); + + var _mapillary; + + function init() { + if (svgMapillaryMapFeatures.initialized) return; // run once + + svgMapillaryMapFeatures.enabled = false; + svgMapillaryMapFeatures.initialized = true; + } + + function getService() { + if (services.mapillary && !_mapillary) { + _mapillary = services.mapillary; + + _mapillary.event.on('loadedMapFeatures', throttledRedraw); + } else if (!services.mapillary && _mapillary) { + _mapillary = null; + } + + return _mapillary; + } + + function showLayer() { + var service = getService(); + if (!service) return; + service.loadObjectResources(context); + editOn(); + } + + function hideLayer() { + throttledRedraw.cancel(); + editOff(); + } + + function editOn() { + layer.style('display', 'block'); + } + + function editOff() { + layer.selectAll('.icon-map-feature').remove(); + layer.style('display', 'none'); + } + + function click(d3_event, d) { + var service = getService(); + if (!service) return; + context.map().centerEase(d.loc); + var selectedImageId = service.getActiveImage() && service.getActiveImage().id; + service.getDetections(d.id).then(function (detections) { + if (detections.length) { + var imageId = detections[0].image.id; + + if (imageId === selectedImageId) { + service.highlightDetection(detections[0]).selectImage(context, imageId); + } else { + service.ensureViewerLoaded(context).then(function () { + service.highlightDetection(detections[0]).selectImage(context, imageId).showViewer(context); + }); + } + } + }); + } + + function filterData(detectedFeatures) { + var fromDate = context.photos().fromDate(); + var toDate = context.photos().toDate(); + + if (fromDate) { + detectedFeatures = detectedFeatures.filter(function (feature) { + return new Date(feature.last_seen_at).getTime() >= new Date(fromDate).getTime(); + }); + } + + if (toDate) { + detectedFeatures = detectedFeatures.filter(function (feature) { + return new Date(feature.first_seen_at).getTime() <= new Date(toDate).getTime(); + }); + } + + return detectedFeatures; + } + + function update() { + var service = getService(); + var data = service ? service.mapFeatures(projection) : []; + data = filterData(data); + var transform = svgPointTransform(projection); + var mapFeatures = layer.selectAll('.icon-map-feature').data(data, function (d) { + return d.id; + }); // exit + + mapFeatures.exit().remove(); // enter + + var enter = mapFeatures.enter().append('g').attr('class', 'icon-map-feature icon-detected').on('click', click); + enter.append('title').text(function (d) { + var id = d.value.replace(/--/g, '.').replace(/-/g, '_'); + return _t('mapillary_map_features.' + id); + }); + enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) { + if (d.value === 'object--billboard') { + // no billboard icon right now, so use the advertisement icon + return '#object--sign--advertisement'; + } + + return '#' + d.value; + }); + enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update + + mapFeatures.merge(enter).attr('transform', transform); + } + + function drawMapFeatures(selection) { + var enabled = svgMapillaryMapFeatures.enabled; + var service = getService(); + layer = selection.selectAll('.layer-mapillary-map-features').data(service ? [0] : []); + layer.exit().remove(); + layer = layer.enter().append('g').attr('class', 'layer-mapillary-map-features layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer); + + if (enabled) { + if (service && ~~context.map().zoom() >= minZoom) { + editOn(); + update(); + service.loadMapFeatures(projection); + service.showFeatureDetections(true); + } else { + editOff(); + } + } else if (service) { + service.showFeatureDetections(false); + } + } + + drawMapFeatures.enabled = function (_) { + if (!arguments.length) return svgMapillaryMapFeatures.enabled; + svgMapillaryMapFeatures.enabled = _; + + if (svgMapillaryMapFeatures.enabled) { + showLayer(); + context.photos().on('change.mapillary_map_features', update); + } else { + hideLayer(); + context.photos().on('change.mapillary_map_features', null); + } + + dispatch.call('change'); + return this; + }; + + drawMapFeatures.supported = function () { + return !!getService(); + }; + + init(); + return drawMapFeatures; + } + + function svgOpenstreetcamImages(projection, context, dispatch) { + var throttledRedraw = throttle(function () { + dispatch.call('change'); + }, 1000); + + var minZoom = 12; + var minMarkerZoom = 16; + var minViewfieldZoom = 18; + var layer = select(null); + + var _openstreetcam; + + function init() { + if (svgOpenstreetcamImages.initialized) return; // run once + + svgOpenstreetcamImages.enabled = false; + svgOpenstreetcamImages.initialized = true; + } + + function getService() { + if (services.openstreetcam && !_openstreetcam) { + _openstreetcam = services.openstreetcam; + + _openstreetcam.event.on('loadedImages', throttledRedraw); + } else if (!services.openstreetcam && _openstreetcam) { + _openstreetcam = null; + } + + return _openstreetcam; + } + + function showLayer() { + var service = getService(); + if (!service) return; + editOn(); + layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () { + dispatch.call('change'); + }); + } + + function hideLayer() { + throttledRedraw.cancel(); + layer.transition().duration(250).style('opacity', 0).on('end', editOff); + } + + function editOn() { + layer.style('display', 'block'); + } + + function editOff() { + layer.selectAll('.viewfield-group').remove(); + layer.style('display', 'none'); + } + + function click(d3_event, d) { + var service = getService(); + if (!service) return; + service.ensureViewerLoaded(context).then(function () { + service.selectImage(context, d.key).showViewer(context); + }); + context.map().centerEase(d.loc); + } + + function mouseover(d3_event, d) { + var service = getService(); + if (service) service.setStyles(context, d); + } + + function mouseout() { + var service = getService(); + if (service) service.setStyles(context, null); + } + + function transform(d) { + var t = svgPointTransform(projection)(d); + + if (d.ca) { + t += ' rotate(' + Math.floor(d.ca) + ',0,0)'; + } + + return t; + } + + function filterImages(images) { + var fromDate = context.photos().fromDate(); + var toDate = context.photos().toDate(); + var usernames = context.photos().usernames(); + + if (fromDate) { + var fromTimestamp = new Date(fromDate).getTime(); + images = images.filter(function (item) { + return new Date(item.captured_at).getTime() >= fromTimestamp; + }); + } + + if (toDate) { + var toTimestamp = new Date(toDate).getTime(); + images = images.filter(function (item) { + return new Date(item.captured_at).getTime() <= toTimestamp; + }); + } + + if (usernames) { + images = images.filter(function (item) { + return usernames.indexOf(item.captured_by) !== -1; + }); + } + + return images; + } + + function filterSequences(sequences) { + var fromDate = context.photos().fromDate(); + var toDate = context.photos().toDate(); + var usernames = context.photos().usernames(); + + if (fromDate) { + var fromTimestamp = new Date(fromDate).getTime(); + sequences = sequences.filter(function (image) { + return new Date(image.properties.captured_at).getTime() >= fromTimestamp; + }); + } + + if (toDate) { + var toTimestamp = new Date(toDate).getTime(); + sequences = sequences.filter(function (image) { + return new Date(image.properties.captured_at).getTime() <= toTimestamp; + }); + } + + if (usernames) { + sequences = sequences.filter(function (image) { + return usernames.indexOf(image.properties.captured_by) !== -1; + }); + } + + return sequences; + } + + function update() { + var viewer = context.container().select('.photoviewer'); + var selected = viewer.empty() ? undefined : viewer.datum(); + var z = ~~context.map().zoom(); + var showMarkers = z >= minMarkerZoom; + var showViewfields = z >= minViewfieldZoom; + var service = getService(); + var sequences = []; + var images = []; + + if (context.photos().showsFlat()) { + sequences = service ? service.sequences(projection) : []; + images = service && showMarkers ? service.images(projection) : []; + sequences = filterSequences(sequences); + images = filterImages(images); + } + + var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) { + return d.properties.key; + }); // exit + + traces.exit().remove(); // enter/update + + traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson); + var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) { + return d.key; + }); // exit + + groups.exit().remove(); // enter + + var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click); + groupsEnter.append('g').attr('class', 'viewfield-scale'); // update + + var markers = groups.merge(groupsEnter).sort(function (a, b) { + return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1]; // sort Y + }).attr('transform', transform).select('.viewfield-scale'); + markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6'); + var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []); + viewfields.exit().remove(); + viewfields.enter() // viewfields may or may not be drawn... + .insert('path', 'circle') // but if they are, draw below the circles + .attr('class', 'viewfield').attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'); + } + + function drawImages(selection) { + var enabled = svgOpenstreetcamImages.enabled, + service = getService(); + layer = selection.selectAll('.layer-openstreetcam').data(service ? [0] : []); + layer.exit().remove(); + var layerEnter = layer.enter().append('g').attr('class', 'layer-openstreetcam').style('display', enabled ? 'block' : 'none'); + layerEnter.append('g').attr('class', 'sequences'); + layerEnter.append('g').attr('class', 'markers'); + layer = layerEnter.merge(layer); + + if (enabled) { + if (service && ~~context.map().zoom() >= minZoom) { + editOn(); + update(); + service.loadImages(projection); + } else { + editOff(); + } + } + } + + drawImages.enabled = function (_) { + if (!arguments.length) return svgOpenstreetcamImages.enabled; + svgOpenstreetcamImages.enabled = _; + + if (svgOpenstreetcamImages.enabled) { + showLayer(); + context.photos().on('change.openstreetcam_images', update); + } else { + hideLayer(); + context.photos().on('change.openstreetcam_images', null); + } + + dispatch.call('change'); + return this; + }; + + drawImages.supported = function () { + return !!getService(); + }; + + init(); + return drawImages; + } + + function svgOsm(projection, context, dispatch) { + var enabled = true; + + function drawOsm(selection) { + selection.selectAll('.layer-osm').data(['covered', 'areas', 'lines', 'points', 'labels']).enter().append('g').attr('class', function (d) { + return 'layer-osm ' + d; + }); + selection.selectAll('.layer-osm.points').selectAll('.points-group').data(['points', 'midpoints', 'vertices', 'turns']).enter().append('g').attr('class', function (d) { + return 'points-group ' + d; + }); + } + + function showLayer() { + var layer = context.surface().selectAll('.data-layer.osm'); + layer.interrupt(); + layer.classed('disabled', false).style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () { + dispatch.call('change'); + }); + } + + function hideLayer() { + var layer = context.surface().selectAll('.data-layer.osm'); + layer.interrupt(); + layer.transition().duration(250).style('opacity', 0).on('end interrupt', function () { + layer.classed('disabled', true); + dispatch.call('change'); + }); + } + + drawOsm.enabled = function (val) { + if (!arguments.length) return enabled; + enabled = val; + + if (enabled) { + showLayer(); + } else { + hideLayer(); + } + + dispatch.call('change'); + return this; + }; + + return drawOsm; + } + + var _notesEnabled = false; + + var _osmService; + + function svgNotes(projection, context, dispatch) { + if (!dispatch) { + dispatch = dispatch$8('change'); + } + + var throttledRedraw = throttle(function () { + dispatch.call('change'); + }, 1000); + + var minZoom = 12; + var touchLayer = select(null); + var drawLayer = select(null); + var _notesVisible = false; + + function markerPath(selection, klass) { + selection.attr('class', klass).attr('transform', 'translate(-8, -22)').attr('d', 'm17.5,0l-15,0c-1.37,0 -2.5,1.12 -2.5,2.5l0,11.25c0,1.37 1.12,2.5 2.5,2.5l3.75,0l0,3.28c0,0.38 0.43,0.6 0.75,0.37l4.87,-3.65l5.62,0c1.37,0 2.5,-1.12 2.5,-2.5l0,-11.25c0,-1.37 -1.12,-2.5 -2.5,-2.5z'); + } // Loosely-coupled osm service for fetching notes. + + + function getService() { + if (services.osm && !_osmService) { + _osmService = services.osm; + + _osmService.on('loadedNotes', throttledRedraw); + } else if (!services.osm && _osmService) { + _osmService = null; + } + + return _osmService; + } // Show the notes + + + function editOn() { + if (!_notesVisible) { + _notesVisible = true; + drawLayer.style('display', 'block'); + } + } // Immediately remove the notes and their touch targets + + + function editOff() { + if (_notesVisible) { + _notesVisible = false; + drawLayer.style('display', 'none'); + drawLayer.selectAll('.note').remove(); + touchLayer.selectAll('.note').remove(); + } + } // Enable the layer. This shows the notes and transitions them to visible. + + + function layerOn() { + editOn(); + drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () { + dispatch.call('change'); + }); + } // Disable the layer. This transitions the layer invisible and then hides the notes. + + + function layerOff() { + throttledRedraw.cancel(); + drawLayer.interrupt(); + touchLayer.selectAll('.note').remove(); + drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () { + editOff(); + dispatch.call('change'); + }); + } // Update the note markers + + + function updateMarkers() { + if (!_notesVisible || !_notesEnabled) return; + var service = getService(); + var selectedID = context.selectedNoteID(); + var data = service ? service.notes(projection) : []; + var getTransform = svgPointTransform(projection); // Draw markers.. + + var notes = drawLayer.selectAll('.note').data(data, function (d) { + return d.status + d.id; + }); // exit + + notes.exit().remove(); // enter + + var notesEnter = notes.enter().append('g').attr('class', function (d) { + return 'note note-' + d.id + ' ' + d.status; + }).classed('new', function (d) { + return d.id < 0; + }); + notesEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke'); + notesEnter.append('path').call(markerPath, 'shadow'); + notesEnter.append('use').attr('class', 'note-fill').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').attr('xlink:href', '#iD-icon-note'); + notesEnter.selectAll('.icon-annotation').data(function (d) { + return [d]; + }).enter().append('use').attr('class', 'icon-annotation').attr('width', '10px').attr('height', '10px').attr('x', '-3px').attr('y', '-19px').attr('xlink:href', function (d) { + if (d.id < 0) return '#iD-icon-plus'; + if (d.status === 'open') return '#iD-icon-close'; + return '#iD-icon-apply'; + }); // update + + notes.merge(notesEnter).sort(sortY).classed('selected', function (d) { + var mode = context.mode(); + var isMoving = mode && mode.id === 'drag-note'; // no shadows when dragging + + return !isMoving && d.id === selectedID; + }).attr('transform', getTransform); // Draw targets.. + + if (touchLayer.empty()) return; + var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; + var targets = touchLayer.selectAll('.note').data(data, function (d) { + return d.id; + }); // exit + + targets.exit().remove(); // enter/update + + targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) { + var newClass = d.id < 0 ? 'new' : ''; + return 'note target note-' + d.id + ' ' + fillClass + newClass; + }).attr('transform', getTransform); + + function sortY(a, b) { + if (a.id === selectedID) return 1; + if (b.id === selectedID) return -1; + return b.loc[1] - a.loc[1]; + } + } // Draw the notes layer and schedule loading notes and updating markers. + + + function drawNotes(selection) { + var service = getService(); + var surface = context.surface(); + + if (surface && !surface.empty()) { + touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers'); + } + + drawLayer = selection.selectAll('.layer-notes').data(service ? [0] : []); + drawLayer.exit().remove(); + drawLayer = drawLayer.enter().append('g').attr('class', 'layer-notes').style('display', _notesEnabled ? 'block' : 'none').merge(drawLayer); + + if (_notesEnabled) { + if (service && ~~context.map().zoom() >= minZoom) { + editOn(); + service.loadNotes(projection); + updateMarkers(); + } else { + editOff(); + } + } + } // Toggles the layer on and off + + + drawNotes.enabled = function (val) { + if (!arguments.length) return _notesEnabled; + _notesEnabled = val; + + if (_notesEnabled) { + layerOn(); + } else { + layerOff(); + + if (context.selectedNoteID()) { + context.enter(modeBrowse(context)); + } + } + + dispatch.call('change'); + return this; + }; + + return drawNotes; + } + + function svgTouch() { + function drawTouch(selection) { + selection.selectAll('.layer-touch').data(['areas', 'lines', 'points', 'turns', 'markers']).enter().append('g').attr('class', function (d) { + return 'layer-touch ' + d; + }); + } + + return drawTouch; + } + + function refresh(selection, node) { + var cr = node.getBoundingClientRect(); + var prop = [cr.width, cr.height]; + selection.property('__dimensions__', prop); + return prop; + } + + function utilGetDimensions(selection, force) { + if (!selection || selection.empty()) { + return [0, 0]; + } + + var node = selection.node(), + cached = selection.property('__dimensions__'); + return !cached || force ? refresh(selection, node) : cached; + } + function utilSetDimensions(selection, dimensions) { + if (!selection || selection.empty()) { + return selection; + } + + var node = selection.node(); + + if (dimensions === null) { + refresh(selection, node); + return selection; + } + + return selection.property('__dimensions__', [dimensions[0], dimensions[1]]).attr('width', dimensions[0]).attr('height', dimensions[1]); + } + + function svgLayers(projection, context) { + var dispatch = dispatch$8('change'); + var svg = select(null); + var _layers = [{ + id: 'osm', + layer: svgOsm(projection, context, dispatch) + }, { + id: 'notes', + layer: svgNotes(projection, context, dispatch) + }, { + id: 'data', + layer: svgData(projection, context, dispatch) + }, { + id: 'keepRight', + layer: svgKeepRight(projection, context, dispatch) + }, { + id: 'improveOSM', + layer: svgImproveOSM(projection, context, dispatch) + }, { + id: 'osmose', + layer: svgOsmose(projection, context, dispatch) + }, { + id: 'streetside', + layer: svgStreetside(projection, context, dispatch) + }, { + id: 'mapillary', + layer: svgMapillaryImages(projection, context, dispatch) + }, { + id: 'mapillary-position', + layer: svgMapillaryPosition(projection, context) + }, { + id: 'mapillary-map-features', + layer: svgMapillaryMapFeatures(projection, context, dispatch) + }, { + id: 'mapillary-signs', + layer: svgMapillarySigns(projection, context, dispatch) + }, { + id: 'openstreetcam', + layer: svgOpenstreetcamImages(projection, context, dispatch) + }, { + id: 'debug', + layer: svgDebug(projection, context) + }, { + id: 'geolocate', + layer: svgGeolocate(projection) + }, { + id: 'touch', + layer: svgTouch() + }]; + + function drawLayers(selection) { + svg = selection.selectAll('.surface').data([0]); + svg = svg.enter().append('svg').attr('class', 'surface').merge(svg); + var defs = svg.selectAll('.surface-defs').data([0]); + defs.enter().append('defs').attr('class', 'surface-defs'); + var groups = svg.selectAll('.data-layer').data(_layers); + groups.exit().remove(); + groups.enter().append('g').attr('class', function (d) { + return 'data-layer ' + d.id; + }).merge(groups).each(function (d) { + select(this).call(d.layer); + }); + } + + drawLayers.all = function () { + return _layers; + }; + + drawLayers.layer = function (id) { + var obj = _layers.find(function (o) { + return o.id === id; + }); + + return obj && obj.layer; + }; + + drawLayers.only = function (what) { + var arr = [].concat(what); + + var all = _layers.map(function (layer) { + return layer.id; + }); + + return drawLayers.remove(utilArrayDifference(all, arr)); + }; + + drawLayers.remove = function (what) { + var arr = [].concat(what); + arr.forEach(function (id) { + _layers = _layers.filter(function (o) { + return o.id !== id; + }); + }); + dispatch.call('change'); + return this; + }; + + drawLayers.add = function (what) { + var arr = [].concat(what); + arr.forEach(function (obj) { + if ('id' in obj && 'layer' in obj) { + _layers.push(obj); + } + }); + dispatch.call('change'); + return this; + }; + + drawLayers.dimensions = function (val) { + if (!arguments.length) return utilGetDimensions(svg); + utilSetDimensions(svg, val); + return this; + }; + + return utilRebind(drawLayers, dispatch, 'on'); + } + + function svgLines(projection, context) { + var detected = utilDetect(); + var highway_stack = { + motorway: 0, + motorway_link: 1, + trunk: 2, + trunk_link: 3, + primary: 4, + primary_link: 5, + secondary: 6, + tertiary: 7, + unclassified: 8, + residential: 9, + service: 10, + footway: 11 + }; + + function drawTargets(selection, graph, entities, filter) { + var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor '; + var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor '; + var getPath = svgPath(projection).geojson; + var activeID = context.activeID(); + var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways + + var data = { + targets: [], + nopes: [] + }; + entities.forEach(function (way) { + var features = svgSegmentWay(way, graph, activeID); + data.targets.push.apply(data.targets, features.passive); + data.nopes.push.apply(data.nopes, features.active); + }); // Targets allow hover and vertex snapping + + var targetData = data.targets.filter(getPath); + var targets = selection.selectAll('.line.target-allowed').filter(function (d) { + return filter(d.properties.entity); + }).data(targetData, function key(d) { + return d.id; + }); // exit + + targets.exit().remove(); + + var segmentWasEdited = function segmentWasEdited(d) { + var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes + + if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) { + return false; + } + + return d.properties.nodes.some(function (n) { + return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc); + }); + }; // enter/update + + + targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) { + return 'way line target target-allowed ' + targetClass + d.id; + }).classed('segment-edited', segmentWasEdited); // NOPE + + var nopeData = data.nopes.filter(getPath); + var nopes = selection.selectAll('.line.target-nope').filter(function (d) { + return filter(d.properties.entity); + }).data(nopeData, function key(d) { + return d.id; + }); // exit + + nopes.exit().remove(); // enter/update + + nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) { + return 'way line target target-nope ' + nopeClass + d.id; + }).classed('segment-edited', segmentWasEdited); + } + + function drawLines(selection, graph, entities, filter) { + var base = context.history().base(); + + function waystack(a, b) { + var selected = context.selectedIDs(); + var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0; + var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0; + + if (a.tags.highway) { + scoreA -= highway_stack[a.tags.highway]; + } + + if (b.tags.highway) { + scoreB -= highway_stack[b.tags.highway]; + } + + return scoreA - scoreB; + } + + function drawLineGroup(selection, klass, isSelected) { + // Note: Don't add `.selected` class in draw modes + var mode = context.mode(); + var isDrawing = mode && /^draw/.test(mode.id); + var selectedClass = !isDrawing && isSelected ? 'selected ' : ''; + var lines = selection.selectAll('path').filter(filter).data(getPathData(isSelected), osmEntity.key); + lines.exit().remove(); // Optimization: Call expensive TagClasses only on enter selection. This + // works because osmEntity.key is defined to include the entity v attribute. + + lines.enter().append('path').attr('class', function (d) { + var prefix = 'way line'; // if this line isn't styled by its own tags + + if (!d.hasInterestingTags()) { + var parentRelations = graph.parentRelations(d); + var parentMultipolygons = parentRelations.filter(function (relation) { + return relation.isMultipolygon(); + }); // and if it's a member of at least one multipolygon relation + + if (parentMultipolygons.length > 0 && // and only multipolygon relations + parentRelations.length === parentMultipolygons.length) { + // then fudge the classes to style this as an area edge + prefix = 'relation area'; + } + } + + var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : ''; + return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id; + }).classed('added', function (d) { + return !base.entities[d.id]; + }).classed('geometry-edited', function (d) { + return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes); + }).classed('retagged', function (d) { + return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags); + }).call(svgTagClasses()).merge(lines).sort(waystack).attr('d', getPath).call(svgTagClasses().tags(svgRelationMemberTags(graph))); + return selection; + } + + function getPathData(isSelected) { + return function () { + var layer = this.parentNode.__data__; + var data = pathdata[layer] || []; + return data.filter(function (d) { + if (isSelected) { + return context.selectedIDs().indexOf(d.id) !== -1; + } else { + return context.selectedIDs().indexOf(d.id) === -1; + } + }); + }; + } + + function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) { + var markergroup = layergroup.selectAll('g.' + groupclass).data([pathclass]); + markergroup = markergroup.enter().append('g').attr('class', groupclass).merge(markergroup); + var markers = markergroup.selectAll('path').filter(filter).data(function data() { + return groupdata[this.parentNode.__data__] || []; + }, function key(d) { + return [d.id, d.index]; + }); + markers.exit().remove(); + markers = markers.enter().append('path').attr('class', pathclass).merge(markers).attr('marker-mid', marker).attr('d', function (d) { + return d.d; + }); + + if (detected.ie) { + markers.each(function () { + this.parentNode.insertBefore(this, this); + }); + } + } + + var getPath = svgPath(projection, graph); + var ways = []; + var onewaydata = {}; + var sideddata = {}; + var oldMultiPolygonOuters = {}; + + for (var i = 0; i < entities.length; i++) { + var entity = entities[i]; + var outer = osmOldMultipolygonOuterMember(entity, graph); + + if (outer) { + ways.push(entity.mergeTags(outer.tags)); + oldMultiPolygonOuters[outer.id] = true; + } else if (entity.geometry(graph) === 'line') { + ways.push(entity); + } + } + + ways = ways.filter(getPath); + var pathdata = utilArrayGroupBy(ways, function (way) { + return way.layer(); + }); + Object.keys(pathdata).forEach(function (k) { + var v = pathdata[k]; + var onewayArr = v.filter(function (d) { + return d.isOneWay(); + }); + var onewaySegments = svgMarkerSegments(projection, graph, 35, function shouldReverse(entity) { + return entity.tags.oneway === '-1'; + }, function bothDirections(entity) { + return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating'; + }); + onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments)); + var sidedArr = v.filter(function (d) { + return d.isSided(); + }); + var sidedSegments = svgMarkerSegments(projection, graph, 30, function shouldReverse() { + return false; + }, function bothDirections() { + return false; + }); + sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments)); + }); + var covered = selection.selectAll('.layer-osm.covered'); // under areas + + var uncovered = selection.selectAll('.layer-osm.lines'); // over areas + + var touchLayer = selection.selectAll('.layer-touch.lines'); // Draw lines.. + + [covered, uncovered].forEach(function (selection) { + var range = selection === covered ? range$1(-10, 0) : range$1(0, 11); + var layergroup = selection.selectAll('g.layergroup').data(range); + layergroup = layergroup.enter().append('g').attr('class', function (d) { + return 'layergroup layer' + String(d); + }).merge(layergroup); + layergroup.selectAll('g.linegroup').data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted']).enter().append('g').attr('class', function (d) { + return 'linegroup line-' + d; + }); + layergroup.selectAll('g.line-shadow').call(drawLineGroup, 'shadow', false); + layergroup.selectAll('g.line-casing').call(drawLineGroup, 'casing', false); + layergroup.selectAll('g.line-stroke').call(drawLineGroup, 'stroke', false); + layergroup.selectAll('g.line-shadow-highlighted').call(drawLineGroup, 'shadow', true); + layergroup.selectAll('g.line-casing-highlighted').call(drawLineGroup, 'casing', true); + layergroup.selectAll('g.line-stroke-highlighted').call(drawLineGroup, 'stroke', true); + addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)'); + addMarkers(layergroup, 'sided', 'sidedgroup', sideddata, function marker(d) { + var category = graph.entity(d.id).sidednessIdentifier(); + return 'url(#ideditor-sided-marker-' + category + ')'; + }); + }); // Draw touch targets.. + + touchLayer.call(drawTargets, graph, ways, filter); + } + + return drawLines; + } + + function svgMidpoints(projection, context) { + var targetRadius = 8; + + function drawTargets(selection, graph, entities, filter) { + var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; + var getTransform = svgPointTransform(projection).geojson; + var data = entities.map(function (midpoint) { + return { + type: 'Feature', + id: midpoint.id, + properties: { + target: true, + entity: midpoint + }, + geometry: { + type: 'Point', + coordinates: midpoint.loc + } + }; + }); + var targets = selection.selectAll('.midpoint.target').filter(function (d) { + return filter(d.properties.entity); + }).data(data, function key(d) { + return d.id; + }); // exit + + targets.exit().remove(); // enter/update + + targets.enter().append('circle').attr('r', targetRadius).merge(targets).attr('class', function (d) { + return 'node midpoint target ' + fillClass + d.id; + }).attr('transform', getTransform); + } + + function drawMidpoints(selection, graph, entities, filter, extent) { + var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints'); + var touchLayer = selection.selectAll('.layer-touch.points'); + var mode = context.mode(); + + if (mode && mode.id !== 'select' || !context.map().withinEditableZoom()) { + drawLayer.selectAll('.midpoint').remove(); + touchLayer.selectAll('.midpoint.target').remove(); + return; + } + + var poly = extent.polygon(); + var midpoints = {}; + + for (var i = 0; i < entities.length; i++) { + var entity = entities[i]; + if (entity.type !== 'way') continue; + if (!filter(entity)) continue; + if (context.selectedIDs().indexOf(entity.id) < 0) continue; + var nodes = graph.childNodes(entity); + + for (var j = 0; j < nodes.length - 1; j++) { + var a = nodes[j]; + var b = nodes[j + 1]; + var id = [a.id, b.id].sort().join('-'); + + if (midpoints[id]) { + midpoints[id].parents.push(entity); + } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) { + var point = geoVecInterp(a.loc, b.loc, 0.5); + var loc = null; + + if (extent.intersects(point)) { + loc = point; + } else { + for (var k = 0; k < 4; k++) { + point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]); + + if (point && geoVecLength(projection(a.loc), projection(point)) > 20 && geoVecLength(projection(b.loc), projection(point)) > 20) { + loc = point; + break; + } + } + } + + if (loc) { + midpoints[id] = { + type: 'midpoint', + id: id, + loc: loc, + edge: [a.id, b.id], + parents: [entity] + }; + } + } + } + } + + function midpointFilter(d) { + if (midpoints[d.id]) return true; + + for (var i = 0; i < d.parents.length; i++) { + if (filter(d.parents[i])) { + return true; + } + } + + return false; + } + + var groups = drawLayer.selectAll('.midpoint').filter(midpointFilter).data(Object.values(midpoints), function (d) { + return d.id; + }); + groups.exit().remove(); + var enter = groups.enter().insert('g', ':first-child').attr('class', 'midpoint'); + enter.append('polygon').attr('points', '-6,8 10,0 -6,-8').attr('class', 'shadow'); + enter.append('polygon').attr('points', '-3,4 5,0 -3,-4').attr('class', 'fill'); + groups = groups.merge(enter).attr('transform', function (d) { + var translate = svgPointTransform(projection); + var a = graph.entity(d.edge[0]); + var b = graph.entity(d.edge[1]); + var angle = geoAngle(a, b, projection) * (180 / Math.PI); + return translate(d) + ' rotate(' + angle + ')'; + }).call(svgTagClasses().tags(function (d) { + return d.parents[0].tags; + })); // Propagate data bindings. + + groups.select('polygon.shadow'); + groups.select('polygon.fill'); // Draw touch targets.. + + touchLayer.call(drawTargets, graph, Object.values(midpoints), midpointFilter); + } + + return drawMidpoints; + } + + function svgPoints(projection, context) { + function markerPath(selection, klass) { + selection.attr('class', klass).attr('transform', 'translate(-8, -23)').attr('d', 'M 17,8 C 17,13 11,21 8.5,23.5 C 6,21 0,13 0,8 C 0,4 4,-0.5 8.5,-0.5 C 13,-0.5 17,4 17,8 z'); + } + + function sortY(a, b) { + return b.loc[1] - a.loc[1]; + } // Avoid exit/enter if we're just moving stuff around. + // The node will get a new version but we only need to run the update selection. + + + function fastEntityKey(d) { + var mode = context.mode(); + var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id); + return isMoving ? d.id : osmEntity.key(d); + } + + function drawTargets(selection, graph, entities, filter) { + var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; + var getTransform = svgPointTransform(projection).geojson; + var activeID = context.activeID(); + var data = []; + entities.forEach(function (node) { + if (activeID === node.id) return; // draw no target on the activeID + + data.push({ + type: 'Feature', + id: node.id, + properties: { + target: true, + entity: node + }, + geometry: node.asGeoJSON() + }); + }); + var targets = selection.selectAll('.point.target').filter(function (d) { + return filter(d.properties.entity); + }).data(data, function key(d) { + return d.id; + }); // exit + + targets.exit().remove(); // enter/update + + targets.enter().append('rect').attr('x', -10).attr('y', -26).attr('width', 20).attr('height', 30).merge(targets).attr('class', function (d) { + return 'node point target ' + fillClass + d.id; + }).attr('transform', getTransform); + } + + function drawPoints(selection, graph, entities, filter) { + var wireframe = context.surface().classed('fill-wireframe'); + var zoom = geoScaleToZoom(projection.scale()); + var base = context.history().base(); // Points with a direction will render as vertices at higher zooms.. + + function renderAsPoint(entity) { + return entity.geometry(graph) === 'point' && !(zoom >= 18 && entity.directions(graph, projection).length); + } // All points will render as vertices in wireframe mode too.. + + + var points = wireframe ? [] : entities.filter(renderAsPoint); + points.sort(sortY); + var drawLayer = selection.selectAll('.layer-osm.points .points-group.points'); + var touchLayer = selection.selectAll('.layer-touch.points'); // Draw points.. + + var groups = drawLayer.selectAll('g.point').filter(filter).data(points, fastEntityKey); + groups.exit().remove(); + var enter = groups.enter().append('g').attr('class', function (d) { + return 'node point ' + d.id; + }).order(); + enter.append('path').call(markerPath, 'shadow'); + enter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke'); + enter.append('path').call(markerPath, 'stroke'); + enter.append('use').attr('transform', 'translate(-5, -19)').attr('class', 'icon').attr('width', '11px').attr('height', '11px'); + groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('added', function (d) { + return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new + }).classed('moved', function (d) { + return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc); + }).classed('retagged', function (d) { + return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags); + }).call(svgTagClasses()); + groups.select('.shadow'); // propagate bound data + + groups.select('.stroke'); // propagate bound data + + groups.select('.icon') // propagate bound data + .attr('xlink:href', function (entity) { + var preset = _mainPresetIndex.match(entity, graph); + var picon = preset && preset.icon; + + if (!picon) { + return ''; + } else { + var isMaki = /^maki-/.test(picon); + return '#' + picon + (isMaki ? '-11' : ''); + } + }); // Draw touch targets.. + + touchLayer.call(drawTargets, graph, points, filter); + } + + return drawPoints; + } + + function svgTurns(projection, context) { + function icon(turn) { + var u = turn.u ? '-u' : ''; + if (turn.no) return '#iD-turn-no' + u; + if (turn.only) return '#iD-turn-only' + u; + return '#iD-turn-yes' + u; + } + + function drawTurns(selection, graph, turns) { + function turnTransform(d) { + var pxRadius = 50; + var toWay = graph.entity(d.to.way); + var toPoints = graph.childNodes(toWay).map(function (n) { + return n.loc; + }).map(projection); + var toLength = geoPathLength(toPoints); + var mid = toLength / 2; // midpoint of destination way + + var toNode = graph.entity(d.to.node); + var toVertex = graph.entity(d.to.vertex); + var a = geoAngle(toVertex, toNode, projection); + var o = projection(toVertex.loc); + var r = d.u ? 0 // u-turn: no radius + : !toWay.__via ? pxRadius // leaf way: put marker at pxRadius + : Math.min(mid, pxRadius); // via way: prefer pxRadius, fallback to mid for very short ways + + return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' + 'rotate(' + a * 180 / Math.PI + ')'; + } + + var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns'); + var touchLayer = selection.selectAll('.layer-touch.turns'); // Draw turns.. + + var groups = drawLayer.selectAll('g.turn').data(turns, function (d) { + return d.key; + }); // exit + + groups.exit().remove(); // enter + + var groupsEnter = groups.enter().append('g').attr('class', function (d) { + return 'turn ' + d.key; + }); + var turnsEnter = groupsEnter.filter(function (d) { + return !d.u; + }); + turnsEnter.append('rect').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24'); + turnsEnter.append('use').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24'); + var uEnter = groupsEnter.filter(function (d) { + return d.u; + }); + uEnter.append('circle').attr('r', '16'); + uEnter.append('use').attr('transform', 'translate(-16, -16)').attr('width', '32').attr('height', '32'); // update + + groups = groups.merge(groupsEnter).attr('opacity', function (d) { + return d.direct === false ? '0.7' : null; + }).attr('transform', turnTransform); + groups.select('use').attr('xlink:href', icon); + groups.select('rect'); // propagate bound data + + groups.select('circle'); // propagate bound data + // Draw touch targets.. + + var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; + groups = touchLayer.selectAll('g.turn').data(turns, function (d) { + return d.key; + }); // exit + + groups.exit().remove(); // enter + + groupsEnter = groups.enter().append('g').attr('class', function (d) { + return 'turn ' + d.key; + }); + turnsEnter = groupsEnter.filter(function (d) { + return !d.u; + }); + turnsEnter.append('rect').attr('class', 'target ' + fillClass).attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24'); + uEnter = groupsEnter.filter(function (d) { + return d.u; + }); + uEnter.append('circle').attr('class', 'target ' + fillClass).attr('r', '16'); // update + + groups = groups.merge(groupsEnter).attr('transform', turnTransform); + groups.select('rect'); // propagate bound data + + groups.select('circle'); // propagate bound data + + return this; + } + + return drawTurns; + } + + function svgVertices(projection, context) { + var radiuses = { + // z16-, z17, z18+, w/icon + shadow: [6, 7.5, 7.5, 12], + stroke: [2.5, 3.5, 3.5, 8], + fill: [1, 1.5, 1.5, 1.5] + }; + + var _currHoverTarget; + + var _currPersistent = {}; + var _currHover = {}; + var _prevHover = {}; + var _currSelected = {}; + var _prevSelected = {}; + var _radii = {}; + + function sortY(a, b) { + return b.loc[1] - a.loc[1]; + } // Avoid exit/enter if we're just moving stuff around. + // The node will get a new version but we only need to run the update selection. + + + function fastEntityKey(d) { + var mode = context.mode(); + var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id); + return isMoving ? d.id : osmEntity.key(d); + } + + function draw(selection, graph, vertices, sets, filter) { + sets = sets || { + selected: {}, + important: {}, + hovered: {} + }; + var icons = {}; + var directions = {}; + var wireframe = context.surface().classed('fill-wireframe'); + var zoom = geoScaleToZoom(projection.scale()); + var z = zoom < 17 ? 0 : zoom < 18 ? 1 : 2; + var activeID = context.activeID(); + var base = context.history().base(); + + function getIcon(d) { + // always check latest entity, as fastEntityKey avoids enter/exit now + var entity = graph.entity(d.id); + if (entity.id in icons) return icons[entity.id]; + icons[entity.id] = entity.hasInterestingTags() && _mainPresetIndex.match(entity, graph).icon; + return icons[entity.id]; + } // memoize directions results, return false for empty arrays (for use in filter) + + + function getDirections(entity) { + if (entity.id in directions) return directions[entity.id]; + var angles = entity.directions(graph, projection); + directions[entity.id] = angles.length ? angles : false; + return angles; + } + + function updateAttributes(selection) { + ['shadow', 'stroke', 'fill'].forEach(function (klass) { + var rads = radiuses[klass]; + selection.selectAll('.' + klass).each(function (entity) { + var i = z && getIcon(entity); + var r = rads[i ? 3 : z]; // slightly increase the size of unconnected endpoints #3775 + + if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) { + r += 1.5; + } + + if (klass === 'shadow') { + // remember this value, so we don't need to + _radii[entity.id] = r; // recompute it when we draw the touch targets + } + + select(this).attr('r', r).attr('visibility', i && klass === 'fill' ? 'hidden' : null); + }); + }); + } + + vertices.sort(sortY); + var groups = selection.selectAll('g.vertex').filter(filter).data(vertices, fastEntityKey); // exit + + groups.exit().remove(); // enter + + var enter = groups.enter().append('g').attr('class', function (d) { + return 'node vertex ' + d.id; + }).order(); + enter.append('circle').attr('class', 'shadow'); + enter.append('circle').attr('class', 'stroke'); // Vertices with tags get a fill. + + enter.filter(function (d) { + return d.hasInterestingTags(); + }).append('circle').attr('class', 'fill'); // update + + groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('sibling', function (d) { + return d.id in sets.selected; + }).classed('shared', function (d) { + return graph.isShared(d); + }).classed('endpoint', function (d) { + return d.isEndpoint(graph); + }).classed('added', function (d) { + return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new + }).classed('moved', function (d) { + return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc); + }).classed('retagged', function (d) { + return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags); + }).call(updateAttributes); // Vertices with icons get a `use`. + + var iconUse = groups.selectAll('.icon').data(function data(d) { + return zoom >= 17 && getIcon(d) ? [d] : []; + }, fastEntityKey); // exit + + iconUse.exit().remove(); // enter + + iconUse.enter().append('use').attr('class', 'icon').attr('width', '11px').attr('height', '11px').attr('transform', 'translate(-5.5, -5.5)').attr('xlink:href', function (d) { + var picon = getIcon(d); + var isMaki = /^maki-/.test(picon); + return '#' + picon + (isMaki ? '-11' : ''); + }); // Vertices with directions get viewfields + + var dgroups = groups.selectAll('.viewfieldgroup').data(function data(d) { + return zoom >= 18 && getDirections(d) ? [d] : []; + }, fastEntityKey); // exit + + dgroups.exit().remove(); // enter/update + + dgroups = dgroups.enter().insert('g', '.shadow').attr('class', 'viewfieldgroup').merge(dgroups); + var viewfields = dgroups.selectAll('.viewfield').data(getDirections, function key(d) { + return osmEntity.key(d); + }); // exit + + viewfields.exit().remove(); // enter/update + + viewfields.enter().append('path').attr('class', 'viewfield').attr('d', 'M0,0H0').merge(viewfields).attr('marker-start', 'url(#ideditor-viewfield-marker' + (wireframe ? '-wireframe' : '') + ')').attr('transform', function (d) { + return 'rotate(' + d + ')'; + }); + } + + function drawTargets(selection, graph, entities, filter) { + var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor '; + var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor '; + var getTransform = svgPointTransform(projection).geojson; + var activeID = context.activeID(); + var data = { + targets: [], + nopes: [] + }; + entities.forEach(function (node) { + if (activeID === node.id) return; // draw no target on the activeID + + var vertexType = svgPassiveVertex(node, graph, activeID); + + if (vertexType !== 0) { + // passive or adjacent - allow to connect + data.targets.push({ + type: 'Feature', + id: node.id, + properties: { + target: true, + entity: node + }, + geometry: node.asGeoJSON() + }); + } else { + data.nopes.push({ + type: 'Feature', + id: node.id + '-nope', + properties: { + nope: true, + target: true, + entity: node + }, + geometry: node.asGeoJSON() + }); + } + }); // Targets allow hover and vertex snapping + + var targets = selection.selectAll('.vertex.target-allowed').filter(function (d) { + return filter(d.properties.entity); + }).data(data.targets, function key(d) { + return d.id; + }); // exit + + targets.exit().remove(); // enter/update + + targets.enter().append('circle').attr('r', function (d) { + return _radii[d.id] || radiuses.shadow[3]; + }).merge(targets).attr('class', function (d) { + return 'node vertex target target-allowed ' + targetClass + d.id; + }).attr('transform', getTransform); // NOPE + + var nopes = selection.selectAll('.vertex.target-nope').filter(function (d) { + return filter(d.properties.entity); + }).data(data.nopes, function key(d) { + return d.id; + }); // exit + + nopes.exit().remove(); // enter/update + + nopes.enter().append('circle').attr('r', function (d) { + return _radii[d.properties.entity.id] || radiuses.shadow[3]; + }).merge(nopes).attr('class', function (d) { + return 'node vertex target target-nope ' + nopeClass + d.id; + }).attr('transform', getTransform); + } // Points can also render as vertices: + // 1. in wireframe mode or + // 2. at higher zooms if they have a direction + + + function renderAsVertex(entity, graph, wireframe, zoom) { + var geometry = entity.geometry(graph); + return geometry === 'vertex' || geometry === 'point' && (wireframe || zoom >= 18 && entity.directions(graph, projection).length); + } + + function isEditedNode(node, base, head) { + var baseNode = base.entities[node.id]; + var headNode = head.entities[node.id]; + return !headNode || !baseNode || !fastDeepEqual(headNode.tags, baseNode.tags) || !fastDeepEqual(headNode.loc, baseNode.loc); + } + + function getSiblingAndChildVertices(ids, graph, wireframe, zoom) { + var results = {}; + var seenIds = {}; + + function addChildVertices(entity) { + // avoid redundant work and infinite recursion of circular relations + if (seenIds[entity.id]) return; + seenIds[entity.id] = true; + var geometry = entity.geometry(graph); + + if (!context.features().isHiddenFeature(entity, graph, geometry)) { + var i; + + if (entity.type === 'way') { + for (i = 0; i < entity.nodes.length; i++) { + var child = graph.hasEntity(entity.nodes[i]); + + if (child) { + addChildVertices(child); + } + } + } else if (entity.type === 'relation') { + for (i = 0; i < entity.members.length; i++) { + var member = graph.hasEntity(entity.members[i].id); + + if (member) { + addChildVertices(member); + } + } + } else if (renderAsVertex(entity, graph, wireframe, zoom)) { + results[entity.id] = entity; + } + } + } + + ids.forEach(function (id) { + var entity = graph.hasEntity(id); + if (!entity) return; + + if (entity.type === 'node') { + if (renderAsVertex(entity, graph, wireframe, zoom)) { + results[entity.id] = entity; + graph.parentWays(entity).forEach(function (entity) { + addChildVertices(entity); + }); + } + } else { + // way, relation + addChildVertices(entity); + } + }); + return results; + } + + function drawVertices(selection, graph, entities, filter, extent, fullRedraw) { + var wireframe = context.surface().classed('fill-wireframe'); + var visualDiff = context.surface().classed('highlight-edited'); + var zoom = geoScaleToZoom(projection.scale()); + var mode = context.mode(); + var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id); + var base = context.history().base(); + var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices'); + var touchLayer = selection.selectAll('.layer-touch.points'); + + if (fullRedraw) { + _currPersistent = {}; + _radii = {}; + } // Collect important vertices from the `entities` list.. + // (during a partial redraw, it will not contain everything) + + + for (var i = 0; i < entities.length; i++) { + var entity = entities[i]; + var geometry = entity.geometry(graph); + var keep = false; // a point that looks like a vertex.. + + if (geometry === 'point' && renderAsVertex(entity, graph, wireframe, zoom)) { + _currPersistent[entity.id] = entity; + keep = true; // a vertex of some importance.. + } else if (geometry === 'vertex' && (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || visualDiff && isEditedNode(entity, base, graph))) { + _currPersistent[entity.id] = entity; + keep = true; + } // whatever this is, it's not a persistent vertex.. + + + if (!keep && !fullRedraw) { + delete _currPersistent[entity.id]; + } + } // 3 sets of vertices to consider: + + + var sets = { + persistent: _currPersistent, + // persistent = important vertices (render always) + selected: _currSelected, + // selected + siblings of selected (render always) + hovered: _currHover // hovered + siblings of hovered (render only in draw modes) + + }; + var all = Object.assign({}, isMoving ? _currHover : {}, _currSelected, _currPersistent); // Draw the vertices.. + // The filter function controls the scope of what objects d3 will touch (exit/enter/update) + // Adjust the filter function to expand the scope beyond whatever entities were passed in. + + var filterRendered = function filterRendered(d) { + return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d); + }; + + drawLayer.call(draw, graph, currentVisible(all), sets, filterRendered); // Draw touch targets.. + // When drawing, render all targets (not just those affected by a partial redraw) + + var filterTouch = function filterTouch(d) { + return isMoving ? true : filterRendered(d); + }; + + touchLayer.call(drawTargets, graph, currentVisible(all), filterTouch); + + function currentVisible(which) { + return Object.keys(which).map(graph.hasEntity, graph) // the current version of this entity + .filter(function (entity) { + return entity && entity.intersects(extent, graph); + }); + } + } // partial redraw - only update the selected items.. + + + drawVertices.drawSelected = function (selection, graph, extent) { + var wireframe = context.surface().classed('fill-wireframe'); + var zoom = geoScaleToZoom(projection.scale()); + _prevSelected = _currSelected || {}; + + if (context.map().isInWideSelection()) { + _currSelected = {}; + context.selectedIDs().forEach(function (id) { + var entity = graph.hasEntity(id); + if (!entity) return; + + if (entity.type === 'node') { + if (renderAsVertex(entity, graph, wireframe, zoom)) { + _currSelected[entity.id] = entity; + } + } + }); + } else { + _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom); + } // note that drawVertices will add `_currSelected` automatically if needed.. + + + var filter = function filter(d) { + return d.id in _prevSelected; + }; + + drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false); + }; // partial redraw - only update the hovered items.. + + + drawVertices.drawHover = function (selection, graph, target, extent) { + if (target === _currHoverTarget) return; // continue only if something changed + + var wireframe = context.surface().classed('fill-wireframe'); + var zoom = geoScaleToZoom(projection.scale()); + _prevHover = _currHover || {}; + _currHoverTarget = target; + var entity = target && target.properties && target.properties.entity; + + if (entity) { + _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom); + } else { + _currHover = {}; + } // note that drawVertices will add `_currHover` automatically if needed.. + + + var filter = function filter(d) { + return d.id in _prevHover; + }; + + drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false); + }; + + return drawVertices; + } + + function utilBindOnce(target, type, listener, capture) { + var typeOnce = type + '.once'; + + function one() { + target.on(typeOnce, null); + listener.apply(this, arguments); + } + + target.on(typeOnce, one, capture); + return this; + } + + function defaultFilter(d3_event) { + return !d3_event.ctrlKey && !d3_event.button; + } + + function defaultExtent() { + var e = this; + + if (e instanceof SVGElement) { + e = e.ownerSVGElement || e; + + if (e.hasAttribute('viewBox')) { + e = e.viewBox.baseVal; + return [[e.x, e.y], [e.x + e.width, e.y + e.height]]; + } + + return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]]; + } + + return [[0, 0], [e.clientWidth, e.clientHeight]]; + } + + function defaultWheelDelta(d3_event) { + return -d3_event.deltaY * (d3_event.deltaMode === 1 ? 0.05 : d3_event.deltaMode ? 1 : 0.002); + } + + function defaultConstrain(transform, extent, translateExtent) { + var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0], + dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0], + dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1], + dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1]; + return transform.translate(dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1), dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)); + } + + function utilZoomPan() { + var filter = defaultFilter, + extent = defaultExtent, + constrain = defaultConstrain, + wheelDelta = defaultWheelDelta, + scaleExtent = [0, Infinity], + translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], + interpolate = interpolateZoom, + dispatch = dispatch$8('start', 'zoom', 'end'), + _wheelDelay = 150, + _transform = identity$2, + _activeGesture; + + function zoom(selection) { + selection.on('pointerdown.zoom', pointerdown).on('wheel.zoom', wheeled).style('touch-action', 'none').style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)'); + select(window).on('pointermove.zoompan', pointermove).on('pointerup.zoompan pointercancel.zoompan', pointerup); + } + + zoom.transform = function (collection, transform, point) { + var selection = collection.selection ? collection.selection() : collection; + + if (collection !== selection) { + schedule(collection, transform, point); + } else { + selection.interrupt().each(function () { + gesture(this, arguments).start(null).zoom(null, null, typeof transform === 'function' ? transform.apply(this, arguments) : transform).end(null); + }); + } + }; + + zoom.scaleBy = function (selection, k, p) { + zoom.scaleTo(selection, function () { + var k0 = _transform.k, + k1 = typeof k === 'function' ? k.apply(this, arguments) : k; + return k0 * k1; + }, p); + }; + + zoom.scaleTo = function (selection, k, p) { + zoom.transform(selection, function () { + var e = extent.apply(this, arguments), + t0 = _transform, + p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p, + p1 = t0.invert(p0), + k1 = typeof k === 'function' ? k.apply(this, arguments) : k; + return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent); + }, p); + }; + + zoom.translateBy = function (selection, x, y) { + zoom.transform(selection, function () { + return constrain(_transform.translate(typeof x === 'function' ? x.apply(this, arguments) : x, typeof y === 'function' ? y.apply(this, arguments) : y), extent.apply(this, arguments), translateExtent); + }); + }; + + zoom.translateTo = function (selection, x, y, p) { + zoom.transform(selection, function () { + var e = extent.apply(this, arguments), + t = _transform, + p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p; + return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate(typeof x === 'function' ? -x.apply(this, arguments) : -x, typeof y === 'function' ? -y.apply(this, arguments) : -y), e, translateExtent); + }, p); + }; + + function scale(transform, k) { + k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k)); + return k === transform.k ? transform : new Transform(k, transform.x, transform.y); + } + + function translate(transform, p0, p1) { + var x = p0[0] - p1[0] * transform.k, + y = p0[1] - p1[1] * transform.k; + return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y); + } + + function centroid(extent) { + return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2]; + } + + function schedule(transition, transform, point) { + transition.on('start.zoom', function () { + gesture(this, arguments).start(null); + }).on('interrupt.zoom end.zoom', function () { + gesture(this, arguments).end(null); + }).tween('zoom', function () { + var that = this, + args = arguments, + g = gesture(that, args), + e = extent.apply(that, args), + p = !point ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point, + w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), + a = _transform, + b = typeof transform === 'function' ? transform.apply(that, args) : transform, + i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k)); + return function (t) { + if (t === 1) { + // Avoid rounding error on end. + t = b; + } else { + var l = i(t); + var k = w / l[2]; + t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); + } + + g.zoom(null, null, t); + }; + }); + } + + function gesture(that, args, clean) { + return !clean && _activeGesture || new Gesture(that, args); + } + + function Gesture(that, args) { + this.that = that; + this.args = args; + this.active = 0; + this.extent = extent.apply(that, args); + } + + Gesture.prototype = { + start: function start(d3_event) { + if (++this.active === 1) { + _activeGesture = this; + dispatch.call('start', this, d3_event); + } + + return this; + }, + zoom: function zoom(d3_event, key, transform) { + if (this.mouse && key !== 'mouse') this.mouse[1] = transform.invert(this.mouse[0]); + if (this.pointer0 && key !== 'touch') this.pointer0[1] = transform.invert(this.pointer0[0]); + if (this.pointer1 && key !== 'touch') this.pointer1[1] = transform.invert(this.pointer1[0]); + _transform = transform; + dispatch.call('zoom', this, d3_event, key, transform); + return this; + }, + end: function end(d3_event) { + if (--this.active === 0) { + _activeGesture = null; + dispatch.call('end', this, d3_event); + } + + return this; + } + }; + + function wheeled(d3_event) { + if (!filter.apply(this, arguments)) return; + var g = gesture(this, arguments), + t = _transform, + k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))), + p = utilFastMouse(this)(d3_event); // If the mouse is in the same location as before, reuse it. + // If there were recent wheel events, reset the wheel idle timeout. + + if (g.wheel) { + if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) { + g.mouse[1] = t.invert(g.mouse[0] = p); + } + + clearTimeout(g.wheel); // Otherwise, capture the mouse point and location at the start. + } else { + g.mouse = [p, t.invert(p)]; + interrupt(this); + g.start(d3_event); + } + + d3_event.preventDefault(); + d3_event.stopImmediatePropagation(); + g.wheel = setTimeout(wheelidled, _wheelDelay); + g.zoom(d3_event, 'mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent)); + + function wheelidled() { + g.wheel = null; + g.end(d3_event); + } + } + + var _downPointerIDs = new Set(); + + var _pointerLocGetter; + + function pointerdown(d3_event) { + _downPointerIDs.add(d3_event.pointerId); + + if (!filter.apply(this, arguments)) return; + var g = gesture(this, arguments, _downPointerIDs.size === 1); + var started; + d3_event.stopImmediatePropagation(); + _pointerLocGetter = utilFastMouse(this); + + var loc = _pointerLocGetter(d3_event); + + var p = [loc, _transform.invert(loc), d3_event.pointerId]; + + if (!g.pointer0) { + g.pointer0 = p; + started = true; + } else if (!g.pointer1 && g.pointer0[2] !== p[2]) { + g.pointer1 = p; + } + + if (started) { + interrupt(this); + g.start(d3_event); + } + } + + function pointermove(d3_event) { + if (!_downPointerIDs.has(d3_event.pointerId)) return; + if (!_activeGesture || !_pointerLocGetter) return; + var g = gesture(this, arguments); + var isPointer0 = g.pointer0 && g.pointer0[2] === d3_event.pointerId; + var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === d3_event.pointerId; + + if ((isPointer0 || isPointer1) && 'buttons' in d3_event && !d3_event.buttons) { + // The pointer went up without ending the gesture somehow, e.g. + // a down mouse was moved off the map and released. End it here. + if (g.pointer0) _downPointerIDs["delete"](g.pointer0[2]); + if (g.pointer1) _downPointerIDs["delete"](g.pointer1[2]); + g.end(d3_event); + return; + } + + d3_event.preventDefault(); + d3_event.stopImmediatePropagation(); + + var loc = _pointerLocGetter(d3_event); + + var t, p, l; + if (isPointer0) g.pointer0[0] = loc;else if (isPointer1) g.pointer1[0] = loc; + t = _transform; + + if (g.pointer1) { + var p0 = g.pointer0[0], + l0 = g.pointer0[1], + p1 = g.pointer1[0], + l1 = g.pointer1[1], + dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp, + dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl; + t = scale(t, Math.sqrt(dp / dl)); + p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2]; + l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2]; + } else if (g.pointer0) { + p = g.pointer0[0]; + l = g.pointer0[1]; + } else { + return; + } + + g.zoom(d3_event, 'touch', constrain(translate(t, p, l), g.extent, translateExtent)); + } + + function pointerup(d3_event) { + if (!_downPointerIDs.has(d3_event.pointerId)) return; + + _downPointerIDs["delete"](d3_event.pointerId); + + if (!_activeGesture) return; + var g = gesture(this, arguments); + d3_event.stopImmediatePropagation(); + 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; + + if (g.pointer1 && !g.pointer0) { + g.pointer0 = g.pointer1; + delete g.pointer1; + } + + if (g.pointer0) { + g.pointer0[1] = _transform.invert(g.pointer0[0]); + } else { + g.end(d3_event); + } + } + + zoom.wheelDelta = function (_) { + return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta; + }; + + zoom.filter = function (_) { + return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter; + }; + + zoom.extent = function (_) { + return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent; + }; + + zoom.scaleExtent = function (_) { + return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]]; + }; + + zoom.translateExtent = function (_) { + return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]]; + }; + + zoom.constrain = function (_) { + return arguments.length ? (constrain = _, zoom) : constrain; + }; + + zoom.interpolate = function (_) { + return arguments.length ? (interpolate = _, zoom) : interpolate; + }; + + zoom._transform = function (_) { + return arguments.length ? (_transform = _, zoom) : _transform; + }; + + return utilRebind(zoom, dispatch, 'on'); + } + + // if pointer events are supported. Falls back to default `dblclick` event. + + function utilDoubleUp() { + var dispatch = dispatch$8('doubleUp'); + var _maxTimespan = 500; // milliseconds + + var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices + + var _pointer; // object representing the pointer that could trigger double up + + + function pointerIsValidFor(loc) { + // second pointerup must occur within a small timeframe after the first pointerdown + return new Date().getTime() - _pointer.startTime <= _maxTimespan && // all pointer events must occur within a small distance of the first pointerdown + geoVecLength(_pointer.startLoc, loc) <= _maxDistance; + } + + function pointerdown(d3_event) { + // ignore right-click + if (d3_event.ctrlKey || d3_event.button === 2) return; + var loc = [d3_event.clientX, d3_event.clientY]; // Don't rely on pointerId here since it can change between pointerdown + // events on touch devices + + if (_pointer && !pointerIsValidFor(loc)) { + // if this pointer is no longer valid, clear it so another can be started + _pointer = undefined; + } + + if (!_pointer) { + _pointer = { + startLoc: loc, + startTime: new Date().getTime(), + upCount: 0, + pointerId: d3_event.pointerId + }; + } else { + // double down + _pointer.pointerId = d3_event.pointerId; + } + } + + function pointerup(d3_event) { + // ignore right-click + if (d3_event.ctrlKey || d3_event.button === 2) return; + if (!_pointer || _pointer.pointerId !== d3_event.pointerId) return; + _pointer.upCount += 1; + + if (_pointer.upCount === 2) { + // double up! + var loc = [d3_event.clientX, d3_event.clientY]; + + if (pointerIsValidFor(loc)) { + var locInThis = utilFastMouse(this)(d3_event); + dispatch.call('doubleUp', this, d3_event, locInThis); + } // clear the pointer info in any case + + + _pointer = undefined; + } + } + + function doubleUp(selection) { + if ('PointerEvent' in window) { + // dblclick isn't well supported on touch devices so manually use + // pointer events if they're available + selection.on('pointerdown.doubleUp', pointerdown).on('pointerup.doubleUp', pointerup); + } else { + // fallback to dblclick + selection.on('dblclick.doubleUp', function (d3_event) { + dispatch.call('doubleUp', this, d3_event, utilFastMouse(this)(d3_event)); + }); + } + } + + doubleUp.off = function (selection) { + selection.on('pointerdown.doubleUp', null).on('pointerup.doubleUp', null).on('dblclick.doubleUp', null); + }; + + return utilRebind(doubleUp, dispatch, 'on'); + } + + var TILESIZE = 256; + var minZoom = 2; + var maxZoom = 24; + var kMin = geoZoomToScale(minZoom, TILESIZE); + var kMax = geoZoomToScale(maxZoom, TILESIZE); + + function clamp$1(num, min, max) { + return Math.max(min, Math.min(num, max)); + } + + function rendererMap(context) { + var dispatch = dispatch$8('move', 'drawn', 'crossEditableZoom', 'hitMinZoom', 'changeHighlighting', 'changeAreaFill'); + var projection = context.projection; + var curtainProjection = context.curtainProjection; + var drawLayers; + var drawPoints; + var drawVertices; + var drawLines; + var drawAreas; + var drawMidpoints; + var drawLabels; + + var _selection = select(null); + + var supersurface = select(null); + var wrapper = select(null); + var surface = select(null); + var _dimensions = [1, 1]; + var _dblClickZoomEnabled = true; + var _redrawEnabled = true; + + var _gestureTransformStart; + + var _transformStart = projection.transform(); + + var _transformLast; + + var _isTransformed = false; + var _minzoom = 0; + + var _getMouseCoords; + + var _lastPointerEvent; + + var _lastWithinEditableZoom; // whether a pointerdown event started the zoom + + + var _pointerDown = false; // use pointer events on supported platforms; fallback to mouse events + + var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom + + + var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom; + + var _zoomerPanner = _zoomerPannerFunction().scaleExtent([kMin, kMax]).interpolate(interpolate$1).filter(zoomEventFilter).on('zoom.map', zoomPan).on('start.map', function (d3_event) { + _pointerDown = d3_event && (d3_event.type === 'pointerdown' || d3_event.sourceEvent && d3_event.sourceEvent.type === 'pointerdown'); + }).on('end.map', function () { + _pointerDown = false; + }); + + var _doubleUpHandler = utilDoubleUp(); + + var scheduleRedraw = throttle(redraw, 750); // var isRedrawScheduled = false; + // var pendingRedrawCall; + // function scheduleRedraw() { + // // Only schedule the redraw if one has not already been set. + // if (isRedrawScheduled) return; + // isRedrawScheduled = true; + // var that = this; + // var args = arguments; + // pendingRedrawCall = window.requestIdleCallback(function () { + // // Reset the boolean so future redraws can be set. + // isRedrawScheduled = false; + // redraw.apply(that, args); + // }, { timeout: 1400 }); + // } + + + function cancelPendingRedraw() { + scheduleRedraw.cancel(); // isRedrawScheduled = false; + // window.cancelIdleCallback(pendingRedrawCall); + } + + function map(selection) { + _selection = selection; + context.on('change.map', immediateRedraw); + var osm = context.connection(); + + if (osm) { + osm.on('change.map', immediateRedraw); + } + + function didUndoOrRedo(targetTransform) { + var mode = context.mode().id; + if (mode !== 'browse' && mode !== 'select') return; + + if (targetTransform) { + map.transformEase(targetTransform); + } + } + + context.history().on('merge.map', function () { + scheduleRedraw(); + }).on('change.map', immediateRedraw).on('undone.map', function (stack, fromStack) { + didUndoOrRedo(fromStack.transform); + }).on('redone.map', function (stack) { + didUndoOrRedo(stack.transform); + }); + context.background().on('change.map', immediateRedraw); + context.features().on('redraw.map', immediateRedraw); + drawLayers.on('change.map', function () { + context.background().updateImagery(); + immediateRedraw(); + }); + selection.on('wheel.map mousewheel.map', function (d3_event) { + // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552 + d3_event.preventDefault(); + }).call(_zoomerPanner).call(_zoomerPanner.transform, projection.transform()).on('dblclick.zoom', null); // override d3-zoom dblclick handling + + map.supersurface = supersurface = selection.append('div').attr('class', 'supersurface').call(utilSetTransform, 0, 0); // Need a wrapper div because Opera can't cope with an absolutely positioned + // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16 + + wrapper = supersurface.append('div').attr('class', 'layer layer-data'); + map.surface = surface = wrapper.call(drawLayers).selectAll('.surface'); + surface.call(drawLabels.observe).call(_doubleUpHandler).on(_pointerPrefix + 'down.zoom', function (d3_event) { + _lastPointerEvent = d3_event; + + if (d3_event.button === 2) { + d3_event.stopPropagation(); + } + }, true).on(_pointerPrefix + 'up.zoom', function (d3_event) { + _lastPointerEvent = d3_event; + + if (resetTransform()) { + immediateRedraw(); + } + }).on(_pointerPrefix + 'move.map', function (d3_event) { + _lastPointerEvent = d3_event; + }).on(_pointerPrefix + 'over.vertices', function (d3_event) { + if (map.editableDataEnabled() && !_isTransformed) { + var hover = d3_event.target.__data__; + surface.call(drawVertices.drawHover, context.graph(), hover, map.extent()); + dispatch.call('drawn', this, { + full: false + }); + } + }).on(_pointerPrefix + 'out.vertices', function (d3_event) { + if (map.editableDataEnabled() && !_isTransformed) { + var hover = d3_event.relatedTarget && d3_event.relatedTarget.__data__; + surface.call(drawVertices.drawHover, context.graph(), hover, map.extent()); + dispatch.call('drawn', this, { + full: false + }); + } + }); + var detected = utilDetect(); // only WebKit supports gesture events + + if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping, + // but we only need to do this on desktop Safari anyway. – #7694 + !detected.isMobileWebKit) { + // Desktop Safari sends gesture events for multitouch trackpad pinches. + // We can listen for these and translate them into map zooms. + surface.on('gesturestart.surface', function (d3_event) { + d3_event.preventDefault(); + _gestureTransformStart = projection.transform(); + }).on('gesturechange.surface', gestureChange); + } // must call after surface init + + + updateAreaFill(); + + _doubleUpHandler.on('doubleUp.map', function (d3_event, p0) { + if (!_dblClickZoomEnabled) return; // don't zoom if targeting something other than the map itself + + if (_typeof(d3_event.target.__data__) === 'object' && // or area fills + !select(d3_event.target).classed('fill')) return; + var zoomOut = d3_event.shiftKey; + var t = projection.transform(); + var p1 = t.invert(p0); + t = t.scale(zoomOut ? 0.5 : 2); + t.x = p0[0] - p1[0] * t.k; + t.y = p0[1] - p1[1] * t.k; + map.transformEase(t); + }); + + context.on('enter.map', function () { + if (!map.editableDataEnabled(true + /* skip zoom check */ + )) return; + if (_isTransformed) return; // redraw immediately any objects affected by a change in selectedIDs. + + var graph = context.graph(); + var selectedAndParents = {}; + context.selectedIDs().forEach(function (id) { + var entity = graph.hasEntity(id); + + if (entity) { + selectedAndParents[entity.id] = entity; + + if (entity.type === 'node') { + graph.parentWays(entity).forEach(function (parent) { + selectedAndParents[parent.id] = parent; + }); + } + } + }); + var data = Object.values(selectedAndParents); + + var filter = function filter(d) { + return d.id in selectedAndParents; + }; + + data = context.features().filter(data, graph); + surface.call(drawVertices.drawSelected, graph, map.extent()).call(drawLines, graph, data, filter).call(drawAreas, graph, data, filter).call(drawMidpoints, graph, data, filter, map.trimmedExtent()); + dispatch.call('drawn', this, { + full: false + }); // redraw everything else later + + scheduleRedraw(); + }); + map.dimensions(utilGetDimensions(selection)); + } + + function zoomEventFilter(d3_event) { + // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18) + // Intercept `mousedown` and check if there is an orphaned zoom gesture. + // This can happen if a previous `mousedown` occurred without a `mouseup`. + // If we detect this, dispatch `mouseup` to complete the orphaned gesture, + // so that d3-zoom won't stop propagation of new `mousedown` events. + if (d3_event.type === 'mousedown') { + var hasOrphan = false; + var listeners = window.__on; + + for (var i = 0; i < listeners.length; i++) { + var listener = listeners[i]; + + if (listener.name === 'zoom' && listener.type === 'mouseup') { + hasOrphan = true; + break; + } + } + + if (hasOrphan) { + var event = window.CustomEvent; + + if (event) { + event = new event('mouseup'); + } else { + event = window.document.createEvent('Event'); + event.initEvent('mouseup', false, false); + } // Event needs to be dispatched with an event.view property. + + + event.view = window; + window.dispatchEvent(event); + } + } + + return d3_event.button !== 2; // ignore right clicks + } + + function pxCenter() { + return [_dimensions[0] / 2, _dimensions[1] / 2]; + } + + function drawEditable(difference, extent) { + var mode = context.mode(); + var graph = context.graph(); + var features = context.features(); + var all = context.history().intersects(map.extent()); + var fullRedraw = false; + var data; + var set; + var filter; + var applyFeatureLayerFilters = true; + + if (map.isInWideSelection()) { + data = []; + utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function (id) { + var entity = context.hasEntity(id); + if (entity) data.push(entity); + }); + fullRedraw = true; + filter = utilFunctor(true); // selected features should always be visible, so we can skip filtering + + applyFeatureLayerFilters = false; + } else if (difference) { + var complete = difference.complete(map.extent()); + data = Object.values(complete).filter(Boolean); + set = new Set(Object.keys(complete)); + + filter = function filter(d) { + return set.has(d.id); + }; + + features.clear(data); + } else { + // force a full redraw if gatherStats detects that a feature + // should be auto-hidden (e.g. points or buildings).. + if (features.gatherStats(all, graph, _dimensions)) { + extent = undefined; + } + + if (extent) { + data = context.history().intersects(map.extent().intersection(extent)); + set = new Set(data.map(function (entity) { + return entity.id; + })); + + filter = function filter(d) { + return set.has(d.id); + }; + } else { + data = all; + fullRedraw = true; + filter = utilFunctor(true); + } + } + + if (applyFeatureLayerFilters) { + data = features.filter(data, graph); + } else { + context.features().resetStats(); + } + + if (mode && mode.id === 'select') { + // update selected vertices - the user might have just double-clicked a way, + // creating a new vertex, triggering a partial redraw without a mode change + surface.call(drawVertices.drawSelected, graph, map.extent()); + } + + surface.call(drawVertices, graph, data, filter, map.extent(), fullRedraw).call(drawLines, graph, data, filter).call(drawAreas, graph, data, filter).call(drawMidpoints, graph, data, filter, map.trimmedExtent()).call(drawLabels, graph, data, filter, _dimensions, fullRedraw).call(drawPoints, graph, data, filter); + dispatch.call('drawn', this, { + full: true + }); + } + + map.init = function () { + drawLayers = svgLayers(projection, context); + drawPoints = svgPoints(projection, context); + drawVertices = svgVertices(projection, context); + drawLines = svgLines(projection, context); + drawAreas = svgAreas(projection, context); + drawMidpoints = svgMidpoints(projection, context); + drawLabels = svgLabels(projection, context); + }; + + function editOff() { + context.features().resetStats(); + surface.selectAll('.layer-osm *').remove(); + surface.selectAll('.layer-touch:not(.markers) *').remove(); + var allowed = { + 'browse': true, + 'save': true, + 'select-note': true, + 'select-data': true, + 'select-error': true + }; + var mode = context.mode(); + + if (mode && !allowed[mode.id]) { + context.enter(modeBrowse(context)); + } + + dispatch.call('drawn', this, { + full: true + }); + } + + function gestureChange(d3_event) { + // Remap Safari gesture events to wheel events - #5492 + // We want these disabled most places, but enabled for zoom/unzoom on map surface + // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent + var e = d3_event; + e.preventDefault(); + var props = { + deltaMode: 0, + // dummy values to ignore in zoomPan + deltaY: 1, + // dummy values to ignore in zoomPan + clientX: e.clientX, + clientY: e.clientY, + screenX: e.screenX, + screenY: e.screenY, + x: e.x, + y: e.y + }; + var e2 = new WheelEvent('wheel', props); + e2._scale = e.scale; // preserve the original scale + + e2._rotation = e.rotation; // preserve the original rotation + + _selection.node().dispatchEvent(e2); + } + + function zoomPan(event, key, transform) { + var source = event && event.sourceEvent || event; + var eventTransform = transform || event && event.transform; + var x = eventTransform.x; + var y = eventTransform.y; + var k = eventTransform.k; // Special handling of 'wheel' events: + // They might be triggered by the user scrolling the mouse wheel, + // or 2-finger pinch/zoom gestures, the transform may need adjustment. + + if (source && source.type === 'wheel') { + // assume that the gesture is already handled by pointer events + if (_pointerDown) return; + var detected = utilDetect(); + var dX = source.deltaX; + var dY = source.deltaY; + var x2 = x; + var y2 = y; + var k2 = k; + var t0, p0, p1; // Normalize mousewheel scroll speed (Firefox) - #3029 + // If wheel delta is provided in LINE units, recalculate it in PIXEL units + // We are essentially redoing the calculations that occur here: + // https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203 + // See this for more info: + // https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js + + if (source.deltaMode === 1 + /* LINE */ + ) { + // Convert from lines to pixels, more if the user is scrolling fast. + // (I made up the exp function to roughly match Firefox to what Chrome does) + // These numbers should be floats, because integers are treated as pan gesture below. + var lines = Math.abs(source.deltaY); + var sign = source.deltaY > 0 ? 1 : -1; + dY = sign * clamp$1(Math.exp((lines - 1) * 0.75) * 4.000244140625, 4.000244140625, // min + 350.000244140625 // max + ); // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3) + // There doesn't seem to be any scroll acceleration. + // This multiplier increases the speed a little bit - #5512 + + if (detected.os !== 'mac') { + dY *= 5; + } // recalculate x2,y2,k2 + + + t0 = _isTransformed ? _transformLast : _transformStart; + p0 = _getMouseCoords(source); + p1 = t0.invert(p0); + k2 = t0.k * Math.pow(2, -dY / 500); + k2 = clamp$1(k2, kMin, kMax); + x2 = p0[0] - p1[0] * k2; + y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (Safari) - #5492 + // These are fake `wheel` events we made from Safari `gesturechange` events.. + } else if (source._scale) { + // recalculate x2,y2,k2 + t0 = _gestureTransformStart; + p0 = _getMouseCoords(source); + p1 = t0.invert(p0); + k2 = t0.k * source._scale; + k2 = clamp$1(k2, kMin, kMax); + x2 = p0[0] - p1[0] * k2; + y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (all browsers except Safari) - #5492 + // Pinch zooming via the `wheel` event will always have: + // - `ctrlKey = true` + // - `deltaY` is not round integer pixels (ignore `deltaX`) + } else if (source.ctrlKey && !isInteger(dY)) { + dY *= 6; // slightly scale up whatever the browser gave us + // recalculate x2,y2,k2 + + t0 = _isTransformed ? _transformLast : _transformStart; + p0 = _getMouseCoords(source); + p1 = t0.invert(p0); + k2 = t0.k * Math.pow(2, -dY / 500); + k2 = clamp$1(k2, kMin, kMax); + x2 = p0[0] - p1[0] * k2; + y2 = p0[1] - p1[1] * k2; // Trackpad scroll zooming with shift or alt/option key down + } else if ((source.altKey || source.shiftKey) && isInteger(dY)) { + // recalculate x2,y2,k2 + t0 = _isTransformed ? _transformLast : _transformStart; + p0 = _getMouseCoords(source); + p1 = t0.invert(p0); + k2 = t0.k * Math.pow(2, -dY / 500); + k2 = clamp$1(k2, kMin, kMax); + x2 = p0[0] - p1[0] * k2; + y2 = p0[1] - p1[1] * k2; // 2 finger map panning (Mac only, all browsers except Firefox #8595) - #5492, #5512 + // Panning via the `wheel` event will always have: + // - `ctrlKey = false` + // - `deltaX`,`deltaY` are round integer pixels + } else if (detected.os === 'mac' && detected.browser !== 'Firefox' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) { + p1 = projection.translate(); + x2 = p1[0] - dX; + y2 = p1[1] - dY; + k2 = projection.scale(); + k2 = clamp$1(k2, kMin, kMax); + } // something changed - replace the event transform + + + if (x2 !== x || y2 !== y || k2 !== k) { + x = x2; + y = y2; + k = k2; + eventTransform = identity$2.translate(x2, y2).scale(k2); + + if (_zoomerPanner._transform) { + // utilZoomPan interface + _zoomerPanner._transform(eventTransform); + } else { + // d3_zoom interface + _selection.node().__zoom = eventTransform; + } + } + } + + if (_transformStart.x === x && _transformStart.y === y && _transformStart.k === k) { + return; // no change + } + + if (geoScaleToZoom(k, TILESIZE) < _minzoom) { + surface.interrupt(); + dispatch.call('hitMinZoom', this, map); + setCenterZoom(map.center(), context.minEditableZoom(), 0, true); + scheduleRedraw(); + dispatch.call('move', this, map); + return; + } + + projection.transform(eventTransform); + var withinEditableZoom = map.withinEditableZoom(); + + if (_lastWithinEditableZoom !== withinEditableZoom) { + if (_lastWithinEditableZoom !== undefined) { + // notify that the map zoomed in or out over the editable zoom threshold + dispatch.call('crossEditableZoom', this, withinEditableZoom); + } + + _lastWithinEditableZoom = withinEditableZoom; + } + + var scale = k / _transformStart.k; + var tX = (x / scale - _transformStart.x) * scale; + var tY = (y / scale - _transformStart.y) * scale; + + if (context.inIntro()) { + curtainProjection.transform({ + x: x - tX, + y: y - tY, + k: k + }); + } + + if (source) { + _lastPointerEvent = event; + } + + _isTransformed = true; + _transformLast = eventTransform; + utilSetTransform(supersurface, tX, tY, scale); + scheduleRedraw(); + dispatch.call('move', this, map); + + function isInteger(val) { + return typeof val === 'number' && isFinite(val) && Math.floor(val) === val; + } + } + + function resetTransform() { + if (!_isTransformed) return false; + utilSetTransform(supersurface, 0, 0); + _isTransformed = false; + + if (context.inIntro()) { + curtainProjection.transform(projection.transform()); + } + + return true; + } + + function redraw(difference, extent) { + if (surface.empty() || !_redrawEnabled) return; // If we are in the middle of a zoom/pan, we can't do differenced redraws. + // It would result in artifacts where differenced entities are redrawn with + // one transform and unchanged entities with another. + + if (resetTransform()) { + difference = extent = undefined; + } + + var zoom = map.zoom(); + var z = String(~~zoom); + + if (surface.attr('data-zoom') !== z) { + surface.attr('data-zoom', z); + } // class surface as `lowzoom` around z17-z18.5 (based on latitude) + + + var lat = map.center()[1]; + var lowzoom = linear().domain([-60, 0, 60]).range([17, 18.5, 17]).clamp(true); + surface.classed('low-zoom', zoom <= lowzoom(lat)); + + if (!difference) { + supersurface.call(context.background()); + wrapper.call(drawLayers); + } // OSM + + + if (map.editableDataEnabled() || map.isInWideSelection()) { + context.loadTiles(projection); + drawEditable(difference, extent); + } else { + editOff(); + } + + _transformStart = projection.transform(); + return map; + } + + var immediateRedraw = function immediateRedraw(difference, extent) { + if (!difference && !extent) cancelPendingRedraw(); + redraw(difference, extent); + }; + + map.lastPointerEvent = function () { + return _lastPointerEvent; + }; + + map.mouse = function (d3_event) { + var event = d3_event || _lastPointerEvent; + + if (event) { + var s; + + while (s = event.sourceEvent) { + event = s; + } + + return _getMouseCoords(event); + } + + return null; + }; // returns Lng/Lat + + + map.mouseCoordinates = function () { + var coord = map.mouse() || pxCenter(); + return projection.invert(coord); + }; + + map.dblclickZoomEnable = function (val) { + if (!arguments.length) return _dblClickZoomEnabled; + _dblClickZoomEnabled = val; + return map; + }; + + map.redrawEnable = function (val) { + if (!arguments.length) return _redrawEnabled; + _redrawEnabled = val; + return map; + }; + + map.isTransformed = function () { + return _isTransformed; + }; + + function setTransform(t2, duration, force) { + var t = projection.transform(); + if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) return false; + + if (duration) { + _selection.transition().duration(duration).on('start', function () { + map.startEase(); + }).call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k)); + } else { + projection.transform(t2); + _transformStart = t2; + + _selection.call(_zoomerPanner.transform, _transformStart); + } + + return true; + } + + function setCenterZoom(loc2, z2, duration, force) { + var c = map.center(); + var z = map.zoom(); + if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) return false; + var proj = geoRawMercator().transform(projection.transform()); // copy projection + + var k2 = clamp$1(geoZoomToScale(z2, TILESIZE), kMin, kMax); + proj.scale(k2); + var t = proj.translate(); + var point = proj(loc2); + var center = pxCenter(); + t[0] += center[0] - point[0]; + t[1] += center[1] - point[1]; + return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force); + } + + map.pan = function (delta, duration) { + var t = projection.translate(); + var k = projection.scale(); + t[0] += delta[0]; + t[1] += delta[1]; + + if (duration) { + _selection.transition().duration(duration).on('start', function () { + map.startEase(); + }).call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k)); + } else { + projection.translate(t); + _transformStart = projection.transform(); + + _selection.call(_zoomerPanner.transform, _transformStart); + + dispatch.call('move', this, map); + immediateRedraw(); + } + + return map; + }; + + map.dimensions = function (val) { + if (!arguments.length) return _dimensions; + _dimensions = val; + drawLayers.dimensions(_dimensions); + context.background().dimensions(_dimensions); + projection.clipExtent([[0, 0], _dimensions]); + _getMouseCoords = utilFastMouse(supersurface.node()); + scheduleRedraw(); + return map; + }; + + function zoomIn(delta) { + setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true); + } + + function zoomOut(delta) { + setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true); + } + + map.zoomIn = function () { + zoomIn(1); + }; + + map.zoomInFurther = function () { + zoomIn(4); + }; + + map.canZoomIn = function () { + return map.zoom() < maxZoom; + }; + + map.zoomOut = function () { + zoomOut(1); + }; + + map.zoomOutFurther = function () { + zoomOut(4); + }; + + map.canZoomOut = function () { + return map.zoom() > minZoom; + }; + + map.center = function (loc2) { + if (!arguments.length) { + return projection.invert(pxCenter()); + } + + if (setCenterZoom(loc2, map.zoom())) { + dispatch.call('move', this, map); + } + + scheduleRedraw(); + return map; + }; + + map.unobscuredCenterZoomEase = function (loc, zoom) { + var offset = map.unobscuredOffsetPx(); + var proj = geoRawMercator().transform(projection.transform()); // copy projection + // use the target zoom to calculate the offset center + + proj.scale(geoZoomToScale(zoom, TILESIZE)); + var locPx = proj(loc); + var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]]; + var offsetLoc = proj.invert(offsetLocPx); + map.centerZoomEase(offsetLoc, zoom); + }; + + map.unobscuredOffsetPx = function () { + var openPane = context.container().select('.map-panes .map-pane.shown'); + + if (!openPane.empty()) { + return [openPane.node().offsetWidth / 2, 0]; + } + + return [0, 0]; + }; + + map.zoom = function (z2) { + if (!arguments.length) { + return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0); + } + + if (z2 < _minzoom) { + surface.interrupt(); + dispatch.call('hitMinZoom', this, map); + z2 = context.minEditableZoom(); + } + + if (setCenterZoom(map.center(), z2)) { + dispatch.call('move', this, map); + } + + scheduleRedraw(); + return map; + }; + + map.centerZoom = function (loc2, z2) { + if (setCenterZoom(loc2, z2)) { + dispatch.call('move', this, map); + } + + scheduleRedraw(); + return map; + }; + + map.zoomTo = function (entity) { + var extent = entity.extent(context.graph()); + if (!isFinite(extent.area())) return map; + var z2 = clamp$1(map.trimmedExtentZoom(extent), 0, 20); + return map.centerZoom(extent.center(), z2); + }; + + map.centerEase = function (loc2, duration) { + duration = duration || 250; + setCenterZoom(loc2, map.zoom(), duration); + return map; + }; + + map.zoomEase = function (z2, duration) { + duration = duration || 250; + setCenterZoom(map.center(), z2, duration, false); + return map; + }; + + map.centerZoomEase = function (loc2, z2, duration) { + duration = duration || 250; + setCenterZoom(loc2, z2, duration, false); + return map; + }; + + map.transformEase = function (t2, duration) { + duration = duration || 250; + setTransform(t2, duration, false + /* don't force */ + ); + return map; + }; + + map.zoomToEase = function (obj, duration) { + var extent; + + if (Array.isArray(obj)) { + obj.forEach(function (entity) { + var entityExtent = entity.extent(context.graph()); + + if (!extent) { + extent = entityExtent; + } else { + extent = extent.extend(entityExtent); + } + }); + } else { + extent = obj.extent(context.graph()); + } + + if (!isFinite(extent.area())) return map; + var z2 = clamp$1(map.trimmedExtentZoom(extent), 0, 20); + return map.centerZoomEase(extent.center(), z2, duration); + }; + + map.startEase = function () { + utilBindOnce(surface, _pointerPrefix + 'down.ease', function () { + map.cancelEase(); + }); + return map; + }; + + map.cancelEase = function () { + _selection.interrupt(); + + return map; + }; + + map.extent = function (val) { + if (!arguments.length) { + return new geoExtent(projection.invert([0, _dimensions[1]]), projection.invert([_dimensions[0], 0])); + } else { + var extent = geoExtent(val); + map.centerZoom(extent.center(), map.extentZoom(extent)); + } + }; + + map.trimmedExtent = function (val) { + if (!arguments.length) { + var headerY = 71; + var footerY = 30; + var pad = 10; + return new geoExtent(projection.invert([pad, _dimensions[1] - footerY - pad]), projection.invert([_dimensions[0] - pad, headerY + pad])); + } else { + var extent = geoExtent(val); + map.centerZoom(extent.center(), map.trimmedExtentZoom(extent)); + } + }; + + function calcExtentZoom(extent, dim) { + var tl = projection([extent[0][0], extent[1][1]]); + var br = projection([extent[1][0], extent[0][1]]); // Calculate maximum zoom that fits extent + + var hFactor = (br[0] - tl[0]) / dim[0]; + var vFactor = (br[1] - tl[1]) / dim[1]; + var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2; + var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2; + var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff); + return newZoom; + } + + map.extentZoom = function (val) { + return calcExtentZoom(geoExtent(val), _dimensions); + }; + + map.trimmedExtentZoom = function (val) { + var trimY = 120; + var trimX = 40; + var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY]; + return calcExtentZoom(geoExtent(val), trimmed); + }; + + map.withinEditableZoom = function () { + return map.zoom() >= context.minEditableZoom(); + }; + + map.isInWideSelection = function () { + return !map.withinEditableZoom() && context.selectedIDs().length; + }; + + map.editableDataEnabled = function (skipZoomCheck) { + var layer = context.layers().layer('osm'); + if (!layer || !layer.enabled()) return false; + return skipZoomCheck || map.withinEditableZoom(); + }; + + map.notesEditable = function () { + var layer = context.layers().layer('notes'); + if (!layer || !layer.enabled()) return false; + return map.withinEditableZoom(); + }; + + map.minzoom = function (val) { + if (!arguments.length) return _minzoom; + _minzoom = val; + return map; + }; + + map.toggleHighlightEdited = function () { + surface.classed('highlight-edited', !surface.classed('highlight-edited')); + map.pan([0, 0]); // trigger a redraw + + dispatch.call('changeHighlighting', this); + }; + + map.areaFillOptions = ['wireframe', 'partial', 'full']; + + map.activeAreaFill = function (val) { + if (!arguments.length) return corePreferences('area-fill') || 'partial'; + corePreferences('area-fill', val); + + if (val !== 'wireframe') { + corePreferences('area-fill-toggle', val); + } + + updateAreaFill(); + map.pan([0, 0]); // trigger a redraw + + dispatch.call('changeAreaFill', this); + return map; + }; + + map.toggleWireframe = function () { + var activeFill = map.activeAreaFill(); + + if (activeFill === 'wireframe') { + activeFill = corePreferences('area-fill-toggle') || 'partial'; + } else { + activeFill = 'wireframe'; + } + + map.activeAreaFill(activeFill); + }; + + function updateAreaFill() { + var activeFill = map.activeAreaFill(); + map.areaFillOptions.forEach(function (opt) { + surface.classed('fill-' + opt, Boolean(opt === activeFill)); + }); + } + + map.layers = function () { + return drawLayers; + }; + + map.doubleUpHandler = function () { + return _doubleUpHandler; + }; + + return utilRebind(map, dispatch, 'on'); + } + + function rendererPhotos(context) { + var dispatch = dispatch$8('change'); + var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam']; + var _allPhotoTypes = ['flat', 'panoramic']; + + var _shownPhotoTypes = _allPhotoTypes.slice(); // shallow copy + + + var _dateFilters = ['fromDate', 'toDate']; + + var _fromDate; + + var _toDate; + + var _usernames; + + function photos() {} + + function updateStorage() { + if (window.mocha) return; + var hash = utilStringQs(window.location.hash); + var enabled = context.layers().all().filter(function (d) { + return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled(); + }).map(function (d) { + return d.id; + }); + + if (enabled.length) { + hash.photo_overlay = enabled.join(','); + } else { + delete hash.photo_overlay; + } + + window.location.replace('#' + utilQsString(hash, true)); + } + + photos.overlayLayerIDs = function () { + return _layerIDs; + }; + + photos.allPhotoTypes = function () { + return _allPhotoTypes; + }; + + photos.dateFilters = function () { + return _dateFilters; + }; + + photos.dateFilterValue = function (val) { + return val === _dateFilters[0] ? _fromDate : _toDate; + }; + + photos.setDateFilter = function (type, val, updateUrl) { + // validate the date + var date = val && new Date(val); + + if (date && !isNaN(date)) { + val = date.toISOString().substr(0, 10); + } else { + val = null; + } + + if (type === _dateFilters[0]) { + _fromDate = val; + + if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) { + _toDate = _fromDate; + } + } + + if (type === _dateFilters[1]) { + _toDate = val; + + if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) { + _fromDate = _toDate; + } + } + + dispatch.call('change', this); + + if (updateUrl) { + var rangeString; + + if (_fromDate || _toDate) { + rangeString = (_fromDate || '') + '_' + (_toDate || ''); + } + + setUrlFilterValue('photo_dates', rangeString); + } + }; + + photos.setUsernameFilter = function (val, updateUrl) { + if (val && typeof val === 'string') val = val.replace(/;/g, ',').split(','); + + if (val) { + val = val.map(function (d) { + return d.trim(); + }).filter(Boolean); + + if (!val.length) { + val = null; + } + } + + _usernames = val; + dispatch.call('change', this); + + if (updateUrl) { + var hashString; + + if (_usernames) { + hashString = _usernames.join(','); + } + + setUrlFilterValue('photo_username', hashString); + } + }; + + function setUrlFilterValue(property, val) { + if (!window.mocha) { + var hash = utilStringQs(window.location.hash); + + if (val) { + if (hash[property] === val) return; + hash[property] = val; + } else { + if (!(property in hash)) return; + delete hash[property]; + } + + window.location.replace('#' + utilQsString(hash, true)); + } + } + + function showsLayer(id) { + var layer = context.layers().layer(id); + return layer && layer.supported() && layer.enabled(); + } + + photos.shouldFilterByDate = function () { + return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside'); + }; + + photos.shouldFilterByPhotoType = function () { + return showsLayer('mapillary') || showsLayer('streetside') && showsLayer('openstreetcam'); + }; + + photos.shouldFilterByUsername = function () { + return !showsLayer('mapillary') && showsLayer('openstreetcam') && !showsLayer('streetside'); + }; + + photos.showsPhotoType = function (val) { + if (!photos.shouldFilterByPhotoType()) return true; + return _shownPhotoTypes.indexOf(val) !== -1; + }; + + photos.showsFlat = function () { + return photos.showsPhotoType('flat'); + }; + + photos.showsPanoramic = function () { + return photos.showsPhotoType('panoramic'); + }; + + photos.fromDate = function () { + return _fromDate; + }; + + photos.toDate = function () { + return _toDate; + }; + + photos.togglePhotoType = function (val) { + var index = _shownPhotoTypes.indexOf(val); + + if (index !== -1) { + _shownPhotoTypes.splice(index, 1); + } else { + _shownPhotoTypes.push(val); + } + + dispatch.call('change', this); + return photos; + }; + + photos.usernames = function () { + return _usernames; + }; + + photos.init = function () { + var hash = utilStringQs(window.location.hash); + + if (hash.photo_dates) { + // expect format like `photo_dates=2019-01-01_2020-12-31`, but allow a couple different separators + var parts = /^(.*)[–_](.*)$/g.exec(hash.photo_dates.trim()); + this.setDateFilter('fromDate', parts && parts.length >= 2 && parts[1], false); + this.setDateFilter('toDate', parts && parts.length >= 3 && parts[2], false); + } + + if (hash.photo_username) { + this.setUsernameFilter(hash.photo_username, false); + } + + if (hash.photo_overlay) { + // support enabling photo layers by default via a URL parameter, e.g. `photo_overlay=openstreetcam;mapillary;streetside` + var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(','); + hashOverlayIDs.forEach(function (id) { + var layer = _layerIDs.indexOf(id) !== -1 && context.layers().layer(id); + if (layer && !layer.enabled()) layer.enabled(true); + }); + } + + if (hash.photo) { + // support opening a photo via a URL parameter, e.g. `photo=mapillary-fztgSDtLpa08ohPZFZjeRQ` + var photoIds = hash.photo.replace(/;/g, ',').split(','); + var photoId = photoIds.length && photoIds[0].trim(); + var results = /(.*)\/(.*)/g.exec(photoId); + + if (results && results.length >= 3) { + var serviceId = results[1]; + var photoKey = results[2]; + var service = services[serviceId]; + + if (service && service.ensureViewerLoaded) { + // if we're showing a photo then make sure its layer is enabled too + var layer = _layerIDs.indexOf(serviceId) !== -1 && context.layers().layer(serviceId); + if (layer && !layer.enabled()) layer.enabled(true); + var baselineTime = Date.now(); + service.on('loadedImages.rendererPhotos', function () { + // don't open the viewer if too much time has elapsed + if (Date.now() - baselineTime > 45000) { + service.on('loadedImages.rendererPhotos', null); + return; + } + + if (!service.cachedImage(photoKey)) return; + service.on('loadedImages.rendererPhotos', null); + service.ensureViewerLoaded(context).then(function () { + service.selectImage(context, photoKey).showViewer(context); + }); + }); + } + } + } + + context.layers().on('change.rendererPhotos', updateStorage); + }; + + return utilRebind(photos, dispatch, 'on'); + } + + function uiAccount(context) { + var osm = context.connection(); + + function update(selection) { + if (!osm) return; + + if (!osm.authenticated()) { + selection.selectAll('.userLink, .logoutLink').classed('hide', true); + return; + } + + osm.userDetails(function (err, details) { + var userLink = selection.select('.userLink'), + logoutLink = selection.select('.logoutLink'); + userLink.html(''); + logoutLink.html(''); + if (err || !details) return; + selection.selectAll('.userLink, .logoutLink').classed('hide', false); // Link + + var userLinkA = userLink.append('a').attr('href', osm.userURL(details.display_name)).attr('target', '_blank'); // Add thumbnail or dont + + if (details.image_url) { + userLinkA.append('img').attr('class', 'icon pre-text user-icon').attr('src', details.image_url); + } else { + userLinkA.call(svgIcon('#iD-icon-avatar', 'pre-text light')); + } // Add user name + + + userLinkA.append('span').attr('class', 'label').html(details.display_name); + logoutLink.append('a').attr('class', 'logout').attr('href', '#').html(_t.html('logout')).on('click.logout', function (d3_event) { + d3_event.preventDefault(); + osm.logout(); + }); + }); + } + + return function (selection) { + selection.append('li').attr('class', 'userLink').classed('hide', true); + selection.append('li').attr('class', 'logoutLink').classed('hide', true); + + if (osm) { + osm.on('change.account', function () { + update(selection); + }); + update(selection); + } + }; + } + + function uiAttribution(context) { + var _selection = select(null); + + function render(selection, data, klass) { + var div = selection.selectAll(".".concat(klass)).data([0]); + div = div.enter().append('div').attr('class', klass).merge(div); + var attributions = div.selectAll('.attribution').data(data, function (d) { + return d.id; + }); + attributions.exit().remove(); + attributions = attributions.enter().append('span').attr('class', 'attribution').each(function (d, i, nodes) { + var attribution = select(nodes[i]); + + if (d.terms_html) { + attribution.html(d.terms_html); + return; + } + + if (d.terms_url) { + attribution = attribution.append('a').attr('href', d.terms_url).attr('target', '_blank'); + } + + var sourceID = d.id.replace(/\./g, ''); + var terms_text = _t("imagery.".concat(sourceID, ".attribution.text"), { + "default": d.terms_text || d.id || d.name() + }); + + if (d.icon && !d.overlay) { + attribution.append('img').attr('class', 'source-image').attr('src', d.icon); + } + + attribution.append('span').attr('class', 'attribution-text').html(terms_text); + }).merge(attributions); + var copyright = attributions.selectAll('.copyright-notice').data(function (d) { + var notice = d.copyrightNotices(context.map().zoom(), context.map().extent()); + return notice ? [notice] : []; + }); + copyright.exit().remove(); + copyright = copyright.enter().append('span').attr('class', 'copyright-notice').merge(copyright); + copyright.html(String); + } + + function update() { + var baselayer = context.background().baseLayerSource(); + + _selection.call(render, baselayer ? [baselayer] : [], 'base-layer-attribution'); + + var z = context.map().zoom(); + var overlays = context.background().overlayLayerSources() || []; + + _selection.call(render, overlays.filter(function (s) { + return s.validZoom(z); + }), 'overlay-layer-attribution'); + } + + return function (selection) { + _selection = selection; + context.background().on('change.attribution', update); + context.map().on('move.attribution', throttle(update, 400, { + leading: false + })); + update(); + }; + } + + function uiContributors(context) { + var osm = context.connection(), + debouncedUpdate = debounce(function () { + update(); + }, 1000), + limit = 4, + hidden = false, + wrap = select(null); + + function update() { + if (!osm) return; + var users = {}, + entities = context.history().intersects(context.map().extent()); + entities.forEach(function (entity) { + if (entity && entity.user) users[entity.user] = true; + }); + var u = Object.keys(users), + subset = u.slice(0, u.length > limit ? limit - 1 : limit); + wrap.html('').call(svgIcon('#iD-icon-nearby', 'pre-text light')); + var userList = select(document.createElement('span')); + userList.selectAll().data(subset).enter().append('a').attr('class', 'user-link').attr('href', function (d) { + return osm.userURL(d); + }).attr('target', '_blank').html(String); + + if (u.length > limit) { + var count = select(document.createElement('span')); + var othersNum = u.length - limit + 1; + count.append('a').attr('target', '_blank').attr('href', function () { + return osm.changesetsURL(context.map().center(), context.map().zoom()); + }).html(othersNum); + wrap.append('span').html(_t.html('contributors.truncated_list', { + n: othersNum, + users: userList.html(), + count: count.html() + })); + } else { + wrap.append('span').html(_t.html('contributors.list', { + users: userList.html() + })); + } + + if (!u.length) { + hidden = true; + wrap.transition().style('opacity', 0); + } else if (hidden) { + wrap.transition().style('opacity', 1); + } + } + + return function (selection) { + if (!osm) return; + wrap = selection; + update(); + osm.on('loaded.contributors', debouncedUpdate); + context.map().on('move.contributors', debouncedUpdate); + }; + } + + var _popoverID = 0; + function uiPopover(klass) { + var _id = _popoverID++; + + var _anchorSelection = select(null); + + var popover = function popover(selection) { + _anchorSelection = selection; + selection.each(setup); + }; + + var _animation = utilFunctor(false); + + var _placement = utilFunctor('top'); // top, bottom, left, right + + + var _alignment = utilFunctor('center'); // leading, center, trailing + + + var _scrollContainer = utilFunctor(select(null)); + + var _content; + + var _displayType = utilFunctor(''); + + var _hasArrow = utilFunctor(true); // use pointer events on supported platforms; fallback to mouse events + + + var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; + + popover.displayType = function (val) { + if (arguments.length) { + _displayType = utilFunctor(val); + return popover; + } else { + return _displayType; + } + }; + + popover.hasArrow = function (val) { + if (arguments.length) { + _hasArrow = utilFunctor(val); + return popover; + } else { + return _hasArrow; + } + }; + + popover.placement = function (val) { + if (arguments.length) { + _placement = utilFunctor(val); + return popover; + } else { + return _placement; + } + }; + + popover.alignment = function (val) { + if (arguments.length) { + _alignment = utilFunctor(val); + return popover; + } else { + return _alignment; + } + }; + + popover.scrollContainer = function (val) { + if (arguments.length) { + _scrollContainer = utilFunctor(val); + return popover; + } else { + return _scrollContainer; + } + }; + + popover.content = function (val) { + if (arguments.length) { + _content = val; + return popover; + } else { + return _content; + } + }; + + popover.isShown = function () { + var popoverSelection = _anchorSelection.select('.popover-' + _id); + + return !popoverSelection.empty() && popoverSelection.classed('in'); + }; + + popover.show = function () { + _anchorSelection.each(show); + }; + + popover.updateContent = function () { + _anchorSelection.each(updateContent); + }; + + popover.hide = function () { + _anchorSelection.each(hide); + }; + + popover.toggle = function () { + _anchorSelection.each(toggle); + }; + + popover.destroy = function (selection, selector) { + // by default, just destroy the current popover + selector = selector || '.popover-' + _id; + selection.on(_pointerPrefix + 'enter.popover', null).on(_pointerPrefix + 'leave.popover', null).on(_pointerPrefix + 'up.popover', null).on(_pointerPrefix + 'down.popover', null).on('click.popover', null).attr('title', function () { + return this.getAttribute('data-original-title') || this.getAttribute('title'); + }).attr('data-original-title', null).selectAll(selector).remove(); + }; + + popover.destroyAny = function (selection) { + selection.call(popover.destroy, '.popover'); + }; + + function setup() { + var anchor = select(this); + + var animate = _animation.apply(this, arguments); + + var popoverSelection = anchor.selectAll('.popover-' + _id).data([0]); + var enter = popoverSelection.enter().append('div').attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : '')).classed('arrowed', _hasArrow.apply(this, arguments)); + enter.append('div').attr('class', 'popover-arrow'); + enter.append('div').attr('class', 'popover-inner'); + popoverSelection = enter.merge(popoverSelection); + + if (animate) { + popoverSelection.classed('fade', true); + } + + var display = _displayType.apply(this, arguments); + + if (display === 'hover') { + var _lastNonMouseEnterTime; + + anchor.on(_pointerPrefix + 'enter.popover', function (d3_event) { + if (d3_event.pointerType) { + if (d3_event.pointerType !== 'mouse') { + _lastNonMouseEnterTime = d3_event.timeStamp; // only allow hover behavior for mouse input + + return; + } else if (_lastNonMouseEnterTime && d3_event.timeStamp - _lastNonMouseEnterTime < 1500) { + // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter + // event for non-mouse interactions right after sending + // the correct type pointerenter event. Workaround by discarding + // any mouse event that occurs immediately after a non-mouse event. + return; + } + } // don't show if buttons are pressed, e.g. during click and drag of map + + + if (d3_event.buttons !== 0) return; + show.apply(this, arguments); + }).on(_pointerPrefix + 'leave.popover', function () { + hide.apply(this, arguments); + }) // show on focus too for better keyboard navigation support + .on('focus.popover', function () { + show.apply(this, arguments); + }).on('blur.popover', function () { + hide.apply(this, arguments); + }); + } else if (display === 'clickFocus') { + anchor.on(_pointerPrefix + 'down.popover', function (d3_event) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + }).on(_pointerPrefix + 'up.popover', function (d3_event) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + }).on('click.popover', toggle); + popoverSelection // This attribute lets the popover take focus + .attr('tabindex', 0).on('blur.popover', function () { + anchor.each(function () { + hide.apply(this, arguments); + }); + }); + } + } + + function show() { + var anchor = select(this); + var popoverSelection = anchor.selectAll('.popover-' + _id); + + if (popoverSelection.empty()) { + // popover was removed somehow, put it back + anchor.call(popover.destroy); + anchor.each(setup); + popoverSelection = anchor.selectAll('.popover-' + _id); + } + + popoverSelection.classed('in', true); + + var displayType = _displayType.apply(this, arguments); + + if (displayType === 'clickFocus') { + anchor.classed('active', true); + popoverSelection.node().focus(); + } + + anchor.each(updateContent); + } + + function updateContent() { + var anchor = select(this); + + if (_content) { + anchor.selectAll('.popover-' + _id + ' > .popover-inner').call(_content.apply(this, arguments)); + } + + updatePosition.apply(this, arguments); // hack: update multiple times to fix instances where the absolute offset is + // set before the dynamic popover size is calculated by the browser + + updatePosition.apply(this, arguments); + updatePosition.apply(this, arguments); + } + + function updatePosition() { + var anchor = select(this); + var popoverSelection = anchor.selectAll('.popover-' + _id); + + var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments); + + var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node(); + var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0; + var scrollTop = scrollNode ? scrollNode.scrollTop : 0; + + var placement = _placement.apply(this, arguments); + + popoverSelection.classed('left', false).classed('right', false).classed('top', false).classed('bottom', false).classed(placement, true); + + var alignment = _alignment.apply(this, arguments); + + var alignFactor = 0.5; + + if (alignment === 'leading') { + alignFactor = 0; + } else if (alignment === 'trailing') { + alignFactor = 1; + } + + var anchorFrame = getFrame(anchor.node()); + var popoverFrame = getFrame(popoverSelection.node()); + var position; + + switch (placement) { + case 'top': + position = { + x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor, + y: anchorFrame.y - popoverFrame.h + }; + break; + + case 'bottom': + position = { + x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor, + y: anchorFrame.y + anchorFrame.h + }; + break; + + case 'left': + position = { + x: anchorFrame.x - popoverFrame.w, + y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor + }; + break; + + case 'right': + position = { + x: anchorFrame.x + anchorFrame.w, + y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor + }; + break; + } + + if (position) { + if (scrollNode && (placement === 'top' || placement === 'bottom')) { + var initialPosX = position.x; + + if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) { + position.x = scrollNode.offsetWidth - 10 - popoverFrame.w; + } else if (position.x < 10) { + position.x = 10; + } + + var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow'); // keep the arrow centered on the button, or as close as possible + + var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10); + arrow.style('left', ~~arrowPosX + 'px'); + } + + popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px'); + } else { + popoverSelection.style('left', null).style('top', null); + } + + function getFrame(node) { + var positionStyle = select(node).style('position'); + + if (positionStyle === 'absolute' || positionStyle === 'static') { + return { + x: node.offsetLeft - scrollLeft, + y: node.offsetTop - scrollTop, + w: node.offsetWidth, + h: node.offsetHeight + }; + } else { + return { + x: 0, + y: 0, + w: node.offsetWidth, + h: node.offsetHeight + }; + } + } + } + + function hide() { + var anchor = select(this); + + if (_displayType.apply(this, arguments) === 'clickFocus') { + anchor.classed('active', false); + } + + anchor.selectAll('.popover-' + _id).classed('in', false); + } + + function toggle() { + if (select(this).select('.popover-' + _id).classed('in')) { + hide.apply(this, arguments); + } else { + show.apply(this, arguments); + } + } + + return popover; + } + + function uiTooltip(klass) { + var tooltip = uiPopover((klass || '') + ' tooltip').displayType('hover'); + + var _title = function _title() { + var title = this.getAttribute('data-original-title'); + + if (title) { + return title; + } else { + title = this.getAttribute('title'); + this.removeAttribute('title'); + this.setAttribute('data-original-title', title); + } + + return title; + }; + + var _heading = utilFunctor(null); + + var _keys = utilFunctor(null); + + tooltip.title = function (val) { + if (!arguments.length) return _title; + _title = utilFunctor(val); + return tooltip; + }; + + tooltip.heading = function (val) { + if (!arguments.length) return _heading; + _heading = utilFunctor(val); + return tooltip; + }; + + tooltip.keys = function (val) { + if (!arguments.length) return _keys; + _keys = utilFunctor(val); + return tooltip; + }; + + tooltip.content(function () { + var heading = _heading.apply(this, arguments); + + var text = _title.apply(this, arguments); + + var keys = _keys.apply(this, arguments); + + return function (selection) { + var headingSelect = selection.selectAll('.tooltip-heading').data(heading ? [heading] : []); + headingSelect.exit().remove(); + headingSelect.enter().append('div').attr('class', 'tooltip-heading').merge(headingSelect).html(heading); + var textSelect = selection.selectAll('.tooltip-text').data(text ? [text] : []); + textSelect.exit().remove(); + textSelect.enter().append('div').attr('class', 'tooltip-text').merge(textSelect).html(text); + var keyhintWrap = selection.selectAll('.keyhint-wrap').data(keys && keys.length ? [0] : []); + keyhintWrap.exit().remove(); + var keyhintWrapEnter = keyhintWrap.enter().append('div').attr('class', 'keyhint-wrap'); + keyhintWrapEnter.append('span').html(_t.html('tooltip_keyhint')); + keyhintWrap = keyhintWrapEnter.merge(keyhintWrap); + keyhintWrap.selectAll('kbd.shortcut').data(keys && keys.length ? keys : []).enter().append('kbd').attr('class', 'shortcut').html(function (d) { + return d; + }); + }; + }); + return tooltip; + } + + function uiEditMenu(context) { + var dispatch = dispatch$8('toggled'); + + var _menu = select(null); + + var _operations = []; // the position the menu should be displayed relative to + + var _anchorLoc = [0, 0]; + var _anchorLocLonLat = [0, 0]; // a string indicating how the menu was opened + + var _triggerType = ''; + var _vpTopMargin = 85; // viewport top margin + + var _vpBottomMargin = 45; // viewport bottom margin + + var _vpSideMargin = 35; // viewport side margin + + var _menuTop = false; + + var _menuHeight; + + var _menuWidth; // hardcode these values to make menu positioning easier + + + var _verticalPadding = 4; // see also `.edit-menu .tooltip` CSS; include margin + + var _tooltipWidth = 210; // offset the menu slightly from the target location + + var _menuSideMargin = 10; + var _tooltips = []; + + var editMenu = function editMenu(selection) { + var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen'); + + var ops = _operations.filter(function (op) { + return !isTouchMenu || !op.mouseOnly; + }); + + if (!ops.length) return; + _tooltips = []; // Position the menu above the anchor for stylus and finger input + // since the mapper's hand likely obscures the screen below the anchor + + _menuTop = isTouchMenu; // Show labels for touch input since there aren't hover tooltips + + var showLabels = isTouchMenu; + var buttonHeight = showLabels ? 32 : 34; + + if (showLabels) { + // Get a general idea of the width based on the length of the label + _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function (op) { + return op.title.length; + }))); + } else { + _menuWidth = 44; + } + + _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight; + _menu = selection.append('div').attr('class', 'edit-menu').classed('touch-menu', isTouchMenu).style('padding', _verticalPadding + 'px 0'); + + var buttons = _menu.selectAll('.edit-menu-item').data(ops); // enter + + + var buttonsEnter = buttons.enter().append('button').attr('class', function (d) { + return 'edit-menu-item edit-menu-item-' + d.id; + }).style('height', buttonHeight + 'px').on('click', click) // don't listen for `mouseup` because we only care about non-mouse pointer types + .on('pointerup', pointerup).on('pointerdown mousedown', function pointerdown(d3_event) { + // don't let button presses also act as map input - #1869 + d3_event.stopPropagation(); + }).on('mouseenter.highlight', function (d3_event, d) { + if (!d.relatedEntityIds || select(this).classed('disabled')) return; + utilHighlightEntities(d.relatedEntityIds(), true, context); + }).on('mouseleave.highlight', function (d3_event, d) { + if (!d.relatedEntityIds) return; + utilHighlightEntities(d.relatedEntityIds(), false, context); + }); + buttonsEnter.each(function (d) { + var tooltip = uiTooltip().heading(d.title).title(d.tooltip()).keys([d.keys[0]]); + + _tooltips.push(tooltip); + + select(this).call(tooltip).append('div').attr('class', 'icon-wrap').call(svgIcon('#iD-operation-' + d.id, 'operation')); + }); + + if (showLabels) { + buttonsEnter.append('span').attr('class', 'label').html(function (d) { + return d.title; + }); + } // update + + + buttonsEnter.merge(buttons).classed('disabled', function (d) { + return d.disabled(); + }); + updatePosition(); + var initialScale = context.projection.scale(); + context.map().on('move.edit-menu', function () { + if (initialScale !== context.projection.scale()) { + editMenu.close(); + } + }).on('drawn.edit-menu', function (info) { + if (info.full) updatePosition(); + }); + var lastPointerUpType; // `pointerup` is always called before `click` + + function pointerup(d3_event) { + lastPointerUpType = d3_event.pointerType; + } + + function click(d3_event, operation) { + d3_event.stopPropagation(); + + if (operation.relatedEntityIds) { + utilHighlightEntities(operation.relatedEntityIds(), false, context); + } + + if (operation.disabled()) { + if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') { + // there are no tooltips for touch interactions so flash feedback instead + context.ui().flash.duration(4000).iconName('#iD-operation-' + operation.id).iconClass('operation disabled').label(operation.tooltip)(); + } + } else { + if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') { + context.ui().flash.duration(2000).iconName('#iD-operation-' + operation.id).iconClass('operation').label(operation.annotation() || operation.title)(); + } + + operation(); + editMenu.close(); + } + + lastPointerUpType = null; + } + + dispatch.call('toggled', this, true); + }; + + function updatePosition() { + if (!_menu || _menu.empty()) return; + var anchorLoc = context.projection(_anchorLocLonLat); + var viewport = context.surfaceRect(); + + if (anchorLoc[0] < 0 || anchorLoc[0] > viewport.width || anchorLoc[1] < 0 || anchorLoc[1] > viewport.height) { + // close the menu if it's gone offscreen + editMenu.close(); + return; + } + + var menuLeft = displayOnLeft(viewport); + var offset = [0, 0]; + offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin; + + if (_menuTop) { + if (anchorLoc[1] - _menuHeight < _vpTopMargin) { + // menu is near top viewport edge, shift downward + offset[1] = -anchorLoc[1] + _vpTopMargin; + } else { + offset[1] = -_menuHeight; + } + } else { + if (anchorLoc[1] + _menuHeight > viewport.height - _vpBottomMargin) { + // menu is near bottom viewport edge, shift upwards + offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin; + } else { + offset[1] = 0; + } + } + + var origin = geoVecAdd(anchorLoc, offset); + + _menu.style('left', origin[0] + 'px').style('top', origin[1] + 'px'); + + var tooltipSide = tooltipPosition(viewport, menuLeft); + + _tooltips.forEach(function (tooltip) { + tooltip.placement(tooltipSide); + }); + + function displayOnLeft(viewport) { + if (_mainLocalizer.textDirection() === 'ltr') { + if (anchorLoc[0] + _menuSideMargin + _menuWidth > viewport.width - _vpSideMargin) { + // right menu would be too close to the right viewport edge, go left + return true; + } // prefer right menu + + + return false; + } else { + // rtl + if (anchorLoc[0] - _menuSideMargin - _menuWidth < _vpSideMargin) { + // left menu would be too close to the left viewport edge, go right + return false; + } // prefer left menu + + + return true; + } + } + + function tooltipPosition(viewport, menuLeft) { + if (_mainLocalizer.textDirection() === 'ltr') { + if (menuLeft) { + // if there's not room for a right-side menu then there definitely + // isn't room for right-side tooltips + return 'left'; + } + + if (anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth > viewport.width - _vpSideMargin) { + // right tooltips would be too close to the right viewport edge, go left + return 'left'; + } // prefer right tooltips + + + return 'right'; + } else { + // rtl + if (!menuLeft) { + return 'right'; + } + + if (anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth < _vpSideMargin) { + // left tooltips would be too close to the left viewport edge, go right + return 'right'; + } // prefer left tooltips + + + return 'left'; + } + } + } + + editMenu.close = function () { + context.map().on('move.edit-menu', null).on('drawn.edit-menu', null); + + _menu.remove(); + + _tooltips = []; + dispatch.call('toggled', this, false); + }; + + editMenu.anchorLoc = function (val) { + if (!arguments.length) return _anchorLoc; + _anchorLoc = val; + _anchorLocLonLat = context.projection.invert(_anchorLoc); + return editMenu; + }; + + editMenu.triggerType = function (val) { + if (!arguments.length) return _triggerType; + _triggerType = val; + return editMenu; + }; + + editMenu.operations = function (val) { + if (!arguments.length) return _operations; + _operations = val; + return editMenu; + }; + + return utilRebind(editMenu, dispatch, 'on'); + } + + function uiFeatureInfo(context) { + function update(selection) { + var features = context.features(); + var stats = features.stats(); + var count = 0; + var hiddenList = features.hidden().map(function (k) { + if (stats[k]) { + count += stats[k]; + return _t('inspector.title_count', { + title: _t.html('feature.' + k + '.description'), + count: stats[k] + }); + } + + return null; + }).filter(Boolean); + selection.html(''); + + if (hiddenList.length) { + var tooltipBehavior = uiTooltip().placement('top').title(function () { + return hiddenList.join('
    '); + }); + selection.append('a').attr('class', 'chip').attr('href', '#').html(_t.html('feature_info.hidden_warning', { + count: count + })).call(tooltipBehavior).on('click', function (d3_event) { + tooltipBehavior.hide(); + d3_event.preventDefault(); // open the Map Data pane + + context.ui().togglePanes(context.container().select('.map-panes .map-data-pane')); + }); + } + + selection.classed('hide', !hiddenList.length); + } + + return function (selection) { + update(selection); + context.features().on('change.feature_info', function () { + update(selection); + }); + }; + } + + function uiFlash(context) { + var _flashTimer; + + var _duration = 2000; + var _iconName = '#iD-icon-no'; + var _iconClass = 'disabled'; + var _label = ''; + + function flash() { + if (_flashTimer) { + _flashTimer.stop(); + } + + context.container().select('.main-footer-wrap').classed('footer-hide', true).classed('footer-show', false); + context.container().select('.flash-wrap').classed('footer-hide', false).classed('footer-show', true); + var content = context.container().select('.flash-wrap').selectAll('.flash-content').data([0]); // Enter + + var contentEnter = content.enter().append('div').attr('class', 'flash-content'); + var iconEnter = contentEnter.append('svg').attr('class', 'flash-icon icon').append('g').attr('transform', 'translate(10,10)'); + iconEnter.append('circle').attr('r', 9); + iconEnter.append('use').attr('transform', 'translate(-7,-7)').attr('width', '14').attr('height', '14'); + contentEnter.append('div').attr('class', 'flash-text'); // Update + + content = content.merge(contentEnter); + content.selectAll('.flash-icon').attr('class', 'icon flash-icon ' + (_iconClass || '')); + content.selectAll('.flash-icon use').attr('xlink:href', _iconName); + content.selectAll('.flash-text').attr('class', 'flash-text').html(_label); + _flashTimer = d3_timeout(function () { + _flashTimer = null; + context.container().select('.main-footer-wrap').classed('footer-hide', false).classed('footer-show', true); + context.container().select('.flash-wrap').classed('footer-hide', true).classed('footer-show', false); + }, _duration); + return content; + } + + flash.duration = function (_) { + if (!arguments.length) return _duration; + _duration = _; + return flash; + }; + + flash.label = function (_) { + if (!arguments.length) return _label; + _label = _; + return flash; + }; + + flash.iconName = function (_) { + if (!arguments.length) return _iconName; + _iconName = _; + return flash; + }; + + flash.iconClass = function (_) { + if (!arguments.length) return _iconClass; + _iconClass = _; + return flash; + }; + + return flash; + } + + function uiFullScreen(context) { + var element = context.container().node(); // var button = d3_select(null); + + function getFullScreenFn() { + if (element.requestFullscreen) { + return element.requestFullscreen; + } else if (element.msRequestFullscreen) { + return element.msRequestFullscreen; + } else if (element.mozRequestFullScreen) { + return element.mozRequestFullScreen; + } else if (element.webkitRequestFullscreen) { + return element.webkitRequestFullscreen; + } + } + + function getExitFullScreenFn() { + if (document.exitFullscreen) { + return document.exitFullscreen; + } else if (document.msExitFullscreen) { + return document.msExitFullscreen; + } else if (document.mozCancelFullScreen) { + return document.mozCancelFullScreen; + } else if (document.webkitExitFullscreen) { + return document.webkitExitFullscreen; + } + } + + function isFullScreen() { + return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement; + } + + function isSupported() { + return !!getFullScreenFn(); + } + + function fullScreen(d3_event) { + d3_event.preventDefault(); + + if (!isFullScreen()) { + // button.classed('active', true); + getFullScreenFn().apply(element); + } else { + // button.classed('active', false); + getExitFullScreenFn().apply(document); + } + } + + return function () { + // selection) { + if (!isSupported()) return; // button = selection.append('button') + // .attr('title', t('full_screen')) + // .on('click', fullScreen) + // .call(tooltip); + // button.append('span') + // .attr('class', 'icon full-screen'); + + var detected = utilDetect(); + var keys = detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11']; + context.keybinding().on(keys, fullScreen); + }; + } + + function uiGeolocate(context) { + var _geolocationOptions = { + // prioritize speed and power usage over precision + enableHighAccuracy: false, + // don't hang indefinitely getting the location + timeout: 6000 // 6sec + + }; + + var _locating = uiLoading(context).message(_t.html('geolocate.locating')).blocking(true); + + var _layer = context.layers().layer('geolocate'); + + var _position; + + var _extent; + + var _timeoutID; + + var _button = select(null); + + function click() { + if (context.inIntro()) return; + + if (!_layer.enabled() && !_locating.isShown()) { + // This timeout ensures that we still call finish() even if + // the user declines to share their location in Firefox + _timeoutID = setTimeout(error, 10000 + /* 10sec */ + ); + context.container().call(_locating); // get the latest position even if we already have one + + navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions); + } else { + _locating.close(); + + _layer.enabled(null, false); + + updateButtonState(); + } + } + + function zoomTo() { + context.enter(modeBrowse(context)); + var map = context.map(); + + _layer.enabled(_position, true); + + updateButtonState(); + map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent))); + } + + function success(geolocation) { + _position = geolocation; + var coords = _position.coords; + _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy); + zoomTo(); + finish(); + } + + function error() { + if (_position) { + // use the position from a previous call if we have one + zoomTo(); + } else { + context.ui().flash.label(_t.html('geolocate.location_unavailable')).iconName('#iD-icon-geolocate')(); + } + + finish(); + } + + function finish() { + _locating.close(); // unblock ui + + + if (_timeoutID) { + clearTimeout(_timeoutID); + } + + _timeoutID = undefined; + } + + function updateButtonState() { + _button.classed('active', _layer.enabled()); + } + + return function (selection) { + if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) return; + _button = selection.append('button').on('click', click).call(svgIcon('#iD-icon-geolocate', 'light')).call(uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(_t.html('geolocate.title')).keys([_t('geolocate.key')])); + context.keybinding().on(_t('geolocate.key'), click); + }; + } + + function uiPanelBackground(context) { + var background = context.background(); + var _currSourceName = null; + var _metadata = {}; + var _metadataKeys = ['zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy']; + + var debouncedRedraw = debounce(redraw, 250); + + function redraw(selection) { + var source = background.baseLayerSource(); + if (!source) return; + var isDG = source.id.match(/^DigitalGlobe/i) !== null; + var sourceLabel = source.label(); + + if (_currSourceName !== sourceLabel) { + _currSourceName = sourceLabel; + _metadata = {}; + } + + selection.html(''); + var list = selection.append('ul').attr('class', 'background-info'); + list.append('li').html(_currSourceName); + + _metadataKeys.forEach(function (k) { + // DigitalGlobe vintage is available in raster layers for now. + if (isDG && k === 'vintage') return; + list.append('li').attr('class', 'background-info-list-' + k).classed('hide', !_metadata[k]).html(_t.html('info_panels.background.' + k) + ':').append('span').attr('class', 'background-info-span-' + k).html(_metadata[k]); + }); + + debouncedGetMetadata(selection); + var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles'; + selection.append('a').html(_t.html('info_panels.background.' + toggleTiles)).attr('href', '#').attr('class', 'button button-toggle-tiles').on('click', function (d3_event) { + d3_event.preventDefault(); + context.setDebug('tile', !context.getDebug('tile')); + selection.call(redraw); + }); + + if (isDG) { + var key = source.id + '-vintage'; + var sourceVintage = context.background().findSource(key); + var showsVintage = context.background().showsLayer(sourceVintage); + var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage'; + selection.append('a').html(_t.html('info_panels.background.' + toggleVintage)).attr('href', '#').attr('class', 'button button-toggle-vintage').on('click', function (d3_event) { + d3_event.preventDefault(); + context.background().toggleOverlayLayer(sourceVintage); + selection.call(redraw); + }); + } // disable if necessary + + + ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function (layerId) { + if (source.id !== layerId) { + var key = layerId + '-vintage'; + var sourceVintage = context.background().findSource(key); + + if (context.background().showsLayer(sourceVintage)) { + context.background().toggleOverlayLayer(sourceVintage); + } + } + }); + } + + var debouncedGetMetadata = debounce(getMetadata, 250); + + function getMetadata(selection) { + var tile = context.container().select('.layer-background img.tile-center'); // tile near viewport center + + if (tile.empty()) return; + var sourceName = _currSourceName; + var d = tile.datum(); + var zoom = d && d.length >= 3 && d[2] || Math.floor(context.map().zoom()); + var center = context.map().center(); // update zoom + + _metadata.zoom = String(zoom); + selection.selectAll('.background-info-list-zoom').classed('hide', false).selectAll('.background-info-span-zoom').html(_metadata.zoom); + if (!d || !d.length >= 3) return; + background.baseLayerSource().getMetadata(center, d, function (err, result) { + if (err || _currSourceName !== sourceName) return; // update vintage + + var vintage = result.vintage; + _metadata.vintage = vintage && vintage.range || _t('info_panels.background.unknown'); + selection.selectAll('.background-info-list-vintage').classed('hide', false).selectAll('.background-info-span-vintage').html(_metadata.vintage); // update other _metadata + + _metadataKeys.forEach(function (k) { + if (k === 'zoom' || k === 'vintage') return; // done already + + var val = result[k]; + _metadata[k] = val; + selection.selectAll('.background-info-list-' + k).classed('hide', !val).selectAll('.background-info-span-' + k).html(val); + }); + }); + } + + var panel = function panel(selection) { + selection.call(redraw); + context.map().on('drawn.info-background', function () { + selection.call(debouncedRedraw); + }).on('move.info-background', function () { + selection.call(debouncedGetMetadata); + }); + }; + + panel.off = function () { + context.map().on('drawn.info-background', null).on('move.info-background', null); + }; + + panel.id = 'background'; + panel.label = _t.html('info_panels.background.title'); + panel.key = _t('info_panels.background.key'); + return panel; + } + + function uiPanelHistory(context) { + var osm; + + function displayTimestamp(timestamp) { + if (!timestamp) return _t('info_panels.history.unknown'); + var options = { + day: 'numeric', + month: 'short', + year: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric' + }; + var d = new Date(timestamp); + if (isNaN(d.getTime())) return _t('info_panels.history.unknown'); + return d.toLocaleString(_mainLocalizer.localeCode(), options); + } + + function displayUser(selection, userName) { + if (!userName) { + selection.append('span').html(_t.html('info_panels.history.unknown')); + return; + } + + selection.append('span').attr('class', 'user-name').html(userName); + var links = selection.append('div').attr('class', 'links'); + + if (osm) { + links.append('a').attr('class', 'user-osm-link').attr('href', osm.userURL(userName)).attr('target', '_blank').html('OSM'); + } + + links.append('a').attr('class', 'user-hdyc-link').attr('href', 'https://hdyc.neis-one.org/?' + userName).attr('target', '_blank').attr('tabindex', -1).html('HDYC'); + } + + function displayChangeset(selection, changeset) { + if (!changeset) { + selection.append('span').html(_t.html('info_panels.history.unknown')); + return; + } + + selection.append('span').attr('class', 'changeset-id').html(changeset); + var links = selection.append('div').attr('class', 'links'); + + if (osm) { + links.append('a').attr('class', 'changeset-osm-link').attr('href', osm.changesetURL(changeset)).attr('target', '_blank').html('OSM'); + } + + links.append('a').attr('class', 'changeset-osmcha-link').attr('href', 'https://osmcha.org/changesets/' + changeset).attr('target', '_blank').html('OSMCha'); + links.append('a').attr('class', 'changeset-achavi-link').attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset).attr('target', '_blank').html('Achavi'); + } + + function redraw(selection) { + var selectedNoteID = context.selectedNoteID(); + osm = context.connection(); + var selected, note, entity; + + if (selectedNoteID && osm) { + // selected 1 note + selected = [_t('note.note') + ' ' + selectedNoteID]; + note = osm.getNote(selectedNoteID); + } else { + // selected 1..n entities + selected = context.selectedIDs().filter(function (e) { + return context.hasEntity(e); + }); + + if (selected.length) { + entity = context.entity(selected[0]); + } + } + + var singular = selected.length === 1 ? selected[0] : null; + selection.html(''); + selection.append('h4').attr('class', 'history-heading').html(singular || _t.html('info_panels.selected', { + n: selected.length + })); + if (!singular) return; + + if (entity) { + selection.call(redrawEntity, entity); + } else if (note) { + selection.call(redrawNote, note); + } + } + + function redrawNote(selection, note) { + if (!note || note.isNew()) { + selection.append('div').html(_t.html('info_panels.history.note_no_history')); + return; + } + + var list = selection.append('ul'); + list.append('li').html(_t.html('info_panels.history.note_comments') + ':').append('span').html(note.comments.length); + + if (note.comments.length) { + list.append('li').html(_t.html('info_panels.history.note_created_date') + ':').append('span').html(displayTimestamp(note.comments[0].date)); + list.append('li').html(_t.html('info_panels.history.note_created_user') + ':').call(displayUser, note.comments[0].user); + } + + if (osm) { + selection.append('a').attr('class', 'view-history-on-osm').attr('target', '_blank').attr('href', osm.noteURL(note)).call(svgIcon('#iD-icon-out-link', 'inline')).append('span').html(_t.html('info_panels.history.note_link_text')); + } + } + + function redrawEntity(selection, entity) { + if (!entity || entity.isNew()) { + selection.append('div').html(_t.html('info_panels.history.no_history')); + return; + } + + var links = selection.append('div').attr('class', 'links'); + + if (osm) { + links.append('a').attr('class', 'view-history-on-osm').attr('href', osm.historyURL(entity)).attr('target', '_blank').attr('title', _t('info_panels.history.link_text')).html('OSM'); + } + + links.append('a').attr('class', 'pewu-history-viewer-link').attr('href', 'https://pewu.github.io/osm-history/#/' + entity.type + '/' + entity.osmId()).attr('target', '_blank').attr('tabindex', -1).html('PeWu'); + var list = selection.append('ul'); + list.append('li').html(_t.html('info_panels.history.version') + ':').append('span').html(entity.version); + list.append('li').html(_t.html('info_panels.history.last_edit') + ':').append('span').html(displayTimestamp(entity.timestamp)); + list.append('li').html(_t.html('info_panels.history.edited_by') + ':').call(displayUser, entity.user); + list.append('li').html(_t.html('info_panels.history.changeset') + ':').call(displayChangeset, entity.changeset); + } + + var panel = function panel(selection) { + selection.call(redraw); + context.map().on('drawn.info-history', function () { + selection.call(redraw); + }); + context.on('enter.info-history', function () { + selection.call(redraw); + }); + }; + + panel.off = function () { + context.map().on('drawn.info-history', null); + context.on('enter.info-history', null); + }; + + panel.id = 'history'; + panel.label = _t.html('info_panels.history.title'); + panel.key = _t('info_panels.history.key'); + return panel; + } + + var OSM_PRECISION = 7; + /** + * Returns a localized representation of the given length measurement. + * + * @param {Number} m area in meters + * @param {Boolean} isImperial true for U.S. customary units; false for metric + */ + + function displayLength(m, isImperial) { + var d = m * (isImperial ? 3.28084 : 1); + var unit; + + if (isImperial) { + if (d >= 5280) { + d /= 5280; + unit = 'miles'; + } else { + unit = 'feet'; + } + } else { + if (d >= 1000) { + d /= 1000; + unit = 'kilometers'; + } else { + unit = 'meters'; + } + } + + return _t('units.' + unit, { + quantity: d.toLocaleString(_mainLocalizer.localeCode(), { + maximumSignificantDigits: 4 + }) + }); + } + /** + * Returns a localized representation of the given area measurement. + * + * @param {Number} m2 area in square meters + * @param {Boolean} isImperial true for U.S. customary units; false for metric + */ + + function displayArea(m2, isImperial) { + var locale = _mainLocalizer.localeCode(); + var d = m2 * (isImperial ? 10.7639111056 : 1); + var d1, d2, area; + var unit1 = ''; + var unit2 = ''; + + if (isImperial) { + if (d >= 6969600) { + // > 0.25mi² show mi² + d1 = d / 27878400; + unit1 = 'square_miles'; + } else { + d1 = d; + unit1 = 'square_feet'; + } + + if (d > 4356 && d < 43560000) { + // 0.1 - 1000 acres + d2 = d / 43560; + unit2 = 'acres'; + } + } else { + if (d >= 250000) { + // > 0.25km² show km² + d1 = d / 1000000; + unit1 = 'square_kilometers'; + } else { + d1 = d; + unit1 = 'square_meters'; + } + + if (d > 1000 && d < 10000000) { + // 0.1 - 1000 hectares + d2 = d / 10000; + unit2 = 'hectares'; + } + } + + area = _t('units.' + unit1, { + quantity: d1.toLocaleString(locale, { + maximumSignificantDigits: 4 + }) + }); + + if (d2) { + return _t('units.area_pair', { + area1: area, + area2: _t('units.' + unit2, { + quantity: d2.toLocaleString(locale, { + maximumSignificantDigits: 2 + }) + }) + }); + } else { + return area; + } + } + + function wrap(x, min, max) { + var d = max - min; + return ((x - min) % d + d) % d + min; + } + + function clamp(x, min, max) { + return Math.max(min, Math.min(x, max)); + } + + function displayCoordinate(deg, pos, neg) { + var locale = _mainLocalizer.localeCode(); + var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60; + var sec = (min - Math.floor(min)) * 60; + var displayDegrees = _t('units.arcdegrees', { + quantity: Math.floor(Math.abs(deg)).toLocaleString(locale) + }); + var displayCoordinate; + + if (Math.floor(sec) > 0) { + displayCoordinate = displayDegrees + _t('units.arcminutes', { + quantity: Math.floor(min).toLocaleString(locale) + }) + _t('units.arcseconds', { + quantity: Math.round(sec).toLocaleString(locale) + }); + } else if (Math.floor(min) > 0) { + displayCoordinate = displayDegrees + _t('units.arcminutes', { + quantity: Math.round(min).toLocaleString(locale) + }); + } else { + displayCoordinate = _t('units.arcdegrees', { + quantity: Math.round(Math.abs(deg)).toLocaleString(locale) + }); + } + + if (deg === 0) { + return displayCoordinate; + } else { + return _t('units.coordinate', { + coordinate: displayCoordinate, + direction: _t('units.' + (deg > 0 ? pos : neg)) + }); + } + } + /** + * Returns given coordinate pair in degree-minute-second format. + * + * @param {Array} coord longitude and latitude + */ + + + function dmsCoordinatePair(coord) { + return _t('units.coordinate_pair', { + latitude: displayCoordinate(clamp(coord[1], -90, 90), 'north', 'south'), + longitude: displayCoordinate(wrap(coord[0], -180, 180), 'east', 'west') + }); + } + /** + * Returns the given coordinate pair in decimal format. + * note: unlocalized to avoid comma ambiguity - see #4765 + * + * @param {Array} coord longitude and latitude + */ + + function decimalCoordinatePair(coord) { + return _t('units.coordinate_pair', { + latitude: clamp(coord[1], -90, 90).toFixed(OSM_PRECISION), + longitude: wrap(coord[0], -180, 180).toFixed(OSM_PRECISION) + }); + } + + function uiPanelLocation(context) { + var currLocation = ''; + + function redraw(selection) { + selection.html(''); + var list = selection.append('ul'); // Mouse coordinates + + var coord = context.map().mouseCoordinates(); + + if (coord.some(isNaN)) { + coord = context.map().center(); + } + + list.append('li').html(dmsCoordinatePair(coord)).append('li').html(decimalCoordinatePair(coord)); // Location Info + + selection.append('div').attr('class', 'location-info').html(currLocation || ' '); + debouncedGetLocation(selection, coord); + } + + var debouncedGetLocation = debounce(getLocation, 250); + + function getLocation(selection, coord) { + if (!services.geocoder) { + currLocation = _t('info_panels.location.unknown_location'); + selection.selectAll('.location-info').html(currLocation); + } else { + services.geocoder.reverse(coord, function (err, result) { + currLocation = result ? result.display_name : _t('info_panels.location.unknown_location'); + selection.selectAll('.location-info').html(currLocation); + }); + } + } + + var panel = function panel(selection) { + selection.call(redraw); + context.surface().on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function () { + selection.call(redraw); + }); + }; + + panel.off = function () { + context.surface().on('.info-location', null); + }; + + panel.id = 'location'; + panel.label = _t.html('info_panels.location.title'); + panel.key = _t('info_panels.location.key'); + return panel; + } + + function uiPanelMeasurement(context) { + function radiansToMeters(r) { + // using WGS84 authalic radius (6371007.1809 m) + return r * 6371007.1809; + } + + function steradiansToSqmeters(r) { + // http://gis.stackexchange.com/a/124857/40446 + return r / (4 * Math.PI) * 510065621724000; + } + + function toLineString(feature) { + if (feature.type === 'LineString') return feature; + var result = { + type: 'LineString', + coordinates: [] + }; + + if (feature.type === 'Polygon') { + result.coordinates = feature.coordinates[0]; + } else if (feature.type === 'MultiPolygon') { + result.coordinates = feature.coordinates[0][0]; + } + + return result; + } + + var _isImperial = !_mainLocalizer.usesMetric(); + + function redraw(selection) { + var graph = context.graph(); + var selectedNoteID = context.selectedNoteID(); + var osm = services.osm; + var localeCode = _mainLocalizer.localeCode(); + var heading; + var center, location, centroid; + var closed, geometry; + var totalNodeCount, + length = 0, + area = 0, + distance; + + if (selectedNoteID && osm) { + // selected 1 note + var note = osm.getNote(selectedNoteID); + heading = _t('note.note') + ' ' + selectedNoteID; + location = note.loc; + geometry = 'note'; + } else { + // selected 1..n entities + var selectedIDs = context.selectedIDs().filter(function (id) { + return context.hasEntity(id); + }); + var selected = selectedIDs.map(function (id) { + return context.entity(id); + }); + heading = selected.length === 1 ? selected[0].id : _t('info_panels.selected', { + n: selected.length + }); + + if (selected.length) { + var extent = geoExtent(); + + for (var i in selected) { + var entity = selected[i]; + + extent._extend(entity.extent(graph)); + + geometry = entity.geometry(graph); + + if (geometry === 'line' || geometry === 'area') { + closed = entity.type === 'relation' || entity.isClosed() && !entity.isDegenerate(); + var feature = entity.asGeoJSON(graph); + length += radiansToMeters(d3_geoLength(toLineString(feature))); + centroid = d3_geoPath(context.projection).centroid(entity.asGeoJSON(graph)); + centroid = centroid && context.projection.invert(centroid); + + if (!centroid || !isFinite(centroid[0]) || !isFinite(centroid[1])) { + centroid = entity.extent(graph).center(); + } + + if (closed) { + area += steradiansToSqmeters(entity.area(graph)); + } + } + } + + if (selected.length > 1) { + geometry = null; + closed = null; + centroid = null; + } + + if (selected.length === 2 && selected[0].type === 'node' && selected[1].type === 'node') { + distance = geoSphericalDistance(selected[0].loc, selected[1].loc); + } + + if (selected.length === 1 && selected[0].type === 'node') { + location = selected[0].loc; + } else { + totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length; + } + + if (!location && !centroid) { + center = extent.center(); + } + } + } + + selection.html(''); + + if (heading) { + selection.append('h4').attr('class', 'measurement-heading').html(heading); + } + + var list = selection.append('ul'); + var coordItem; + + if (geometry) { + list.append('li').html(_t.html('info_panels.measurement.geometry') + ':').append('span').html(closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry)); + } + + if (totalNodeCount) { + list.append('li').html(_t.html('info_panels.measurement.node_count') + ':').append('span').html(totalNodeCount.toLocaleString(localeCode)); + } + + if (area) { + list.append('li').html(_t.html('info_panels.measurement.area') + ':').append('span').html(displayArea(area, _isImperial)); + } + + if (length) { + list.append('li').html(_t.html('info_panels.measurement.' + (closed ? 'perimeter' : 'length')) + ':').append('span').html(displayLength(length, _isImperial)); + } + + if (typeof distance === 'number') { + list.append('li').html(_t.html('info_panels.measurement.distance') + ':').append('span').html(displayLength(distance, _isImperial)); + } + + if (location) { + coordItem = list.append('li').html(_t.html('info_panels.measurement.location') + ':'); + coordItem.append('span').html(dmsCoordinatePair(location)); + coordItem.append('span').html(decimalCoordinatePair(location)); + } + + if (centroid) { + coordItem = list.append('li').html(_t.html('info_panels.measurement.centroid') + ':'); + coordItem.append('span').html(dmsCoordinatePair(centroid)); + coordItem.append('span').html(decimalCoordinatePair(centroid)); + } + + if (center) { + coordItem = list.append('li').html(_t.html('info_panels.measurement.center') + ':'); + coordItem.append('span').html(dmsCoordinatePair(center)); + coordItem.append('span').html(decimalCoordinatePair(center)); + } + + if (length || area || typeof distance === 'number') { + var toggle = _isImperial ? 'imperial' : 'metric'; + selection.append('a').html(_t.html('info_panels.measurement.' + toggle)).attr('href', '#').attr('class', 'button button-toggle-units').on('click', function (d3_event) { + d3_event.preventDefault(); + _isImperial = !_isImperial; + selection.call(redraw); + }); + } + } + + var panel = function panel(selection) { + selection.call(redraw); + context.map().on('drawn.info-measurement', function () { + selection.call(redraw); + }); + context.on('enter.info-measurement', function () { + selection.call(redraw); + }); + }; + + panel.off = function () { + context.map().on('drawn.info-measurement', null); + context.on('enter.info-measurement', null); + }; + + panel.id = 'measurement'; + panel.label = _t.html('info_panels.measurement.title'); + panel.key = _t('info_panels.measurement.key'); + return panel; + } + + var uiInfoPanels = { + background: uiPanelBackground, + history: uiPanelHistory, + location: uiPanelLocation, + measurement: uiPanelMeasurement + }; + + function uiInfo(context) { + var ids = Object.keys(uiInfoPanels); + var wasActive = ['measurement']; + var panels = {}; + var active = {}; // create panels + + ids.forEach(function (k) { + if (!panels[k]) { + panels[k] = uiInfoPanels[k](context); + active[k] = false; + } + }); + + function info(selection) { + function redraw() { + var activeids = ids.filter(function (k) { + return active[k]; + }).sort(); + var containers = infoPanels.selectAll('.panel-container').data(activeids, function (k) { + return k; + }); + containers.exit().style('opacity', 1).transition().duration(200).style('opacity', 0).on('end', function (d) { + select(this).call(panels[d].off).remove(); + }); + var enter = containers.enter().append('div').attr('class', function (d) { + return 'fillD2 panel-container panel-container-' + d; + }); + enter.style('opacity', 0).transition().duration(200).style('opacity', 1); + var title = enter.append('div').attr('class', 'panel-title fillD2'); + title.append('h3').html(function (d) { + return panels[d].label; + }); + title.append('button').attr('class', 'close').on('click', function (d3_event, d) { + d3_event.stopImmediatePropagation(); + d3_event.preventDefault(); + info.toggle(d); + }).call(svgIcon('#iD-icon-close')); + enter.append('div').attr('class', function (d) { + return 'panel-content panel-content-' + d; + }); // redraw the panels + + infoPanels.selectAll('.panel-content').each(function (d) { + select(this).call(panels[d]); + }); + } + + info.toggle = function (which) { + var activeids = ids.filter(function (k) { + return active[k]; + }); + + if (which) { + // toggle one + active[which] = !active[which]; + + if (activeids.length === 1 && activeids[0] === which) { + // none active anymore + wasActive = [which]; + } + + context.container().select('.' + which + '-panel-toggle-item').classed('active', active[which]).select('input').property('checked', active[which]); + } else { + // toggle all + if (activeids.length) { + wasActive = activeids; + activeids.forEach(function (k) { + active[k] = false; + }); + } else { + wasActive.forEach(function (k) { + active[k] = true; + }); + } + } + + redraw(); + }; + + var infoPanels = selection.selectAll('.info-panels').data([0]); + infoPanels = infoPanels.enter().append('div').attr('class', 'info-panels').merge(infoPanels); + redraw(); + context.keybinding().on(uiCmd('⌘' + _t('info_panels.key')), function (d3_event) { + d3_event.stopImmediatePropagation(); + d3_event.preventDefault(); + info.toggle(); + }); + ids.forEach(function (k) { + var key = _t('info_panels.' + k + '.key', { + "default": null + }); + if (!key) return; + context.keybinding().on(uiCmd('⌘⇧' + key), function (d3_event) { + d3_event.stopImmediatePropagation(); + d3_event.preventDefault(); + info.toggle(k); + }); + }); + } + + return info; + } + + function pointBox(loc, context) { + var rect = context.surfaceRect(); + var point = context.curtainProjection(loc); + return { + left: point[0] + rect.left - 40, + top: point[1] + rect.top - 60, + width: 80, + height: 90 + }; + } + function pad(locOrBox, padding, context) { + var box; + + if (locOrBox instanceof Array) { + var rect = context.surfaceRect(); + var point = context.curtainProjection(locOrBox); + box = { + left: point[0] + rect.left, + top: point[1] + rect.top + }; + } else { + box = locOrBox; + } + + return { + left: box.left - padding, + top: box.top - padding, + width: (box.width || 0) + 2 * padding, + height: (box.width || 0) + 2 * padding + }; + } + function icon(name, svgklass, useklass) { + return '' + ''; + } + var helpStringReplacements; // Returns the localized HTML element for `id` with a standardized set of icon, key, and + // label replacements suitable for tutorials and documentation. Optionally supplemented + // with custom `replacements` + + function helpHtml(id, replacements) { + // only load these the first time + if (!helpStringReplacements) { + helpStringReplacements = { + // insert icons corresponding to various UI elements + point_icon: icon('#iD-icon-point', 'inline'), + line_icon: icon('#iD-icon-line', 'inline'), + area_icon: icon('#iD-icon-area', 'inline'), + note_icon: icon('#iD-icon-note', 'inline add-note'), + plus: icon('#iD-icon-plus', 'inline'), + minus: icon('#iD-icon-minus', 'inline'), + layers_icon: icon('#iD-icon-layers', 'inline'), + data_icon: icon('#iD-icon-data', 'inline'), + inspect: icon('#iD-icon-inspect', 'inline'), + help_icon: icon('#iD-icon-help', 'inline'), + undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'inline'), + redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'inline'), + save_icon: icon('#iD-icon-save', 'inline'), + // operation icons + circularize_icon: icon('#iD-operation-circularize', 'inline operation'), + continue_icon: icon('#iD-operation-continue', 'inline operation'), + copy_icon: icon('#iD-operation-copy', 'inline operation'), + delete_icon: icon('#iD-operation-delete', 'inline operation'), + disconnect_icon: icon('#iD-operation-disconnect', 'inline operation'), + downgrade_icon: icon('#iD-operation-downgrade', 'inline operation'), + extract_icon: icon('#iD-operation-extract', 'inline operation'), + merge_icon: icon('#iD-operation-merge', 'inline operation'), + move_icon: icon('#iD-operation-move', 'inline operation'), + orthogonalize_icon: icon('#iD-operation-orthogonalize', 'inline operation'), + paste_icon: icon('#iD-operation-paste', 'inline operation'), + reflect_long_icon: icon('#iD-operation-reflect-long', 'inline operation'), + reflect_short_icon: icon('#iD-operation-reflect-short', 'inline operation'), + reverse_icon: icon('#iD-operation-reverse', 'inline operation'), + rotate_icon: icon('#iD-operation-rotate', 'inline operation'), + split_icon: icon('#iD-operation-split', 'inline operation'), + straighten_icon: icon('#iD-operation-straighten', 'inline operation'), + // interaction icons + leftclick: icon('#iD-walkthrough-mouse-left', 'inline operation'), + rightclick: icon('#iD-walkthrough-mouse-right', 'inline operation'), + mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'inline operation'), + tap_icon: icon('#iD-walkthrough-tap', 'inline operation'), + doubletap_icon: icon('#iD-walkthrough-doubletap', 'inline operation'), + longpress_icon: icon('#iD-walkthrough-longpress', 'inline operation'), + touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'inline operation'), + pinch_icon: icon('#iD-walkthrough-pinch-apart', 'inline operation'), + // insert keys; may be localized and platform-dependent + shift: uiCmd.display('⇧'), + alt: uiCmd.display('⌥'), + "return": uiCmd.display('↵'), + esc: _t.html('shortcuts.key.esc'), + space: _t.html('shortcuts.key.space'), + add_note_key: _t.html('modes.add_note.key'), + help_key: _t.html('help.key'), + shortcuts_key: _t.html('shortcuts.toggle.key'), + // reference localized UI labels directly so that they'll always match + save: _t.html('save.title'), + undo: _t.html('undo.title'), + redo: _t.html('redo.title'), + upload: _t.html('commit.save'), + point: _t.html('modes.add_point.title'), + line: _t.html('modes.add_line.title'), + area: _t.html('modes.add_area.title'), + note: _t.html('modes.add_note.label'), + circularize: _t.html('operations.circularize.title'), + "continue": _t.html('operations.continue.title'), + copy: _t.html('operations.copy.title'), + "delete": _t.html('operations.delete.title'), + disconnect: _t.html('operations.disconnect.title'), + downgrade: _t.html('operations.downgrade.title'), + extract: _t.html('operations.extract.title'), + merge: _t.html('operations.merge.title'), + move: _t.html('operations.move.title'), + orthogonalize: _t.html('operations.orthogonalize.title'), + paste: _t.html('operations.paste.title'), + reflect_long: _t.html('operations.reflect.title.long'), + reflect_short: _t.html('operations.reflect.title.short'), + reverse: _t.html('operations.reverse.title'), + rotate: _t.html('operations.rotate.title'), + split: _t.html('operations.split.title'), + straighten: _t.html('operations.straighten.title'), + map_data: _t.html('map_data.title'), + osm_notes: _t.html('map_data.layers.notes.title'), + fields: _t.html('inspector.fields'), + tags: _t.html('inspector.tags'), + relations: _t.html('inspector.relations'), + new_relation: _t.html('inspector.new_relation'), + turn_restrictions: _t.html('_tagging.presets.fields.restrictions.label'), + background_settings: _t.html('background.description'), + imagery_offset: _t.html('background.fix_misalignment'), + start_the_walkthrough: _t.html('splash.walkthrough'), + help: _t.html('help.title'), + ok: _t.html('intro.ok') + }; + } + + var reps; + + if (replacements) { + reps = Object.assign(replacements, helpStringReplacements); + } else { + reps = helpStringReplacements; + } + + return _t.html(id, reps) // use keyboard key styling for shortcuts + .replace(/\`(.*?)\`/g, '$1'); + } + + function slugify(text) { + return text.toString().toLowerCase().replace(/\s+/g, '-') // Replace spaces with - + .replace(/[^\w\-]+/g, '') // Remove all non-word chars + .replace(/\-\-+/g, '-') // Replace multiple - with single - + .replace(/^-+/, '') // Trim - from start of text + .replace(/-+$/, ''); // Trim - from end of text + } // console warning for missing walkthrough names + + + var missingStrings = {}; + + function checkKey(key, text) { + if (_t(key, { + "default": undefined + }) === undefined) { + if (missingStrings.hasOwnProperty(key)) return; // warn once + + missingStrings[key] = text; + var missing = key + ': ' + text; + if (typeof console !== 'undefined') console.log(missing); // eslint-disable-line + } + } + + function localize(obj) { + var key; // Assign name if entity has one.. + + var name = obj.tags && obj.tags.name; + + if (name) { + key = 'intro.graph.name.' + slugify(name); + obj.tags.name = _t(key, { + "default": name + }); + checkKey(key, name); + } // Assign street name if entity has one.. + + + var street = obj.tags && obj.tags['addr:street']; + + if (street) { + key = 'intro.graph.name.' + slugify(street); + obj.tags['addr:street'] = _t(key, { + "default": street + }); + checkKey(key, street); // Add address details common across walkthrough.. + + var addrTags = ['block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood', 'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb']; + addrTags.forEach(function (k) { + var key = 'intro.graph.' + k; + var tag = 'addr:' + k; + var val = obj.tags && obj.tags[tag]; + var str = _t(key, { + "default": val + }); + + if (str) { + if (str.match(/^<.*>$/) !== null) { + delete obj.tags[tag]; + } else { + obj.tags[tag] = str; + } + } + }); + } + + return obj; + } // Used to detect squareness.. some duplicataion of code from actionOrthogonalize. + + function isMostlySquare(points) { + // note: uses 15 here instead of the 12 from actionOrthogonalize because + // actionOrthogonalize can actually straighten some larger angles as it iterates + var threshold = 15; // degrees within right or straight + + var lowerBound = Math.cos((90 - threshold) * Math.PI / 180); // near right + + var upperBound = Math.cos(threshold * Math.PI / 180); // near straight + + for (var i = 0; i < points.length; i++) { + var a = points[(i - 1 + points.length) % points.length]; + var origin = points[i]; + var b = points[(i + 1) % points.length]; + var dotp = geoVecNormalizedDot(a, b, origin); + var mag = Math.abs(dotp); + + if (mag > lowerBound && mag < upperBound) { + return false; + } + } + + return true; + } + function selectMenuItem(context, operation) { + return context.container().select('.edit-menu .edit-menu-item-' + operation); + } + function transitionTime(point1, point2) { + var distance = geoSphericalDistance(point1, point2); + + if (distance === 0) { + return 0; + } else if (distance < 80) { + return 500; + } else { + return 1000; + } + } + + // hide class, which sets display=none, and a d3 transition for opacity. + // this will cause blinking when called repeatedly, so check that the + // value actually changes between calls. + + function uiToggle(show, callback) { + return function (selection) { + selection.style('opacity', show ? 0 : 1).classed('hide', false).transition().style('opacity', show ? 1 : 0).on('end', function () { + select(this).classed('hide', !show).style('opacity', null); + if (callback) callback.apply(this); + }); + }; + } + + function uiCurtain(containerNode) { + var surface = select(null), + tooltip = select(null), + darkness = select(null); + + function curtain(selection) { + surface = selection.append('svg').attr('class', 'curtain').style('top', 0).style('left', 0); + darkness = surface.append('path').attr('x', 0).attr('y', 0).attr('class', 'curtain-darkness'); + select(window).on('resize.curtain', resize); + tooltip = selection.append('div').attr('class', 'tooltip'); + tooltip.append('div').attr('class', 'popover-arrow'); + tooltip.append('div').attr('class', 'popover-inner'); + resize(); + + function resize() { + surface.attr('width', containerNode.clientWidth).attr('height', containerNode.clientHeight); + curtain.cut(darkness.datum()); + } + } + /** + * Reveal cuts the curtain to highlight the given box, + * and shows a tooltip with instructions next to the box. + * + * @param {String|ClientRect} [box] box used to cut the curtain + * @param {String} [text] text for a tooltip + * @param {Object} [options] + * @param {string} [options.tooltipClass] optional class to add to the tooltip + * @param {integer} [options.duration] transition time in milliseconds + * @param {string} [options.buttonText] if set, create a button with this text label + * @param {function} [options.buttonCallback] if set, the callback for the button + * @param {function} [options.padding] extra margin in px to put around bbox + * @param {String|ClientRect} [options.tooltipBox] box for tooltip position, if different from box for the curtain + */ + + + curtain.reveal = function (box, html, options) { + options = options || {}; + + if (typeof box === 'string') { + box = select(box).node(); + } + + if (box && box.getBoundingClientRect) { + box = copyBox(box.getBoundingClientRect()); + var containerRect = containerNode.getBoundingClientRect(); + box.top -= containerRect.top; + box.left -= containerRect.left; + } + + if (box && options.padding) { + box.top -= options.padding; + box.left -= options.padding; + box.bottom += options.padding; + box.right += options.padding; + box.height += options.padding * 2; + box.width += options.padding * 2; + } + + var tooltipBox; + + if (options.tooltipBox) { + tooltipBox = options.tooltipBox; + + if (typeof tooltipBox === 'string') { + tooltipBox = select(tooltipBox).node(); + } + + if (tooltipBox && tooltipBox.getBoundingClientRect) { + tooltipBox = copyBox(tooltipBox.getBoundingClientRect()); + } + } else { + tooltipBox = box; + } + + if (tooltipBox && html) { + if (html.indexOf('**') !== -1) { + if (html.indexOf(')(.+?)(\*\*)/, '$1$2$3'); + } else { + html = html.replace(/^(.+?)(\*\*)/, '$1$2'); + } // pseudo markdown bold text for the instruction section.. + + + html = html.replace(/\*\*(.*?)\*\*/g, '$1'); + } + + html = html.replace(/\*(.*?)\*/g, '$1'); // emphasis + + html = html.replace(/\{br\}/g, '

    '); // linebreak + + if (options.buttonText && options.buttonCallback) { + html += '
    ' + '
    '; + } + + var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || ''); + tooltip.classed(classes, true).selectAll('.popover-inner').html(html); + + if (options.buttonText && options.buttonCallback) { + var button = tooltip.selectAll('.button-section .button.action'); + button.on('click', function (d3_event) { + d3_event.preventDefault(); + options.buttonCallback(); + }); + } + + var tip = copyBox(tooltip.node().getBoundingClientRect()), + w = containerNode.clientWidth, + h = containerNode.clientHeight, + tooltipWidth = 200, + tooltipArrow = 5, + side, + pos; // hack: this will have bottom placement, + // so need to reserve extra space for the tooltip illustration. + + if (options.tooltipClass === 'intro-mouse') { + tip.height += 80; + } // trim box dimensions to just the portion that fits in the container.. + + + if (tooltipBox.top + tooltipBox.height > h) { + tooltipBox.height -= tooltipBox.top + tooltipBox.height - h; + } + + if (tooltipBox.left + tooltipBox.width > w) { + tooltipBox.width -= tooltipBox.left + tooltipBox.width - w; + } // determine tooltip placement.. + + + if (tooltipBox.top + tooltipBox.height < 100) { + // tooltip below box.. + side = 'bottom'; + pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top + tooltipBox.height]; + } else if (tooltipBox.top > h - 140) { + // tooltip above box.. + side = 'top'; + pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top - tip.height]; + } else { + // tooltip to the side of the tooltipBox.. + var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2; + + if (_mainLocalizer.textDirection() === 'rtl') { + if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) { + side = 'right'; + pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY]; + } else { + side = 'left'; + pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY]; + } + } else { + if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) { + side = 'left'; + pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY]; + } else { + side = 'right'; + pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY]; + } + } + } + + if (options.duration !== 0 || !tooltip.classed(side)) { + tooltip.call(uiToggle(true)); + } + + tooltip.style('top', pos[1] + 'px').style('left', pos[0] + 'px').attr('class', classes + ' ' + side); // shift popover-inner if it is very close to the top or bottom edge + // (doesn't affect the placement of the popover-arrow) + + var shiftY = 0; + + if (side === 'left' || side === 'right') { + if (pos[1] < 60) { + shiftY = 60 - pos[1]; + } else if (pos[1] + tip.height > h - 100) { + shiftY = h - pos[1] - tip.height - 100; + } + } + + tooltip.selectAll('.popover-inner').style('top', shiftY + 'px'); + } else { + tooltip.classed('in', false).call(uiToggle(false)); + } + + curtain.cut(box, options.duration); + return tooltip; + }; + + curtain.cut = function (datum, duration) { + darkness.datum(datum).interrupt(); + var selection; + + if (duration === 0) { + selection = darkness; + } else { + selection = darkness.transition().duration(duration || 600).ease(linear$1); + } + + selection.attr('d', function (d) { + var containerWidth = containerNode.clientWidth; + var containerHeight = containerNode.clientHeight; + var string = 'M 0,0 L 0,' + containerHeight + ' L ' + containerWidth + ',' + containerHeight + 'L' + containerWidth + ',0 Z'; + if (!d) return string; + return string + 'M' + d.left + ',' + d.top + 'L' + d.left + ',' + (d.top + d.height) + 'L' + (d.left + d.width) + ',' + (d.top + d.height) + 'L' + (d.left + d.width) + ',' + d.top + 'Z'; + }); + }; + + curtain.remove = function () { + surface.remove(); + tooltip.remove(); + select(window).on('resize.curtain', null); + }; // ClientRects are immutable, so copy them to an object, + // in case we need to trim the height/width. + + + function copyBox(src) { + return { + top: src.top, + right: src.right, + bottom: src.bottom, + left: src.left, + width: src.width, + height: src.height + }; + } + + return curtain; + } + + function uiIntroWelcome(context, reveal) { + var dispatch = dispatch$8('done'); + var chapter = { + title: 'intro.welcome.title' + }; + + function welcome() { + context.map().centerZoom([-85.63591, 41.94285], 19); + reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.welcome'), { + buttonText: _t.html('intro.ok'), + buttonCallback: practice + }); + } + + function practice() { + reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.practice'), { + buttonText: _t.html('intro.ok'), + buttonCallback: words + }); + } + + function words() { + reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.words'), { + buttonText: _t.html('intro.ok'), + buttonCallback: chapters + }); + } + + function chapters() { + dispatch.call('done'); + reveal('.intro-nav-wrap .chapter-navigation', helpHtml('intro.welcome.chapters', { + next: _t('intro.navigation.title') + })); + } + + chapter.enter = function () { + welcome(); + }; + + chapter.exit = function () { + context.container().select('.curtain-tooltip.intro-mouse').selectAll('.counter').remove(); + }; + + chapter.restart = function () { + chapter.exit(); + chapter.enter(); + }; + + return utilRebind(chapter, dispatch, 'on'); + } + + function uiIntroNavigation(context, reveal) { + var dispatch = dispatch$8('done'); + var timeouts = []; + var hallId = 'n2061'; + var townHall = [-85.63591, 41.94285]; + var springStreetId = 'w397'; + var springStreetEndId = 'n1834'; + var springStreet = [-85.63582, 41.94255]; + var onewayField = _mainPresetIndex.field('oneway'); + var maxspeedField = _mainPresetIndex.field('maxspeed'); + var chapter = { + title: 'intro.navigation.title' + }; + + function timeout(f, t) { + timeouts.push(window.setTimeout(f, t)); + } + + function eventCancel(d3_event) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + } + + function isTownHallSelected() { + var ids = context.selectedIDs(); + return ids.length === 1 && ids[0] === hallId; + } + + function dragMap() { + context.enter(modeBrowse(context)); + context.history().reset('initial'); + var msec = transitionTime(townHall, context.map().center()); + + if (msec) { + reveal(null, null, { + duration: 0 + }); + } + + context.map().centerZoomEase(townHall, 19, msec); + timeout(function () { + var centerStart = context.map().center(); + var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch'; + var dragString = helpHtml('intro.navigation.map_info') + '{br}' + helpHtml('intro.navigation.' + textId); + reveal('.surface', dragString); + context.map().on('drawn.intro', function () { + reveal('.surface', dragString, { + duration: 0 + }); + }); + context.map().on('move.intro', function () { + var centerNow = context.map().center(); + + if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) { + context.map().on('move.intro', null); + timeout(function () { + continueTo(zoomMap); + }, 3000); + } + }); + }, msec + 100); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + nextStep(); + } + } + + function zoomMap() { + var zoomStart = context.map().zoom(); + var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch'; + var zoomString = helpHtml('intro.navigation.' + textId); + reveal('.surface', zoomString); + context.map().on('drawn.intro', function () { + reveal('.surface', zoomString, { + duration: 0 + }); + }); + context.map().on('move.intro', function () { + if (context.map().zoom() !== zoomStart) { + context.map().on('move.intro', null); + timeout(function () { + continueTo(features); + }, 3000); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + nextStep(); + } + } + + function features() { + var onClick = function onClick() { + continueTo(pointsLinesAreas); + }; + + reveal('.surface', helpHtml('intro.navigation.features'), { + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + context.map().on('drawn.intro', function () { + reveal('.surface', helpHtml('intro.navigation.features'), { + duration: 0, + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + }); + + function continueTo(nextStep) { + context.map().on('drawn.intro', null); + nextStep(); + } + } + + function pointsLinesAreas() { + var onClick = function onClick() { + continueTo(nodesWays); + }; + + reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), { + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + context.map().on('drawn.intro', function () { + reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), { + duration: 0, + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + }); + + function continueTo(nextStep) { + context.map().on('drawn.intro', null); + nextStep(); + } + } + + function nodesWays() { + var onClick = function onClick() { + continueTo(clickTownHall); + }; + + reveal('.surface', helpHtml('intro.navigation.nodes_ways'), { + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + context.map().on('drawn.intro', function () { + reveal('.surface', helpHtml('intro.navigation.nodes_ways'), { + duration: 0, + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + }); + + function continueTo(nextStep) { + context.map().on('drawn.intro', null); + nextStep(); + } + } + + function clickTownHall() { + context.enter(modeBrowse(context)); + context.history().reset('initial'); + var entity = context.hasEntity(hallId); + if (!entity) return; + reveal(null, null, { + duration: 0 + }); + context.map().centerZoomEase(entity.loc, 19, 500); + timeout(function () { + var entity = context.hasEntity(hallId); + if (!entity) return; + var box = pointBox(entity.loc, context); + var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall'; + reveal(box, helpHtml('intro.navigation.' + textId)); + context.map().on('move.intro drawn.intro', function () { + var entity = context.hasEntity(hallId); + if (!entity) return; + var box = pointBox(entity.loc, context); + reveal(box, helpHtml('intro.navigation.' + textId), { + duration: 0 + }); + }); + context.on('enter.intro', function () { + if (isTownHallSelected()) continueTo(selectedTownHall); + }); + }, 550); // after centerZoomEase + + context.history().on('change.intro', function () { + if (!context.hasEntity(hallId)) { + continueTo(clickTownHall); + } + }); + + function continueTo(nextStep) { + context.on('enter.intro', null); + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function selectedTownHall() { + if (!isTownHallSelected()) return clickTownHall(); + var entity = context.hasEntity(hallId); + if (!entity) return clickTownHall(); + var box = pointBox(entity.loc, context); + + var onClick = function onClick() { + continueTo(editorTownHall); + }; + + reveal(box, helpHtml('intro.navigation.selected_townhall'), { + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + context.map().on('move.intro drawn.intro', function () { + var entity = context.hasEntity(hallId); + if (!entity) return; + var box = pointBox(entity.loc, context); + reveal(box, helpHtml('intro.navigation.selected_townhall'), { + duration: 0, + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + }); + context.history().on('change.intro', function () { + if (!context.hasEntity(hallId)) { + continueTo(clickTownHall); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function editorTownHall() { + if (!isTownHallSelected()) return clickTownHall(); // disallow scrolling + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + + var onClick = function onClick() { + continueTo(presetTownHall); + }; + + reveal('.entity-editor-pane', helpHtml('intro.navigation.editor_townhall'), { + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + context.on('exit.intro', function () { + continueTo(clickTownHall); + }); + context.history().on('change.intro', function () { + if (!context.hasEntity(hallId)) { + continueTo(clickTownHall); + } + }); + + function continueTo(nextStep) { + context.on('exit.intro', null); + context.history().on('change.intro', null); + context.container().select('.inspector-wrap').on('wheel.intro', null); + nextStep(); + } + } + + function presetTownHall() { + if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it.. + + context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); // preset match, in case the user happened to change it. + + var entity = context.entity(context.selectedIDs()[0]); + var preset = _mainPresetIndex.match(entity, context.graph()); + + var onClick = function onClick() { + continueTo(fieldsTownHall); + }; + + reveal('.entity-editor-pane .section-feature-type', helpHtml('intro.navigation.preset_townhall', { + preset: preset.name() + }), { + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + context.on('exit.intro', function () { + continueTo(clickTownHall); + }); + context.history().on('change.intro', function () { + if (!context.hasEntity(hallId)) { + continueTo(clickTownHall); + } + }); + + function continueTo(nextStep) { + context.on('exit.intro', null); + context.history().on('change.intro', null); + context.container().select('.inspector-wrap').on('wheel.intro', null); + nextStep(); + } + } + + function fieldsTownHall() { + if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it.. + + context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + + var onClick = function onClick() { + continueTo(closeTownHall); + }; + + reveal('.entity-editor-pane .section-preset-fields', helpHtml('intro.navigation.fields_townhall'), { + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + context.on('exit.intro', function () { + continueTo(clickTownHall); + }); + context.history().on('change.intro', function () { + if (!context.hasEntity(hallId)) { + continueTo(clickTownHall); + } + }); + + function continueTo(nextStep) { + context.on('exit.intro', null); + context.history().on('change.intro', null); + context.container().select('.inspector-wrap').on('wheel.intro', null); + nextStep(); + } + } + + function closeTownHall() { + if (!isTownHallSelected()) return clickTownHall(); + var selector = '.entity-editor-pane button.close svg use'; + var href = select(selector).attr('href') || '#iD-icon-close'; + reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', { + button: icon(href, 'inline') + })); + context.on('exit.intro', function () { + continueTo(searchStreet); + }); + context.history().on('change.intro', function () { + // update the close icon in the tooltip if the user edits something. + var selector = '.entity-editor-pane button.close svg use'; + var href = select(selector).attr('href') || '#iD-icon-close'; + reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', { + button: icon(href, 'inline') + }), { + duration: 0 + }); + }); + + function continueTo(nextStep) { + context.on('exit.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function searchStreet() { + context.enter(modeBrowse(context)); + context.history().reset('initial'); // ensure spring street exists + + var msec = transitionTime(springStreet, context.map().center()); + + if (msec) { + reveal(null, null, { + duration: 0 + }); + } + + context.map().centerZoomEase(springStreet, 19, msec); // ..and user can see it + + timeout(function () { + reveal('.search-header input', helpHtml('intro.navigation.search_street', { + name: _t('intro.graph.name.spring-street') + })); + context.container().select('.search-header input').on('keyup.intro', checkSearchResult); + }, msec + 100); + } + + function checkSearchResult() { + var first = context.container().select('.feature-list-item:nth-child(0n+2)'); // skip "No Results" item + + var firstName = first.select('.entity-name'); + var name = _t('intro.graph.name.spring-street'); + + if (!firstName.empty() && firstName.html() === name) { + reveal(first.node(), helpHtml('intro.navigation.choose_street', { + name: name + }), { + duration: 300 + }); + context.on('exit.intro', function () { + continueTo(selectedStreet); + }); + context.container().select('.search-header input').on('keydown.intro', eventCancel, true).on('keyup.intro', null); + } + + function continueTo(nextStep) { + context.on('exit.intro', null); + context.container().select('.search-header input').on('keydown.intro', null).on('keyup.intro', null); + nextStep(); + } + } + + function selectedStreet() { + if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) { + return searchStreet(); + } + + var onClick = function onClick() { + continueTo(editorStreet); + }; + + var entity = context.entity(springStreetEndId); + var box = pointBox(entity.loc, context); + box.height = 500; + reveal(box, helpHtml('intro.navigation.selected_street', { + name: _t('intro.graph.name.spring-street') + }), { + duration: 600, + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + timeout(function () { + context.map().on('move.intro drawn.intro', function () { + var entity = context.hasEntity(springStreetEndId); + if (!entity) return; + var box = pointBox(entity.loc, context); + box.height = 500; + reveal(box, helpHtml('intro.navigation.selected_street', { + name: _t('intro.graph.name.spring-street') + }), { + duration: 0, + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + }); + }, 600); // after reveal. + + context.on('enter.intro', function (mode) { + if (!context.hasEntity(springStreetId)) { + return continueTo(searchStreet); + } + + var ids = context.selectedIDs(); + + if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) { + // keep Spring Street selected.. + context.enter(modeSelect(context, [springStreetId])); + } + }); + context.history().on('change.intro', function () { + if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) { + timeout(function () { + continueTo(searchStreet); + }, 300); // after any transition (e.g. if user deleted intersection) + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function editorStreet() { + var selector = '.entity-editor-pane button.close svg use'; + var href = select(selector).attr('href') || '#iD-icon-close'; + reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', { + button: icon(href, 'inline'), + field1: onewayField.label(), + field2: maxspeedField.label() + })); + context.on('exit.intro', function () { + continueTo(play); + }); + context.history().on('change.intro', function () { + // update the close icon in the tooltip if the user edits something. + var selector = '.entity-editor-pane button.close svg use'; + var href = select(selector).attr('href') || '#iD-icon-close'; + reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', { + button: icon(href, 'inline'), + field1: onewayField.label(), + field2: maxspeedField.label() + }), { + duration: 0 + }); + }); + + function continueTo(nextStep) { + context.on('exit.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function play() { + dispatch.call('done'); + reveal('.ideditor', helpHtml('intro.navigation.play', { + next: _t('intro.points.title') + }), { + tooltipBox: '.intro-nav-wrap .chapter-point', + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + reveal('.ideditor'); + } + }); + } + + chapter.enter = function () { + dragMap(); + }; + + chapter.exit = function () { + timeouts.forEach(window.clearTimeout); + context.on('enter.intro exit.intro', null); + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + context.container().select('.inspector-wrap').on('wheel.intro', null); + context.container().select('.search-header input').on('keydown.intro keyup.intro', null); + }; + + chapter.restart = function () { + chapter.exit(); + chapter.enter(); + }; + + return utilRebind(chapter, dispatch, 'on'); + } + + function uiIntroPoint(context, reveal) { + var dispatch = dispatch$8('done'); + var timeouts = []; + var intersection = [-85.63279, 41.94394]; + var building = [-85.632422, 41.944045]; + var cafePreset = _mainPresetIndex.item('amenity/cafe'); + var _pointID = null; + var chapter = { + title: 'intro.points.title' + }; + + function timeout(f, t) { + timeouts.push(window.setTimeout(f, t)); + } + + function eventCancel(d3_event) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + } + + function addPoint() { + context.enter(modeBrowse(context)); + context.history().reset('initial'); + var msec = transitionTime(intersection, context.map().center()); + + if (msec) { + reveal(null, null, { + duration: 0 + }); + } + + context.map().centerZoomEase(intersection, 19, msec); + timeout(function () { + var tooltip = reveal('button.add-point', helpHtml('intro.points.points_info') + '{br}' + helpHtml('intro.points.add_point')); + _pointID = null; + tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-points'); + context.on('enter.intro', function (mode) { + if (mode.id !== 'add-point') return; + continueTo(placePoint); + }); + }, msec + 100); + + function continueTo(nextStep) { + context.on('enter.intro', null); + nextStep(); + } + } + + function placePoint() { + if (context.mode().id !== 'add-point') { + return chapter.restart(); + } + + var pointBox = pad(building, 150, context); + var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch'; + reveal(pointBox, helpHtml('intro.points.' + textId)); + context.map().on('move.intro drawn.intro', function () { + pointBox = pad(building, 150, context); + reveal(pointBox, helpHtml('intro.points.' + textId), { + duration: 0 + }); + }); + context.on('enter.intro', function (mode) { + if (mode.id !== 'select') return chapter.restart(); + _pointID = context.mode().selectedIDs()[0]; + continueTo(searchPreset); + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function searchPreset() { + if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) { + return addPoint(); + } // disallow scrolling + + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch); + reveal('.preset-search-input', helpHtml('intro.points.search_cafe', { + preset: cafePreset.name() + })); + context.on('enter.intro', function (mode) { + if (!_pointID || !context.hasEntity(_pointID)) { + return continueTo(addPoint); + } + + var ids = context.selectedIDs(); + + if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) { + // keep the user's point selected.. + context.enter(modeSelect(context, [_pointID])); // disallow scrolling + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch); + reveal('.preset-search-input', helpHtml('intro.points.search_cafe', { + preset: cafePreset.name() + })); + context.history().on('change.intro', null); + } + }); + + function checkPresetSearch() { + var first = context.container().select('.preset-list-item:first-child'); + + if (first.classed('preset-amenity-cafe')) { + context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null); + reveal(first.select('.preset-list-button').node(), helpHtml('intro.points.choose_cafe', { + preset: cafePreset.name() + }), { + duration: 300 + }); + context.history().on('change.intro', function () { + continueTo(aboutFeatureEditor); + }); + } + } + + function continueTo(nextStep) { + context.on('enter.intro', null); + context.history().on('change.intro', null); + context.container().select('.inspector-wrap').on('wheel.intro', null); + context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null); + nextStep(); + } + } + + function aboutFeatureEditor() { + if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) { + return addPoint(); + } + + timeout(function () { + reveal('.entity-editor-pane', helpHtml('intro.points.feature_editor'), { + tooltipClass: 'intro-points-describe', + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + continueTo(addName); + } + }); + }, 400); + context.on('exit.intro', function () { + // if user leaves select mode here, just continue with the tutorial. + continueTo(reselectPoint); + }); + + function continueTo(nextStep) { + context.on('exit.intro', null); + nextStep(); + } + } + + function addName() { + if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) { + return addPoint(); + } // reset pane, in case user happened to change it.. + + + context.container().select('.inspector-wrap .panewrap').style('right', '0%'); + var addNameString = helpHtml('intro.points.fields_info') + '{br}' + helpHtml('intro.points.add_name'); + timeout(function () { + // It's possible for the user to add a name in a previous step.. + // If so, don't tell them to add the name in this step. + // Give them an OK button instead. + var entity = context.entity(_pointID); + + if (entity.tags.name) { + var tooltip = reveal('.entity-editor-pane', addNameString, { + tooltipClass: 'intro-points-describe', + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + continueTo(addCloseEditor); + } + }); + tooltip.select('.instruction').style('display', 'none'); + } else { + reveal('.entity-editor-pane', addNameString, { + tooltipClass: 'intro-points-describe' + }); + } + }, 400); + context.history().on('change.intro', function () { + continueTo(addCloseEditor); + }); + context.on('exit.intro', function () { + // if user leaves select mode here, just continue with the tutorial. + continueTo(reselectPoint); + }); + + function continueTo(nextStep) { + context.on('exit.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function addCloseEditor() { + // reset pane, in case user happened to change it.. + context.container().select('.inspector-wrap .panewrap').style('right', '0%'); + var selector = '.entity-editor-pane button.close svg use'; + var href = select(selector).attr('href') || '#iD-icon-close'; + context.on('exit.intro', function () { + continueTo(reselectPoint); + }); + reveal('.entity-editor-pane', helpHtml('intro.points.add_close', { + button: icon(href, 'inline') + })); + + function continueTo(nextStep) { + context.on('exit.intro', null); + nextStep(); + } + } + + function reselectPoint() { + if (!_pointID) return chapter.restart(); + var entity = context.hasEntity(_pointID); + if (!entity) return chapter.restart(); // make sure it's still a cafe, in case user somehow changed it.. + + var oldPreset = _mainPresetIndex.match(entity, context.graph()); + context.replace(actionChangePreset(_pointID, oldPreset, cafePreset)); + context.enter(modeBrowse(context)); + var msec = transitionTime(entity.loc, context.map().center()); + + if (msec) { + reveal(null, null, { + duration: 0 + }); + } + + context.map().centerEase(entity.loc, msec); + timeout(function () { + var box = pointBox(entity.loc, context); + reveal(box, helpHtml('intro.points.reselect'), { + duration: 600 + }); + timeout(function () { + context.map().on('move.intro drawn.intro', function () { + var entity = context.hasEntity(_pointID); + if (!entity) return chapter.restart(); + var box = pointBox(entity.loc, context); + reveal(box, helpHtml('intro.points.reselect'), { + duration: 0 + }); + }); + }, 600); // after reveal.. + + context.on('enter.intro', function (mode) { + if (mode.id !== 'select') return; + continueTo(updatePoint); + }); + }, msec + 100); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function updatePoint() { + if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) { + return continueTo(reselectPoint); + } // reset pane, in case user happened to untag the point.. + + + context.container().select('.inspector-wrap .panewrap').style('right', '0%'); + context.on('exit.intro', function () { + continueTo(reselectPoint); + }); + context.history().on('change.intro', function () { + continueTo(updateCloseEditor); + }); + timeout(function () { + reveal('.entity-editor-pane', helpHtml('intro.points.update'), { + tooltipClass: 'intro-points-describe' + }); + }, 400); + + function continueTo(nextStep) { + context.on('exit.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function updateCloseEditor() { + if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) { + return continueTo(reselectPoint); + } // reset pane, in case user happened to change it.. + + + context.container().select('.inspector-wrap .panewrap').style('right', '0%'); + context.on('exit.intro', function () { + continueTo(rightClickPoint); + }); + timeout(function () { + reveal('.entity-editor-pane', helpHtml('intro.points.update_close', { + button: icon('#iD-icon-close', 'inline') + })); + }, 500); + + function continueTo(nextStep) { + context.on('exit.intro', null); + nextStep(); + } + } + + function rightClickPoint() { + if (!_pointID) return chapter.restart(); + var entity = context.hasEntity(_pointID); + if (!entity) return chapter.restart(); + context.enter(modeBrowse(context)); + var box = pointBox(entity.loc, context); + var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'; + reveal(box, helpHtml('intro.points.' + textId), { + duration: 600 + }); + timeout(function () { + context.map().on('move.intro', function () { + var entity = context.hasEntity(_pointID); + if (!entity) return chapter.restart(); + var box = pointBox(entity.loc, context); + reveal(box, helpHtml('intro.points.' + textId), { + duration: 0 + }); + }); + }, 600); // after reveal + + context.on('enter.intro', function (mode) { + if (mode.id !== 'select') return; + var ids = context.selectedIDs(); + if (ids.length !== 1 || ids[0] !== _pointID) return; + timeout(function () { + var node = selectMenuItem(context, 'delete').node(); + if (!node) return; + continueTo(enterDelete); + }, 50); // after menu visible + }); + + function continueTo(nextStep) { + context.on('enter.intro', null); + context.map().on('move.intro', null); + nextStep(); + } + } + + function enterDelete() { + if (!_pointID) return chapter.restart(); + var entity = context.hasEntity(_pointID); + if (!entity) return chapter.restart(); + var node = selectMenuItem(context, 'delete').node(); + + if (!node) { + return continueTo(rightClickPoint); + } + + reveal('.edit-menu', helpHtml('intro.points.delete'), { + padding: 50 + }); + timeout(function () { + context.map().on('move.intro', function () { + reveal('.edit-menu', helpHtml('intro.points.delete'), { + duration: 0, + padding: 50 + }); + }); + }, 300); // after menu visible + + context.on('exit.intro', function () { + if (!_pointID) return chapter.restart(); + var entity = context.hasEntity(_pointID); + if (entity) return continueTo(rightClickPoint); // point still exists + }); + context.history().on('change.intro', function (changed) { + if (changed.deleted().length) { + continueTo(undo); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro', null); + context.history().on('change.intro', null); + context.on('exit.intro', null); + nextStep(); + } + } + + function undo() { + context.history().on('change.intro', function () { + continueTo(play); + }); + reveal('.top-toolbar button.undo-button', helpHtml('intro.points.undo')); + + function continueTo(nextStep) { + context.history().on('change.intro', null); + nextStep(); + } + } + + function play() { + dispatch.call('done'); + reveal('.ideditor', helpHtml('intro.points.play', { + next: _t('intro.areas.title') + }), { + tooltipBox: '.intro-nav-wrap .chapter-area', + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + reveal('.ideditor'); + } + }); + } + + chapter.enter = function () { + addPoint(); + }; + + chapter.exit = function () { + timeouts.forEach(window.clearTimeout); + context.on('enter.intro exit.intro', null); + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null); + }; + + chapter.restart = function () { + chapter.exit(); + chapter.enter(); + }; + + return utilRebind(chapter, dispatch, 'on'); + } + + function uiIntroArea(context, reveal) { + var dispatch = dispatch$8('done'); + var playground = [-85.63552, 41.94159]; + var playgroundPreset = _mainPresetIndex.item('leisure/playground'); + var nameField = _mainPresetIndex.field('name'); + var descriptionField = _mainPresetIndex.field('description'); + var timeouts = []; + + var _areaID; + + var chapter = { + title: 'intro.areas.title' + }; + + function timeout(f, t) { + timeouts.push(window.setTimeout(f, t)); + } + + function eventCancel(d3_event) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + } + + function revealPlayground(center, text, options) { + var padding = 180 * Math.pow(2, context.map().zoom() - 19.5); + var box = pad(center, padding, context); + reveal(box, text, options); + } + + function addArea() { + context.enter(modeBrowse(context)); + context.history().reset('initial'); + _areaID = null; + var msec = transitionTime(playground, context.map().center()); + + if (msec) { + reveal(null, null, { + duration: 0 + }); + } + + context.map().centerZoomEase(playground, 19, msec); + timeout(function () { + var tooltip = reveal('button.add-area', helpHtml('intro.areas.add_playground')); + tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-areas'); + context.on('enter.intro', function (mode) { + if (mode.id !== 'add-area') return; + continueTo(startPlayground); + }); + }, msec + 100); + + function continueTo(nextStep) { + context.on('enter.intro', null); + nextStep(); + } + } + + function startPlayground() { + if (context.mode().id !== 'add-area') { + return chapter.restart(); + } + + _areaID = null; + context.map().zoomEase(19.5, 500); + timeout(function () { + var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap'; + var startDrawString = helpHtml('intro.areas.start_playground') + helpHtml('intro.areas.' + textId); + revealPlayground(playground, startDrawString, { + duration: 250 + }); + timeout(function () { + context.map().on('move.intro drawn.intro', function () { + revealPlayground(playground, startDrawString, { + duration: 0 + }); + }); + context.on('enter.intro', function (mode) { + if (mode.id !== 'draw-area') return chapter.restart(); + continueTo(continuePlayground); + }); + }, 250); // after reveal + }, 550); // after easing + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function continuePlayground() { + if (context.mode().id !== 'draw-area') { + return chapter.restart(); + } + + _areaID = null; + revealPlayground(playground, helpHtml('intro.areas.continue_playground'), { + duration: 250 + }); + timeout(function () { + context.map().on('move.intro drawn.intro', function () { + revealPlayground(playground, helpHtml('intro.areas.continue_playground'), { + duration: 0 + }); + }); + }, 250); // after reveal + + context.on('enter.intro', function (mode) { + if (mode.id === 'draw-area') { + var entity = context.hasEntity(context.selectedIDs()[0]); + + if (entity && entity.nodes.length >= 6) { + return continueTo(finishPlayground); + } else { + return; + } + } else if (mode.id === 'select') { + _areaID = context.selectedIDs()[0]; + return continueTo(searchPresets); + } else { + return chapter.restart(); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function finishPlayground() { + if (context.mode().id !== 'draw-area') { + return chapter.restart(); + } + + _areaID = null; + var finishString = helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.areas.finish_playground'); + revealPlayground(playground, finishString, { + duration: 250 + }); + timeout(function () { + context.map().on('move.intro drawn.intro', function () { + revealPlayground(playground, finishString, { + duration: 0 + }); + }); + }, 250); // after reveal + + context.on('enter.intro', function (mode) { + if (mode.id === 'draw-area') { + return; + } else if (mode.id === 'select') { + _areaID = context.selectedIDs()[0]; + return continueTo(searchPresets); + } else { + return chapter.restart(); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function searchPresets() { + if (!_areaID || !context.hasEntity(_areaID)) { + return addArea(); + } + + var ids = context.selectedIDs(); + + if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) { + context.enter(modeSelect(context, [_areaID])); + } // disallow scrolling + + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + timeout(function () { + // reset pane, in case user somehow happened to change it.. + context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); + context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch); + reveal('.preset-search-input', helpHtml('intro.areas.search_playground', { + preset: playgroundPreset.name() + })); + }, 400); // after preset list pane visible.. + + context.on('enter.intro', function (mode) { + if (!_areaID || !context.hasEntity(_areaID)) { + return continueTo(addArea); + } + + var ids = context.selectedIDs(); + + if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) { + // keep the user's area selected.. + context.enter(modeSelect(context, [_areaID])); // reset pane, in case user somehow happened to change it.. + + context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch); + reveal('.preset-search-input', helpHtml('intro.areas.search_playground', { + preset: playgroundPreset.name() + })); + context.history().on('change.intro', null); + } + }); + + function checkPresetSearch() { + var first = context.container().select('.preset-list-item:first-child'); + + if (first.classed('preset-leisure-playground')) { + reveal(first.select('.preset-list-button').node(), helpHtml('intro.areas.choose_playground', { + preset: playgroundPreset.name() + }), { + duration: 300 + }); + context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null); + context.history().on('change.intro', function () { + continueTo(clickAddField); + }); + } + } + + function continueTo(nextStep) { + context.container().select('.inspector-wrap').on('wheel.intro', null); + context.on('enter.intro', null); + context.history().on('change.intro', null); + context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null); + nextStep(); + } + } + + function clickAddField() { + if (!_areaID || !context.hasEntity(_areaID)) { + return addArea(); + } + + var ids = context.selectedIDs(); + + if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) { + return searchPresets(); + } + + if (!context.container().select('.form-field-description').empty()) { + return continueTo(describePlayground); + } // disallow scrolling + + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + timeout(function () { + // reset pane, in case user somehow happened to change it.. + context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // It's possible for the user to add a description in a previous step.. + // If they did this already, just continue to next step. + + var entity = context.entity(_areaID); + + if (entity.tags.description) { + return continueTo(play); + } // scroll "Add field" into view + + + var box = context.container().select('.more-fields').node().getBoundingClientRect(); + + if (box.top > 300) { + var pane = context.container().select('.entity-editor-pane .inspector-body'); + var start = pane.node().scrollTop; + var end = start + (box.top - 300); + pane.transition().duration(250).tween('scroll.inspector', function () { + var node = this; + var i = d3_interpolateNumber(start, end); + return function (t) { + node.scrollTop = i(t); + }; + }); + } + + timeout(function () { + reveal('.more-fields .combobox-input', helpHtml('intro.areas.add_field', { + name: nameField.label(), + description: descriptionField.label() + }), { + duration: 300 + }); + context.container().select('.more-fields .combobox-input').on('click.intro', function () { + // Watch for the combobox to appear... + var watcher; + watcher = window.setInterval(function () { + if (!context.container().select('div.combobox').empty()) { + window.clearInterval(watcher); + continueTo(chooseDescriptionField); + } + }, 300); + }); + }, 300); // after "Add Field" visible + }, 400); // after editor pane visible + + context.on('exit.intro', function () { + return continueTo(searchPresets); + }); + + function continueTo(nextStep) { + context.container().select('.inspector-wrap').on('wheel.intro', null); + context.container().select('.more-fields .combobox-input').on('click.intro', null); + context.on('exit.intro', null); + nextStep(); + } + } + + function chooseDescriptionField() { + if (!_areaID || !context.hasEntity(_areaID)) { + return addArea(); + } + + var ids = context.selectedIDs(); + + if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) { + return searchPresets(); + } + + if (!context.container().select('.form-field-description').empty()) { + return continueTo(describePlayground); + } // Make sure combobox is ready.. + + + if (context.container().select('div.combobox').empty()) { + return continueTo(clickAddField); + } // Watch for the combobox to go away.. + + + var watcher; + watcher = window.setInterval(function () { + if (context.container().select('div.combobox').empty()) { + window.clearInterval(watcher); + timeout(function () { + if (context.container().select('.form-field-description').empty()) { + continueTo(retryChooseDescription); + } else { + continueTo(describePlayground); + } + }, 300); // after description field added. + } + }, 300); + reveal('div.combobox', helpHtml('intro.areas.choose_field', { + field: descriptionField.label() + }), { + duration: 300 + }); + context.on('exit.intro', function () { + return continueTo(searchPresets); + }); + + function continueTo(nextStep) { + if (watcher) window.clearInterval(watcher); + context.on('exit.intro', null); + nextStep(); + } + } + + function describePlayground() { + if (!_areaID || !context.hasEntity(_areaID)) { + return addArea(); + } + + var ids = context.selectedIDs(); + + if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) { + return searchPresets(); + } // reset pane, in case user happened to change it.. + + + context.container().select('.inspector-wrap .panewrap').style('right', '0%'); + + if (context.container().select('.form-field-description').empty()) { + return continueTo(retryChooseDescription); + } + + context.on('exit.intro', function () { + continueTo(play); + }); + reveal('.entity-editor-pane', helpHtml('intro.areas.describe_playground', { + button: icon('#iD-icon-close', 'inline') + }), { + duration: 300 + }); + + function continueTo(nextStep) { + context.on('exit.intro', null); + nextStep(); + } + } + + function retryChooseDescription() { + if (!_areaID || !context.hasEntity(_areaID)) { + return addArea(); + } + + var ids = context.selectedIDs(); + + if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) { + return searchPresets(); + } // reset pane, in case user happened to change it.. + + + context.container().select('.inspector-wrap .panewrap').style('right', '0%'); + reveal('.entity-editor-pane', helpHtml('intro.areas.retry_add_field', { + field: descriptionField.label() + }), { + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + continueTo(clickAddField); + } + }); + context.on('exit.intro', function () { + return continueTo(searchPresets); + }); + + function continueTo(nextStep) { + context.on('exit.intro', null); + nextStep(); + } + } + + function play() { + dispatch.call('done'); + reveal('.ideditor', helpHtml('intro.areas.play', { + next: _t('intro.lines.title') + }), { + tooltipBox: '.intro-nav-wrap .chapter-line', + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + reveal('.ideditor'); + } + }); + } + + chapter.enter = function () { + addArea(); + }; + + chapter.exit = function () { + timeouts.forEach(window.clearTimeout); + context.on('enter.intro exit.intro', null); + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + context.container().select('.inspector-wrap').on('wheel.intro', null); + context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null); + context.container().select('.more-fields .combobox-input').on('click.intro', null); + }; + + chapter.restart = function () { + chapter.exit(); + chapter.enter(); + }; + + return utilRebind(chapter, dispatch, 'on'); + } + + function uiIntroLine(context, reveal) { + var dispatch = dispatch$8('done'); + var timeouts = []; + var _tulipRoadID = null; + var flowerRoadID = 'w646'; + var tulipRoadStart = [-85.6297754121684, 41.95805253325314]; + var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204]; + var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585]; + var roadCategory = _mainPresetIndex.item('category-road_minor'); + var residentialPreset = _mainPresetIndex.item('highway/residential'); + var woodRoadID = 'w525'; + var woodRoadEndID = 'n2862'; + var woodRoadAddNode = [-85.62390110349587, 41.95397111462291]; + var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487]; + var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872]; + var washingtonStreetID = 'w522'; + var twelfthAvenueID = 'w1'; + var eleventhAvenueEndID = 'n3550'; + var twelfthAvenueEndID = 'n5'; + var _washingtonSegmentID = null; + var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc; + var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc; + var deleteLinesLoc = [-85.6219395542764, 41.95228033922477]; + var twelfthAvenue = [-85.62219310052491, 41.952505413152956]; + var chapter = { + title: 'intro.lines.title' + }; + + function timeout(f, t) { + timeouts.push(window.setTimeout(f, t)); + } + + function eventCancel(d3_event) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + } + + function addLine() { + context.enter(modeBrowse(context)); + context.history().reset('initial'); + var msec = transitionTime(tulipRoadStart, context.map().center()); + + if (msec) { + reveal(null, null, { + duration: 0 + }); + } + + context.map().centerZoomEase(tulipRoadStart, 18.5, msec); + timeout(function () { + var tooltip = reveal('button.add-line', helpHtml('intro.lines.add_line')); + tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-lines'); + context.on('enter.intro', function (mode) { + if (mode.id !== 'add-line') return; + continueTo(startLine); + }); + }, msec + 100); + + function continueTo(nextStep) { + context.on('enter.intro', null); + nextStep(); + } + } + + function startLine() { + if (context.mode().id !== 'add-line') return chapter.restart(); + _tulipRoadID = null; + var padding = 70 * Math.pow(2, context.map().zoom() - 18); + var box = pad(tulipRoadStart, padding, context); + box.height = box.height + 100; + var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap'; + var startLineString = helpHtml('intro.lines.missing_road') + '{br}' + helpHtml('intro.lines.line_draw_info') + helpHtml('intro.lines.' + textId); + reveal(box, startLineString); + context.map().on('move.intro drawn.intro', function () { + padding = 70 * Math.pow(2, context.map().zoom() - 18); + box = pad(tulipRoadStart, padding, context); + box.height = box.height + 100; + reveal(box, startLineString, { + duration: 0 + }); + }); + context.on('enter.intro', function (mode) { + if (mode.id !== 'draw-line') return chapter.restart(); + continueTo(drawLine); + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function drawLine() { + if (context.mode().id !== 'draw-line') return chapter.restart(); + _tulipRoadID = context.mode().selectedIDs()[0]; + context.map().centerEase(tulipRoadMidpoint, 500); + timeout(function () { + var padding = 200 * Math.pow(2, context.map().zoom() - 18.5); + var box = pad(tulipRoadMidpoint, padding, context); + box.height = box.height * 2; + reveal(box, helpHtml('intro.lines.intersect', { + name: _t('intro.graph.name.flower-street') + })); + context.map().on('move.intro drawn.intro', function () { + padding = 200 * Math.pow(2, context.map().zoom() - 18.5); + box = pad(tulipRoadMidpoint, padding, context); + box.height = box.height * 2; + reveal(box, helpHtml('intro.lines.intersect', { + name: _t('intro.graph.name.flower-street') + }), { + duration: 0 + }); + }); + }, 550); // after easing.. + + context.history().on('change.intro', function () { + if (isLineConnected()) { + continueTo(continueLine); + } + }); + context.on('enter.intro', function (mode) { + if (mode.id === 'draw-line') { + return; + } else if (mode.id === 'select') { + continueTo(retryIntersect); + return; + } else { + return chapter.restart(); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function isLineConnected() { + var entity = _tulipRoadID && context.hasEntity(_tulipRoadID); + + if (!entity) return false; + var drawNodes = context.graph().childNodes(entity); + return drawNodes.some(function (node) { + return context.graph().parentWays(node).some(function (parent) { + return parent.id === flowerRoadID; + }); + }); + } + + function retryIntersect() { + select(window).on('pointerdown.intro mousedown.intro', eventCancel, true); + var box = pad(tulipRoadIntersection, 80, context); + reveal(box, helpHtml('intro.lines.retry_intersect', { + name: _t('intro.graph.name.flower-street') + })); + timeout(chapter.restart, 3000); + } + + function continueLine() { + if (context.mode().id !== 'draw-line') return chapter.restart(); + + var entity = _tulipRoadID && context.hasEntity(_tulipRoadID); + + if (!entity) return chapter.restart(); + context.map().centerEase(tulipRoadIntersection, 500); + var continueLineText = helpHtml('intro.lines.continue_line') + '{br}' + helpHtml('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.lines.finish_road'); + reveal('.surface', continueLineText); + context.on('enter.intro', function (mode) { + if (mode.id === 'draw-line') { + return; + } else if (mode.id === 'select') { + return continueTo(chooseCategoryRoad); + } else { + return chapter.restart(); + } + }); + + function continueTo(nextStep) { + context.on('enter.intro', null); + nextStep(); + } + } + + function chooseCategoryRoad() { + if (context.mode().id !== 'select') return chapter.restart(); + context.on('exit.intro', function () { + return chapter.restart(); + }); + var button = context.container().select('.preset-category-road_minor .preset-list-button'); + if (button.empty()) return chapter.restart(); // disallow scrolling + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + timeout(function () { + // reset pane, in case user somehow happened to change it.. + context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); + reveal(button.node(), helpHtml('intro.lines.choose_category_road', { + category: roadCategory.name() + })); + button.on('click.intro', function () { + continueTo(choosePresetResidential); + }); + }, 400); // after editor pane visible + + function continueTo(nextStep) { + context.container().select('.inspector-wrap').on('wheel.intro', null); + context.container().select('.preset-list-button').on('click.intro', null); + context.on('exit.intro', null); + nextStep(); + } + } + + function choosePresetResidential() { + if (context.mode().id !== 'select') return chapter.restart(); + context.on('exit.intro', function () { + return chapter.restart(); + }); + var subgrid = context.container().select('.preset-category-road_minor .subgrid'); + if (subgrid.empty()) return chapter.restart(); + subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button').on('click.intro', function () { + continueTo(retryPresetResidential); + }); + subgrid.selectAll('.preset-highway-residential .preset-list-button').on('click.intro', function () { + continueTo(nameRoad); + }); + timeout(function () { + reveal(subgrid.node(), helpHtml('intro.lines.choose_preset_residential', { + preset: residentialPreset.name() + }), { + tooltipBox: '.preset-highway-residential .preset-list-button', + duration: 300 + }); + }, 300); + + function continueTo(nextStep) { + context.container().select('.preset-list-button').on('click.intro', null); + context.on('exit.intro', null); + nextStep(); + } + } // selected wrong road type + + + function retryPresetResidential() { + if (context.mode().id !== 'select') return chapter.restart(); + context.on('exit.intro', function () { + return chapter.restart(); + }); // disallow scrolling + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + timeout(function () { + var button = context.container().select('.entity-editor-pane .preset-list-button'); + reveal(button.node(), helpHtml('intro.lines.retry_preset_residential', { + preset: residentialPreset.name() + })); + button.on('click.intro', function () { + continueTo(chooseCategoryRoad); + }); + }, 500); + + function continueTo(nextStep) { + context.container().select('.inspector-wrap').on('wheel.intro', null); + context.container().select('.preset-list-button').on('click.intro', null); + context.on('exit.intro', null); + nextStep(); + } + } + + function nameRoad() { + context.on('exit.intro', function () { + continueTo(didNameRoad); + }); + timeout(function () { + reveal('.entity-editor-pane', helpHtml('intro.lines.name_road', { + button: icon('#iD-icon-close', 'inline') + }), { + tooltipClass: 'intro-lines-name_road' + }); + }, 500); + + function continueTo(nextStep) { + context.on('exit.intro', null); + nextStep(); + } + } + + function didNameRoad() { + context.history().checkpoint('doneAddLine'); + timeout(function () { + reveal('.surface', helpHtml('intro.lines.did_name_road'), { + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + continueTo(updateLine); + } + }); + }, 500); + + function continueTo(nextStep) { + nextStep(); + } + } + + function updateLine() { + context.history().reset('doneAddLine'); + + if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { + return chapter.restart(); + } + + var msec = transitionTime(woodRoadDragMidpoint, context.map().center()); + + if (msec) { + reveal(null, null, { + duration: 0 + }); + } + + context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec); + timeout(function () { + var padding = 250 * Math.pow(2, context.map().zoom() - 19); + var box = pad(woodRoadDragMidpoint, padding, context); + + var advance = function advance() { + continueTo(addNode); + }; + + reveal(box, helpHtml('intro.lines.update_line'), { + buttonText: _t.html('intro.ok'), + buttonCallback: advance + }); + context.map().on('move.intro drawn.intro', function () { + var padding = 250 * Math.pow(2, context.map().zoom() - 19); + var box = pad(woodRoadDragMidpoint, padding, context); + reveal(box, helpHtml('intro.lines.update_line'), { + duration: 0, + buttonText: _t.html('intro.ok'), + buttonCallback: advance + }); + }); + }, msec + 100); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + nextStep(); + } + } + + function addNode() { + context.history().reset('doneAddLine'); + + if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { + return chapter.restart(); + } + + var padding = 40 * Math.pow(2, context.map().zoom() - 19); + var box = pad(woodRoadAddNode, padding, context); + var addNodeString = helpHtml('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch')); + reveal(box, addNodeString); + context.map().on('move.intro drawn.intro', function () { + var padding = 40 * Math.pow(2, context.map().zoom() - 19); + var box = pad(woodRoadAddNode, padding, context); + reveal(box, addNodeString, { + duration: 0 + }); + }); + context.history().on('change.intro', function (changed) { + if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { + return continueTo(updateLine); + } + + if (changed.created().length === 1) { + timeout(function () { + continueTo(startDragEndpoint); + }, 500); + } + }); + context.on('enter.intro', function (mode) { + if (mode.id !== 'select') { + continueTo(updateLine); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function startDragEndpoint() { + if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { + return continueTo(updateLine); + } + + var padding = 100 * Math.pow(2, context.map().zoom() - 19); + var box = pad(woodRoadDragEndpoint, padding, context); + var startDragString = helpHtml('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) + helpHtml('intro.lines.drag_to_intersection'); + reveal(box, startDragString); + context.map().on('move.intro drawn.intro', function () { + if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { + return continueTo(updateLine); + } + + var padding = 100 * Math.pow(2, context.map().zoom() - 19); + var box = pad(woodRoadDragEndpoint, padding, context); + reveal(box, startDragString, { + duration: 0 + }); + var entity = context.entity(woodRoadEndID); + + if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) { + continueTo(finishDragEndpoint); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + nextStep(); + } + } + + function finishDragEndpoint() { + if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { + return continueTo(updateLine); + } + + var padding = 100 * Math.pow(2, context.map().zoom() - 19); + var box = pad(woodRoadDragEndpoint, padding, context); + var finishDragString = helpHtml('intro.lines.spot_looks_good') + helpHtml('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')); + reveal(box, finishDragString); + context.map().on('move.intro drawn.intro', function () { + if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { + return continueTo(updateLine); + } + + var padding = 100 * Math.pow(2, context.map().zoom() - 19); + var box = pad(woodRoadDragEndpoint, padding, context); + reveal(box, finishDragString, { + duration: 0 + }); + var entity = context.entity(woodRoadEndID); + + if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) { + continueTo(startDragEndpoint); + } + }); + context.on('enter.intro', function () { + continueTo(startDragMidpoint); + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function startDragMidpoint() { + if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { + return continueTo(updateLine); + } + + if (context.selectedIDs().indexOf(woodRoadID) === -1) { + context.enter(modeSelect(context, [woodRoadID])); + } + + var padding = 80 * Math.pow(2, context.map().zoom() - 19); + var box = pad(woodRoadDragMidpoint, padding, context); + reveal(box, helpHtml('intro.lines.start_drag_midpoint')); + context.map().on('move.intro drawn.intro', function () { + if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { + return continueTo(updateLine); + } + + var padding = 80 * Math.pow(2, context.map().zoom() - 19); + var box = pad(woodRoadDragMidpoint, padding, context); + reveal(box, helpHtml('intro.lines.start_drag_midpoint'), { + duration: 0 + }); + }); + context.history().on('change.intro', function (changed) { + if (changed.created().length === 1) { + continueTo(continueDragMidpoint); + } + }); + context.on('enter.intro', function (mode) { + if (mode.id !== 'select') { + // keep Wood Road selected so midpoint triangles are drawn.. + context.enter(modeSelect(context, [woodRoadID])); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function continueDragMidpoint() { + if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { + return continueTo(updateLine); + } + + var padding = 100 * Math.pow(2, context.map().zoom() - 19); + var box = pad(woodRoadDragEndpoint, padding, context); + box.height += 400; + + var advance = function advance() { + context.history().checkpoint('doneUpdateLine'); + continueTo(deleteLines); + }; + + reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), { + buttonText: _t.html('intro.ok'), + buttonCallback: advance + }); + context.map().on('move.intro drawn.intro', function () { + if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { + return continueTo(updateLine); + } + + var padding = 100 * Math.pow(2, context.map().zoom() - 19); + var box = pad(woodRoadDragEndpoint, padding, context); + box.height += 400; + reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), { + duration: 0, + buttonText: _t.html('intro.ok'), + buttonCallback: advance + }); + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + nextStep(); + } + } + + function deleteLines() { + context.history().reset('doneUpdateLine'); + context.enter(modeBrowse(context)); + + if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) { + return chapter.restart(); + } + + var msec = transitionTime(deleteLinesLoc, context.map().center()); + + if (msec) { + reveal(null, null, { + duration: 0 + }); + } + + context.map().centerZoomEase(deleteLinesLoc, 18, msec); + timeout(function () { + var padding = 200 * Math.pow(2, context.map().zoom() - 18); + var box = pad(deleteLinesLoc, padding, context); + box.top -= 200; + box.height += 400; + + var advance = function advance() { + continueTo(rightClickIntersection); + }; + + reveal(box, helpHtml('intro.lines.delete_lines', { + street: _t('intro.graph.name.12th-avenue') + }), { + buttonText: _t.html('intro.ok'), + buttonCallback: advance + }); + context.map().on('move.intro drawn.intro', function () { + var padding = 200 * Math.pow(2, context.map().zoom() - 18); + var box = pad(deleteLinesLoc, padding, context); + box.top -= 200; + box.height += 400; + reveal(box, helpHtml('intro.lines.delete_lines', { + street: _t('intro.graph.name.12th-avenue') + }), { + duration: 0, + buttonText: _t.html('intro.ok'), + buttonCallback: advance + }); + }); + context.history().on('change.intro', function () { + timeout(function () { + continueTo(deleteLines); + }, 500); // after any transition (e.g. if user deleted intersection) + }); + }, msec + 100); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function rightClickIntersection() { + context.history().reset('doneUpdateLine'); + context.enter(modeBrowse(context)); + context.map().centerZoomEase(eleventhAvenueEnd, 18, 500); + var rightClickString = helpHtml('intro.lines.split_street', { + street1: _t('intro.graph.name.11th-avenue'), + street2: _t('intro.graph.name.washington-street') + }) + helpHtml('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch')); + timeout(function () { + var padding = 60 * Math.pow(2, context.map().zoom() - 18); + var box = pad(eleventhAvenueEnd, padding, context); + reveal(box, rightClickString); + context.map().on('move.intro drawn.intro', function () { + var padding = 60 * Math.pow(2, context.map().zoom() - 18); + var box = pad(eleventhAvenueEnd, padding, context); + reveal(box, rightClickString, { + duration: 0 + }); + }); + context.on('enter.intro', function (mode) { + if (mode.id !== 'select') return; + var ids = context.selectedIDs(); + if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) return; + timeout(function () { + var node = selectMenuItem(context, 'split').node(); + if (!node) return; + continueTo(splitIntersection); + }, 50); // after menu visible + }); + context.history().on('change.intro', function () { + timeout(function () { + continueTo(deleteLines); + }, 300); // after any transition (e.g. if user deleted intersection) + }); + }, 600); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function splitIntersection() { + if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) { + return continueTo(deleteLines); + } + + var node = selectMenuItem(context, 'split').node(); + + if (!node) { + return continueTo(rightClickIntersection); + } + + var wasChanged = false; + _washingtonSegmentID = null; + reveal('.edit-menu', helpHtml('intro.lines.split_intersection', { + street: _t('intro.graph.name.washington-street') + }), { + padding: 50 + }); + context.map().on('move.intro drawn.intro', function () { + var node = selectMenuItem(context, 'split').node(); + + if (!wasChanged && !node) { + return continueTo(rightClickIntersection); + } + + reveal('.edit-menu', helpHtml('intro.lines.split_intersection', { + street: _t('intro.graph.name.washington-street') + }), { + duration: 0, + padding: 50 + }); + }); + context.history().on('change.intro', function (changed) { + wasChanged = true; + timeout(function () { + if (context.history().undoAnnotation() === _t('operations.split.annotation.line', { + n: 1 + })) { + _washingtonSegmentID = changed.created()[0].id; + continueTo(didSplit); + } else { + _washingtonSegmentID = null; + continueTo(retrySplit); + } + }, 300); // after any transition (e.g. if user deleted intersection) + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function retrySplit() { + context.enter(modeBrowse(context)); + context.map().centerZoomEase(eleventhAvenueEnd, 18, 500); + + var advance = function advance() { + continueTo(rightClickIntersection); + }; + + var padding = 60 * Math.pow(2, context.map().zoom() - 18); + var box = pad(eleventhAvenueEnd, padding, context); + reveal(box, helpHtml('intro.lines.retry_split'), { + buttonText: _t.html('intro.ok'), + buttonCallback: advance + }); + context.map().on('move.intro drawn.intro', function () { + var padding = 60 * Math.pow(2, context.map().zoom() - 18); + var box = pad(eleventhAvenueEnd, padding, context); + reveal(box, helpHtml('intro.lines.retry_split'), { + duration: 0, + buttonText: _t.html('intro.ok'), + buttonCallback: advance + }); + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + nextStep(); + } + } + + function didSplit() { + if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) { + return continueTo(rightClickIntersection); + } + + var ids = context.selectedIDs(); + var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single'); + var street = _t('intro.graph.name.washington-street'); + var padding = 200 * Math.pow(2, context.map().zoom() - 18); + var box = pad(twelfthAvenue, padding, context); + box.width = box.width / 2; + reveal(box, helpHtml(string, { + street1: street, + street2: street + }), { + duration: 500 + }); + timeout(function () { + context.map().centerZoomEase(twelfthAvenue, 18, 500); + context.map().on('move.intro drawn.intro', function () { + var padding = 200 * Math.pow(2, context.map().zoom() - 18); + var box = pad(twelfthAvenue, padding, context); + box.width = box.width / 2; + reveal(box, helpHtml(string, { + street1: street, + street2: street + }), { + duration: 0 + }); + }); + }, 600); // after initial reveal and curtain cut + + context.on('enter.intro', function () { + var ids = context.selectedIDs(); + + if (ids.length === 1 && ids[0] === _washingtonSegmentID) { + continueTo(multiSelect); + } + }); + context.history().on('change.intro', function () { + if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) { + return continueTo(rightClickIntersection); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function multiSelect() { + if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) { + return continueTo(rightClickIntersection); + } + + var ids = context.selectedIDs(); + var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1; + var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1; + + if (hasWashington && hasTwelfth) { + return continueTo(multiRightClick); + } else if (!hasWashington && !hasTwelfth) { + return continueTo(didSplit); + } + + context.map().centerZoomEase(twelfthAvenue, 18, 500); + timeout(function () { + var selected, other, padding, box; + + if (hasWashington) { + selected = _t('intro.graph.name.washington-street'); + other = _t('intro.graph.name.12th-avenue'); + padding = 60 * Math.pow(2, context.map().zoom() - 18); + box = pad(twelfthAvenueEnd, padding, context); + box.width *= 3; + } else { + selected = _t('intro.graph.name.12th-avenue'); + other = _t('intro.graph.name.washington-street'); + padding = 200 * Math.pow(2, context.map().zoom() - 18); + box = pad(twelfthAvenue, padding, context); + box.width /= 2; + } + + reveal(box, helpHtml('intro.lines.multi_select', { + selected: selected, + other1: other + }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), { + selected: selected, + other2: other + })); + context.map().on('move.intro drawn.intro', function () { + if (hasWashington) { + selected = _t('intro.graph.name.washington-street'); + other = _t('intro.graph.name.12th-avenue'); + padding = 60 * Math.pow(2, context.map().zoom() - 18); + box = pad(twelfthAvenueEnd, padding, context); + box.width *= 3; + } else { + selected = _t('intro.graph.name.12th-avenue'); + other = _t('intro.graph.name.washington-street'); + padding = 200 * Math.pow(2, context.map().zoom() - 18); + box = pad(twelfthAvenue, padding, context); + box.width /= 2; + } + + reveal(box, helpHtml('intro.lines.multi_select', { + selected: selected, + other1: other + }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), { + selected: selected, + other2: other + }), { + duration: 0 + }); + }); + context.on('enter.intro', function () { + continueTo(multiSelect); + }); + context.history().on('change.intro', function () { + if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) { + return continueTo(rightClickIntersection); + } + }); + }, 600); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function multiRightClick() { + if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) { + return continueTo(rightClickIntersection); + } + + var padding = 200 * Math.pow(2, context.map().zoom() - 18); + var box = pad(twelfthAvenue, padding, context); + var rightClickString = helpHtml('intro.lines.multi_select_success') + helpHtml('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch')); + reveal(box, rightClickString); + context.map().on('move.intro drawn.intro', function () { + var padding = 200 * Math.pow(2, context.map().zoom() - 18); + var box = pad(twelfthAvenue, padding, context); + reveal(box, rightClickString, { + duration: 0 + }); + }); + context.ui().editMenu().on('toggled.intro', function (open) { + if (!open) return; + timeout(function () { + var ids = context.selectedIDs(); + + if (ids.length === 2 && ids.indexOf(twelfthAvenueID) !== -1 && ids.indexOf(_washingtonSegmentID) !== -1) { + var node = selectMenuItem(context, 'delete').node(); + if (!node) return; + continueTo(multiDelete); + } else if (ids.length === 1 && ids.indexOf(_washingtonSegmentID) !== -1) { + return continueTo(multiSelect); + } else { + return continueTo(didSplit); + } + }, 300); // after edit menu visible + }); + context.history().on('change.intro', function () { + if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) { + return continueTo(rightClickIntersection); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.ui().editMenu().on('toggled.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function multiDelete() { + if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) { + return continueTo(rightClickIntersection); + } + + var node = selectMenuItem(context, 'delete').node(); + if (!node) return continueTo(multiRightClick); + reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), { + padding: 50 + }); + context.map().on('move.intro drawn.intro', function () { + reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), { + duration: 0, + padding: 50 + }); + }); + context.on('exit.intro', function () { + if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) { + return continueTo(multiSelect); // left select mode but roads still exist + } + }); + context.history().on('change.intro', function () { + if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) { + continueTo(retryDelete); // changed something but roads still exist + } else { + continueTo(play); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('exit.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function retryDelete() { + context.enter(modeBrowse(context)); + var padding = 200 * Math.pow(2, context.map().zoom() - 18); + var box = pad(twelfthAvenue, padding, context); + reveal(box, helpHtml('intro.lines.retry_delete'), { + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + continueTo(multiSelect); + } + }); + + function continueTo(nextStep) { + nextStep(); + } + } + + function play() { + dispatch.call('done'); + reveal('.ideditor', helpHtml('intro.lines.play', { + next: _t('intro.buildings.title') + }), { + tooltipBox: '.intro-nav-wrap .chapter-building', + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + reveal('.ideditor'); + } + }); + } + + chapter.enter = function () { + addLine(); + }; + + chapter.exit = function () { + timeouts.forEach(window.clearTimeout); + select(window).on('pointerdown.intro mousedown.intro', null, true); + context.on('enter.intro exit.intro', null); + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + context.container().select('.inspector-wrap').on('wheel.intro', null); + context.container().select('.preset-list-button').on('click.intro', null); + }; + + chapter.restart = function () { + chapter.exit(); + chapter.enter(); + }; + + return utilRebind(chapter, dispatch, 'on'); + } + + function uiIntroBuilding(context, reveal) { + var dispatch = dispatch$8('done'); + var house = [-85.62815, 41.95638]; + var tank = [-85.62732, 41.95347]; + var buildingCatetory = _mainPresetIndex.item('category-building'); + var housePreset = _mainPresetIndex.item('building/house'); + var tankPreset = _mainPresetIndex.item('man_made/storage_tank'); + var timeouts = []; + var _houseID = null; + var _tankID = null; + var chapter = { + title: 'intro.buildings.title' + }; + + function timeout(f, t) { + timeouts.push(window.setTimeout(f, t)); + } + + function eventCancel(d3_event) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + } + + function revealHouse(center, text, options) { + var padding = 160 * Math.pow(2, context.map().zoom() - 20); + var box = pad(center, padding, context); + reveal(box, text, options); + } + + function revealTank(center, text, options) { + var padding = 190 * Math.pow(2, context.map().zoom() - 19.5); + var box = pad(center, padding, context); + reveal(box, text, options); + } + + function addHouse() { + context.enter(modeBrowse(context)); + context.history().reset('initial'); + _houseID = null; + var msec = transitionTime(house, context.map().center()); + + if (msec) { + reveal(null, null, { + duration: 0 + }); + } + + context.map().centerZoomEase(house, 19, msec); + timeout(function () { + var tooltip = reveal('button.add-area', helpHtml('intro.buildings.add_building')); + tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-buildings'); + context.on('enter.intro', function (mode) { + if (mode.id !== 'add-area') return; + continueTo(startHouse); + }); + }, msec + 100); + + function continueTo(nextStep) { + context.on('enter.intro', null); + nextStep(); + } + } + + function startHouse() { + if (context.mode().id !== 'add-area') { + return continueTo(addHouse); + } + + _houseID = null; + context.map().zoomEase(20, 500); + timeout(function () { + var startString = helpHtml('intro.buildings.start_building') + helpHtml('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')); + revealHouse(house, startString); + context.map().on('move.intro drawn.intro', function () { + revealHouse(house, startString, { + duration: 0 + }); + }); + context.on('enter.intro', function (mode) { + if (mode.id !== 'draw-area') return chapter.restart(); + continueTo(continueHouse); + }); + }, 550); // after easing + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function continueHouse() { + if (context.mode().id !== 'draw-area') { + return continueTo(addHouse); + } + + _houseID = null; + var continueString = helpHtml('intro.buildings.continue_building') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_building'); + revealHouse(house, continueString); + context.map().on('move.intro drawn.intro', function () { + revealHouse(house, continueString, { + duration: 0 + }); + }); + context.on('enter.intro', function (mode) { + if (mode.id === 'draw-area') { + return; + } else if (mode.id === 'select') { + var graph = context.graph(); + var way = context.entity(context.selectedIDs()[0]); + var nodes = graph.childNodes(way); + var points = utilArrayUniq(nodes).map(function (n) { + return context.projection(n.loc); + }); + + if (isMostlySquare(points)) { + _houseID = way.id; + return continueTo(chooseCategoryBuilding); + } else { + return continueTo(retryHouse); + } + } else { + return chapter.restart(); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function retryHouse() { + var onClick = function onClick() { + continueTo(addHouse); + }; + + revealHouse(house, helpHtml('intro.buildings.retry_building'), { + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + context.map().on('move.intro drawn.intro', function () { + revealHouse(house, helpHtml('intro.buildings.retry_building'), { + duration: 0, + buttonText: _t.html('intro.ok'), + buttonCallback: onClick + }); + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + nextStep(); + } + } + + function chooseCategoryBuilding() { + if (!_houseID || !context.hasEntity(_houseID)) { + return addHouse(); + } + + var ids = context.selectedIDs(); + + if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) { + context.enter(modeSelect(context, [_houseID])); + } // disallow scrolling + + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + timeout(function () { + // reset pane, in case user somehow happened to change it.. + context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); + var button = context.container().select('.preset-category-building .preset-list-button'); + reveal(button.node(), helpHtml('intro.buildings.choose_category_building', { + category: buildingCatetory.name() + })); + button.on('click.intro', function () { + button.on('click.intro', null); + continueTo(choosePresetHouse); + }); + }, 400); // after preset list pane visible.. + + context.on('enter.intro', function (mode) { + if (!_houseID || !context.hasEntity(_houseID)) { + return continueTo(addHouse); + } + + var ids = context.selectedIDs(); + + if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) { + return continueTo(chooseCategoryBuilding); + } + }); + + function continueTo(nextStep) { + context.container().select('.inspector-wrap').on('wheel.intro', null); + context.container().select('.preset-list-button').on('click.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function choosePresetHouse() { + if (!_houseID || !context.hasEntity(_houseID)) { + return addHouse(); + } + + var ids = context.selectedIDs(); + + if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) { + context.enter(modeSelect(context, [_houseID])); + } // disallow scrolling + + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + timeout(function () { + // reset pane, in case user somehow happened to change it.. + context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); + var button = context.container().select('.preset-building-house .preset-list-button'); + reveal(button.node(), helpHtml('intro.buildings.choose_preset_house', { + preset: housePreset.name() + }), { + duration: 300 + }); + button.on('click.intro', function () { + button.on('click.intro', null); + continueTo(closeEditorHouse); + }); + }, 400); // after preset list pane visible.. + + context.on('enter.intro', function (mode) { + if (!_houseID || !context.hasEntity(_houseID)) { + return continueTo(addHouse); + } + + var ids = context.selectedIDs(); + + if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) { + return continueTo(chooseCategoryBuilding); + } + }); + + function continueTo(nextStep) { + context.container().select('.inspector-wrap').on('wheel.intro', null); + context.container().select('.preset-list-button').on('click.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function closeEditorHouse() { + if (!_houseID || !context.hasEntity(_houseID)) { + return addHouse(); + } + + var ids = context.selectedIDs(); + + if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) { + context.enter(modeSelect(context, [_houseID])); + } + + context.history().checkpoint('hasHouse'); + context.on('exit.intro', function () { + continueTo(rightClickHouse); + }); + timeout(function () { + reveal('.entity-editor-pane', helpHtml('intro.buildings.close', { + button: icon('#iD-icon-close', 'inline') + })); + }, 500); + + function continueTo(nextStep) { + context.on('exit.intro', null); + nextStep(); + } + } + + function rightClickHouse() { + if (!_houseID) return chapter.restart(); + context.enter(modeBrowse(context)); + context.history().reset('hasHouse'); + var zoom = context.map().zoom(); + + if (zoom < 20) { + zoom = 20; + } + + context.map().centerZoomEase(house, zoom, 500); + context.on('enter.intro', function (mode) { + if (mode.id !== 'select') return; + var ids = context.selectedIDs(); + if (ids.length !== 1 || ids[0] !== _houseID) return; + timeout(function () { + var node = selectMenuItem(context, 'orthogonalize').node(); + if (!node) return; + continueTo(clickSquare); + }, 50); // after menu visible + }); + context.map().on('move.intro drawn.intro', function () { + var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch')); + revealHouse(house, rightclickString, { + duration: 0 + }); + }); + context.history().on('change.intro', function () { + continueTo(rightClickHouse); + }); + + function continueTo(nextStep) { + context.on('enter.intro', null); + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function clickSquare() { + if (!_houseID) return chapter.restart(); + var entity = context.hasEntity(_houseID); + if (!entity) return continueTo(rightClickHouse); + var node = selectMenuItem(context, 'orthogonalize').node(); + + if (!node) { + return continueTo(rightClickHouse); + } + + var wasChanged = false; + reveal('.edit-menu', helpHtml('intro.buildings.square_building'), { + padding: 50 + }); + context.on('enter.intro', function (mode) { + if (mode.id === 'browse') { + continueTo(rightClickHouse); + } else if (mode.id === 'move' || mode.id === 'rotate') { + continueTo(retryClickSquare); + } + }); + context.map().on('move.intro', function () { + var node = selectMenuItem(context, 'orthogonalize').node(); + + if (!wasChanged && !node) { + return continueTo(rightClickHouse); + } + + reveal('.edit-menu', helpHtml('intro.buildings.square_building'), { + duration: 0, + padding: 50 + }); + }); + context.history().on('change.intro', function () { + wasChanged = true; + context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation. + + timeout(function () { + if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature', { + n: 1 + })) { + continueTo(doneSquare); + } else { + continueTo(retryClickSquare); + } + }, 500); // after transitioned actions + }); + + function continueTo(nextStep) { + context.on('enter.intro', null); + context.map().on('move.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function retryClickSquare() { + context.enter(modeBrowse(context)); + revealHouse(house, helpHtml('intro.buildings.retry_square'), { + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + continueTo(rightClickHouse); + } + }); + + function continueTo(nextStep) { + nextStep(); + } + } + + function doneSquare() { + context.history().checkpoint('doneSquare'); + revealHouse(house, helpHtml('intro.buildings.done_square'), { + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + continueTo(addTank); + } + }); + + function continueTo(nextStep) { + nextStep(); + } + } + + function addTank() { + context.enter(modeBrowse(context)); + context.history().reset('doneSquare'); + _tankID = null; + var msec = transitionTime(tank, context.map().center()); + + if (msec) { + reveal(null, null, { + duration: 0 + }); + } + + context.map().centerZoomEase(tank, 19.5, msec); + timeout(function () { + reveal('button.add-area', helpHtml('intro.buildings.add_tank')); + context.on('enter.intro', function (mode) { + if (mode.id !== 'add-area') return; + continueTo(startTank); + }); + }, msec + 100); + + function continueTo(nextStep) { + context.on('enter.intro', null); + nextStep(); + } + } + + function startTank() { + if (context.mode().id !== 'add-area') { + return continueTo(addTank); + } + + _tankID = null; + timeout(function () { + var startString = helpHtml('intro.buildings.start_tank') + helpHtml('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')); + revealTank(tank, startString); + context.map().on('move.intro drawn.intro', function () { + revealTank(tank, startString, { + duration: 0 + }); + }); + context.on('enter.intro', function (mode) { + if (mode.id !== 'draw-area') return chapter.restart(); + continueTo(continueTank); + }); + }, 550); // after easing + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function continueTank() { + if (context.mode().id !== 'draw-area') { + return continueTo(addTank); + } + + _tankID = null; + var continueString = helpHtml('intro.buildings.continue_tank') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_tank'); + revealTank(tank, continueString); + context.map().on('move.intro drawn.intro', function () { + revealTank(tank, continueString, { + duration: 0 + }); + }); + context.on('enter.intro', function (mode) { + if (mode.id === 'draw-area') { + return; + } else if (mode.id === 'select') { + _tankID = context.selectedIDs()[0]; + return continueTo(searchPresetTank); + } else { + return continueTo(addTank); + } + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + function searchPresetTank() { + if (!_tankID || !context.hasEntity(_tankID)) { + return addTank(); + } + + var ids = context.selectedIDs(); + + if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) { + context.enter(modeSelect(context, [_tankID])); + } // disallow scrolling + + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + timeout(function () { + // reset pane, in case user somehow happened to change it.. + context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); + context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch); + reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', { + preset: tankPreset.name() + })); + }, 400); // after preset list pane visible.. + + context.on('enter.intro', function (mode) { + if (!_tankID || !context.hasEntity(_tankID)) { + return continueTo(addTank); + } + + var ids = context.selectedIDs(); + + if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) { + // keep the user's area selected.. + context.enter(modeSelect(context, [_tankID])); // reset pane, in case user somehow happened to change it.. + + context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling + + context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); + context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch); + reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', { + preset: tankPreset.name() + })); + context.history().on('change.intro', null); + } + }); + + function checkPresetSearch() { + var first = context.container().select('.preset-list-item:first-child'); + + if (first.classed('preset-man_made-storage_tank')) { + reveal(first.select('.preset-list-button').node(), helpHtml('intro.buildings.choose_tank', { + preset: tankPreset.name() + }), { + duration: 300 + }); + context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null); + context.history().on('change.intro', function () { + continueTo(closeEditorTank); + }); + } + } + + function continueTo(nextStep) { + context.container().select('.inspector-wrap').on('wheel.intro', null); + context.on('enter.intro', null); + context.history().on('change.intro', null); + context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null); + nextStep(); + } + } + + function closeEditorTank() { + if (!_tankID || !context.hasEntity(_tankID)) { + return addTank(); + } + + var ids = context.selectedIDs(); + + if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) { + context.enter(modeSelect(context, [_tankID])); + } + + context.history().checkpoint('hasTank'); + context.on('exit.intro', function () { + continueTo(rightClickTank); + }); + timeout(function () { + reveal('.entity-editor-pane', helpHtml('intro.buildings.close', { + button: icon('#iD-icon-close', 'inline') + })); + }, 500); + + function continueTo(nextStep) { + context.on('exit.intro', null); + nextStep(); + } + } + + function rightClickTank() { + if (!_tankID) return continueTo(addTank); + context.enter(modeBrowse(context)); + context.history().reset('hasTank'); + context.map().centerEase(tank, 500); + timeout(function () { + context.on('enter.intro', function (mode) { + if (mode.id !== 'select') return; + var ids = context.selectedIDs(); + if (ids.length !== 1 || ids[0] !== _tankID) return; + timeout(function () { + var node = selectMenuItem(context, 'circularize').node(); + if (!node) return; + continueTo(clickCircle); + }, 50); // after menu visible + }); + var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch')); + revealTank(tank, rightclickString); + context.map().on('move.intro drawn.intro', function () { + revealTank(tank, rightclickString, { + duration: 0 + }); + }); + context.history().on('change.intro', function () { + continueTo(rightClickTank); + }); + }, 600); + + function continueTo(nextStep) { + context.on('enter.intro', null); + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function clickCircle() { + if (!_tankID) return chapter.restart(); + var entity = context.hasEntity(_tankID); + if (!entity) return continueTo(rightClickTank); + var node = selectMenuItem(context, 'circularize').node(); + + if (!node) { + return continueTo(rightClickTank); + } + + var wasChanged = false; + reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), { + padding: 50 + }); + context.on('enter.intro', function (mode) { + if (mode.id === 'browse') { + continueTo(rightClickTank); + } else if (mode.id === 'move' || mode.id === 'rotate') { + continueTo(retryClickCircle); + } + }); + context.map().on('move.intro', function () { + var node = selectMenuItem(context, 'circularize').node(); + + if (!wasChanged && !node) { + return continueTo(rightClickTank); + } + + reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), { + duration: 0, + padding: 50 + }); + }); + context.history().on('change.intro', function () { + wasChanged = true; + context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation. + + timeout(function () { + if (context.history().undoAnnotation() === _t('operations.circularize.annotation.feature', { + n: 1 + })) { + continueTo(play); + } else { + continueTo(retryClickCircle); + } + }, 500); // after transitioned actions + }); + + function continueTo(nextStep) { + context.on('enter.intro', null); + context.map().on('move.intro', null); + context.history().on('change.intro', null); + nextStep(); + } + } + + function retryClickCircle() { + context.enter(modeBrowse(context)); + revealTank(tank, helpHtml('intro.buildings.retry_circle'), { + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + continueTo(rightClickTank); + } + }); + + function continueTo(nextStep) { + nextStep(); + } + } + + function play() { + dispatch.call('done'); + reveal('.ideditor', helpHtml('intro.buildings.play', { + next: _t('intro.startediting.title') + }), { + tooltipBox: '.intro-nav-wrap .chapter-startEditing', + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + reveal('.ideditor'); + } + }); + } + + chapter.enter = function () { + addHouse(); + }; + + chapter.exit = function () { + timeouts.forEach(window.clearTimeout); + context.on('enter.intro exit.intro', null); + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + context.container().select('.inspector-wrap').on('wheel.intro', null); + context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null); + context.container().select('.more-fields .combobox-input').on('click.intro', null); + }; + + chapter.restart = function () { + chapter.exit(); + chapter.enter(); + }; + + return utilRebind(chapter, dispatch, 'on'); + } + + function uiIntroStartEditing(context, reveal) { + var dispatch = dispatch$8('done', 'startEditing'); + var modalSelection = select(null); + var chapter = { + title: 'intro.startediting.title' + }; + + function showHelp() { + reveal('.map-control.help-control', helpHtml('intro.startediting.help'), { + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + shortcuts(); + } + }); + } + + function shortcuts() { + reveal('.map-control.help-control', helpHtml('intro.startediting.shortcuts'), { + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + showSave(); + } + }); + } + + function showSave() { + context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts + + reveal('.top-toolbar button.save', helpHtml('intro.startediting.save'), { + buttonText: _t.html('intro.ok'), + buttonCallback: function buttonCallback() { + showStart(); + } + }); + } + + function showStart() { + context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts + + modalSelection = uiModal(context.container()); + modalSelection.select('.modal').attr('class', 'modal-splash modal'); + modalSelection.selectAll('.close').remove(); + var startbutton = modalSelection.select('.content').attr('class', 'fillL').append('button').attr('class', 'modal-section huge-modal-button').on('click', function () { + modalSelection.remove(); + }); + startbutton.append('svg').attr('class', 'illustration').append('use').attr('xlink:href', '#iD-logo-walkthrough'); + startbutton.append('h2').html(_t.html('intro.startediting.start')); + dispatch.call('startEditing'); + } + + chapter.enter = function () { + showHelp(); + }; + + chapter.exit = function () { + modalSelection.remove(); + context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts + }; + + return utilRebind(chapter, dispatch, 'on'); + } + + var chapterUi = { + welcome: uiIntroWelcome, + navigation: uiIntroNavigation, + point: uiIntroPoint, + area: uiIntroArea, + line: uiIntroLine, + building: uiIntroBuilding, + startEditing: uiIntroStartEditing + }; + var chapterFlow = ['welcome', 'navigation', 'point', 'area', 'line', 'building', 'startEditing']; + function uiIntro(context) { + var INTRO_IMAGERY = 'EsriWorldImageryClarity'; + var _introGraph = {}; + + var _currChapter; + + function intro(selection) { + _mainFileFetcher.get('intro_graph').then(function (dataIntroGraph) { + // create entities for intro graph and localize names + for (var id in dataIntroGraph) { + if (!_introGraph[id]) { + _introGraph[id] = osmEntity(localize(dataIntroGraph[id])); + } + } + + selection.call(startIntro); + })["catch"](function () { + /* ignore */ + }); + } + + function startIntro(selection) { + context.enter(modeBrowse(context)); // Save current map state + + var osm = context.connection(); + var history = context.history().toJSON(); + var hash = window.location.hash; + var center = context.map().center(); + var zoom = context.map().zoom(); + var background = context.background().baseLayerSource(); + var overlays = context.background().overlayLayerSources(); + var opacity = context.container().selectAll('.main-map .layer-background').style('opacity'); + var caches = osm && osm.caches(); + var baseEntities = context.history().graph().base().entities; // Show sidebar and disable the sidebar resizing button + // (this needs to be before `context.inIntro(true)`) + + context.ui().sidebar.expand(); + context.container().selectAll('button.sidebar-toggle').classed('disabled', true); // Block saving + + context.inIntro(true); // Load semi-real data used in intro + + if (osm) { + osm.toggle(false).reset(); + } + + context.history().reset(); + context.history().merge(Object.values(coreGraph().load(_introGraph).entities)); + context.history().checkpoint('initial'); // Setup imagery + + var imagery = context.background().findSource(INTRO_IMAGERY); + + if (imagery) { + context.background().baseLayerSource(imagery); + } else { + context.background().bing(); + } + + overlays.forEach(function (d) { + return context.background().toggleOverlayLayer(d); + }); // Setup data layers (only OSM) + + var layers = context.layers(); + layers.all().forEach(function (item) { + // if the layer has the function `enabled` + if (typeof item.layer.enabled === 'function') { + item.layer.enabled(item.id === 'osm'); + } + }); + context.container().selectAll('.main-map .layer-background').style('opacity', 1); + var curtain = uiCurtain(context.container().node()); + selection.call(curtain); // Store that the user started the walkthrough.. + + corePreferences('walkthrough_started', 'yes'); // Restore previous walkthrough progress.. + + var storedProgress = corePreferences('walkthrough_progress') || ''; + var progress = storedProgress.split(';').filter(Boolean); + var chapters = chapterFlow.map(function (chapter, i) { + var s = chapterUi[chapter](context, curtain.reveal).on('done', function () { + buttons.filter(function (d) { + return d.title === s.title; + }).classed('finished', true); + + if (i < chapterFlow.length - 1) { + var next = chapterFlow[i + 1]; + context.container().select("button.chapter-".concat(next)).classed('next', true); + } // Store walkthrough progress.. + + + progress.push(chapter); + corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';')); + }); + return s; + }); + chapters[chapters.length - 1].on('startEditing', function () { + // Store walkthrough progress.. + progress.push('startEditing'); + corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';')); // Store if walkthrough is completed.. + + var incomplete = utilArrayDifference(chapterFlow, progress); + + if (!incomplete.length) { + corePreferences('walkthrough_completed', 'yes'); + } + + curtain.remove(); + navwrap.remove(); + context.container().selectAll('.main-map .layer-background').style('opacity', opacity); + context.container().selectAll('button.sidebar-toggle').classed('disabled', false); + + if (osm) { + osm.toggle(true).reset().caches(caches); + } + + context.history().reset().merge(Object.values(baseEntities)); + context.background().baseLayerSource(background); + overlays.forEach(function (d) { + return context.background().toggleOverlayLayer(d); + }); + + if (history) { + context.history().fromJSON(history, false); + } + + context.map().centerZoom(center, zoom); + window.location.replace(hash); + context.inIntro(false); + }); + var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD'); + navwrap.append('svg').attr('class', 'intro-nav-wrap-logo').append('use').attr('xlink:href', '#iD-logo-walkthrough'); + var buttonwrap = navwrap.append('div').attr('class', 'joined').selectAll('button.chapter'); + var buttons = buttonwrap.data(chapters).enter().append('button').attr('class', function (d, i) { + return "chapter chapter-".concat(chapterFlow[i]); + }).on('click', enterChapter); + buttons.append('span').html(function (d) { + return _t.html(d.title); + }); + buttons.append('span').attr('class', 'status').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline')); + enterChapter(null, chapters[0]); + + function enterChapter(d3_event, newChapter) { + if (_currChapter) { + _currChapter.exit(); + } + + context.enter(modeBrowse(context)); + _currChapter = newChapter; + + _currChapter.enter(); + + buttons.classed('next', false).classed('active', function (d) { + return d.title === _currChapter.title; + }); + } + } + + return intro; + } + + function uiIssuesInfo(context) { + var warningsItem = { + id: 'warnings', + count: 0, + iconID: 'iD-icon-alert', + descriptionID: 'issues.warnings_and_errors' + }; + var resolvedItem = { + id: 'resolved', + count: 0, + iconID: 'iD-icon-apply', + descriptionID: 'issues.user_resolved_issues' + }; + + function update(selection) { + var shownItems = []; + var liveIssues = context.validator().getIssues({ + what: corePreferences('validate-what') || 'edited', + where: corePreferences('validate-where') || 'all' + }); + + if (liveIssues.length) { + warningsItem.count = liveIssues.length; + shownItems.push(warningsItem); + } + + if (corePreferences('validate-what') === 'all') { + var resolvedIssues = context.validator().getResolvedIssues(); + + if (resolvedIssues.length) { + resolvedItem.count = resolvedIssues.length; + shownItems.push(resolvedItem); + } + } + + var chips = selection.selectAll('.chip').data(shownItems, function (d) { + return d.id; + }); + chips.exit().remove(); + var enter = chips.enter().append('a').attr('class', function (d) { + return 'chip ' + d.id + '-count'; + }).attr('href', '#').each(function (d) { + var chipSelection = select(this); + var tooltipBehavior = uiTooltip().placement('top').title(_t.html(d.descriptionID)); + chipSelection.call(tooltipBehavior).on('click', function (d3_event) { + d3_event.preventDefault(); + tooltipBehavior.hide(select(this)); // open the Issues pane + + context.ui().togglePanes(context.container().select('.map-panes .issues-pane')); + }); + chipSelection.call(svgIcon('#' + d.iconID)); + }); + enter.append('span').attr('class', 'count'); + enter.merge(chips).selectAll('span.count').html(function (d) { + return d.count.toString(); + }); + } + + return function (selection) { + update(selection); + context.validator().on('validated.infobox', function () { + update(selection); + }); + }; + } + + function uiMapInMap(context) { + function mapInMap(selection) { + var backgroundLayer = rendererTileLayer(context); + var overlayLayers = {}; + var projection = geoRawMercator(); + var dataLayer = svgData(projection, context).showLabels(false); + var debugLayer = svgDebug(projection, context); + var zoom = d3_zoom().scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)]).on('start', zoomStarted).on('zoom', zoomed).on('end', zoomEnded); + var wrap = select(null); + var tiles = select(null); + var viewport = select(null); + var _isTransformed = false; + var _isHidden = true; + var _skipEvents = false; + var _gesture = null; + var _zDiff = 6; // by default, minimap renders at (main zoom - 6) + + var _dMini; // dimensions of minimap + + + var _cMini; // center pixel of minimap + + + var _tStart; // transform at start of gesture + + + var _tCurr; // transform at most recent event + + + var _timeoutID; + + function zoomStarted() { + if (_skipEvents) return; + _tStart = _tCurr = projection.transform(); + _gesture = null; + } + + function zoomed(d3_event) { + if (_skipEvents) return; + var x = d3_event.transform.x; + var y = d3_event.transform.y; + var k = d3_event.transform.k; + var isZooming = k !== _tStart.k; + var isPanning = x !== _tStart.x || y !== _tStart.y; + + if (!isZooming && !isPanning) { + return; // no change + } // lock in either zooming or panning, don't allow both in minimap. + + + if (!_gesture) { + _gesture = isZooming ? 'zoom' : 'pan'; + } + + var tMini = projection.transform(); + var tX, tY, scale; + + if (_gesture === 'zoom') { + scale = k / tMini.k; + tX = (_cMini[0] / scale - _cMini[0]) * scale; + tY = (_cMini[1] / scale - _cMini[1]) * scale; + } else { + k = tMini.k; + scale = 1; + tX = x - tMini.x; + tY = y - tMini.y; + } + + utilSetTransform(tiles, tX, tY, scale); + utilSetTransform(viewport, 0, 0, scale); + _isTransformed = true; + _tCurr = identity$2.translate(x, y).scale(k); + var zMain = geoScaleToZoom(context.projection.scale()); + var zMini = geoScaleToZoom(k); + _zDiff = zMain - zMini; + queueRedraw(); + } + + function zoomEnded() { + if (_skipEvents) return; + if (_gesture !== 'pan') return; + updateProjection(); + _gesture = null; + context.map().center(projection.invert(_cMini)); // recenter main map.. + } + + function updateProjection() { + var loc = context.map().center(); + var tMain = context.projection.transform(); + var zMain = geoScaleToZoom(tMain.k); + var zMini = Math.max(zMain - _zDiff, 0.5); + var kMini = geoZoomToScale(zMini); + projection.translate([tMain.x, tMain.y]).scale(kMini); + var point = projection(loc); + var mouse = _gesture === 'pan' ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0]; + var xMini = _cMini[0] - point[0] + tMain.x + mouse[0]; + var yMini = _cMini[1] - point[1] + tMain.y + mouse[1]; + projection.translate([xMini, yMini]).clipExtent([[0, 0], _dMini]); + _tCurr = projection.transform(); + + if (_isTransformed) { + utilSetTransform(tiles, 0, 0); + utilSetTransform(viewport, 0, 0); + _isTransformed = false; + } + + zoom.scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]); + _skipEvents = true; + wrap.call(zoom.transform, _tCurr); + _skipEvents = false; + } + + function redraw() { + clearTimeout(_timeoutID); + if (_isHidden) return; + updateProjection(); + var zMini = geoScaleToZoom(projection.scale()); // setup tile container + + tiles = wrap.selectAll('.map-in-map-tiles').data([0]); + tiles = tiles.enter().append('div').attr('class', 'map-in-map-tiles').merge(tiles); // redraw background + + backgroundLayer.source(context.background().baseLayerSource()).projection(projection).dimensions(_dMini); + var background = tiles.selectAll('.map-in-map-background').data([0]); + background.enter().append('div').attr('class', 'map-in-map-background').merge(background).call(backgroundLayer); // redraw overlay + + var overlaySources = context.background().overlayLayerSources(); + var activeOverlayLayers = []; + + for (var i = 0; i < overlaySources.length; i++) { + if (overlaySources[i].validZoom(zMini)) { + if (!overlayLayers[i]) overlayLayers[i] = rendererTileLayer(context); + activeOverlayLayers.push(overlayLayers[i].source(overlaySources[i]).projection(projection).dimensions(_dMini)); + } + } + + var overlay = tiles.selectAll('.map-in-map-overlay').data([0]); + overlay = overlay.enter().append('div').attr('class', 'map-in-map-overlay').merge(overlay); + var overlays = overlay.selectAll('div').data(activeOverlayLayers, function (d) { + return d.source().name(); + }); + overlays.exit().remove(); + overlays = overlays.enter().append('div').merge(overlays).each(function (layer) { + select(this).call(layer); + }); + var dataLayers = tiles.selectAll('.map-in-map-data').data([0]); + dataLayers.exit().remove(); + dataLayers = dataLayers.enter().append('svg').attr('class', 'map-in-map-data').merge(dataLayers).call(dataLayer).call(debugLayer); // redraw viewport bounding box + + if (_gesture !== 'pan') { + var getPath = d3_geoPath(projection); + var bbox = { + type: 'Polygon', + coordinates: [context.map().extent().polygon()] + }; + viewport = wrap.selectAll('.map-in-map-viewport').data([0]); + viewport = viewport.enter().append('svg').attr('class', 'map-in-map-viewport').merge(viewport); + var path = viewport.selectAll('.map-in-map-bbox').data([bbox]); + path.enter().append('path').attr('class', 'map-in-map-bbox').merge(path).attr('d', getPath).classed('thick', function (d) { + return getPath.area(d) < 30; + }); + } + } + + function queueRedraw() { + clearTimeout(_timeoutID); + _timeoutID = setTimeout(function () { + redraw(); + }, 750); + } + + function toggle(d3_event) { + if (d3_event) d3_event.preventDefault(); + _isHidden = !_isHidden; + context.container().select('.minimap-toggle-item').classed('active', !_isHidden).select('input').property('checked', !_isHidden); + + if (_isHidden) { + wrap.style('display', 'block').style('opacity', '1').transition().duration(200).style('opacity', '0').on('end', function () { + selection.selectAll('.map-in-map').style('display', 'none'); + }); + } else { + wrap.style('display', 'block').style('opacity', '0').transition().duration(200).style('opacity', '1').on('end', function () { + redraw(); + }); + } + } + + uiMapInMap.toggle = toggle; + wrap = selection.selectAll('.map-in-map').data([0]); + wrap = wrap.enter().append('div').attr('class', 'map-in-map').style('display', _isHidden ? 'none' : 'block').call(zoom).on('dblclick.zoom', null).merge(wrap); // reflow warning: Hardcode dimensions - currently can't resize it anyway.. + + _dMini = [200, 150]; //utilGetDimensions(wrap); + + _cMini = geoVecScale(_dMini, 0.5); + context.map().on('drawn.map-in-map', function (drawn) { + if (drawn.full === true) { + redraw(); + } + }); + redraw(); + context.keybinding().on(_t('background.minimap.key'), toggle); + } + + return mapInMap; + } + + function uiNotice(context) { + return function (selection) { + var div = selection.append('div').attr('class', 'notice'); + var button = div.append('button').attr('class', 'zoom-to notice fillD').on('click', function () { + context.map().zoomEase(context.minEditableZoom()); + }).on('wheel', function (d3_event) { + // let wheel events pass through #4482 + var e2 = new WheelEvent(d3_event.type, d3_event); + context.surface().node().dispatchEvent(e2); + }); + button.call(svgIcon('#iD-icon-plus', 'pre-text')).append('span').attr('class', 'label').html(_t.html('zoom_in_edit')); + + function disableTooHigh() { + var canEdit = context.map().zoom() >= context.minEditableZoom(); + div.style('display', canEdit ? 'none' : 'block'); + } + + context.map().on('move.notice', debounce(disableTooHigh, 500)); + disableTooHigh(); + }; + } + + function uiPhotoviewer(context) { + var dispatch = dispatch$8('resize'); + + var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; + + function photoviewer(selection) { + selection.append('button').attr('class', 'thumb-hide').on('click', function () { + if (services.streetside) { + services.streetside.hideViewer(context); + } + + if (services.mapillary) { + services.mapillary.hideViewer(context); + } + + if (services.openstreetcam) { + services.openstreetcam.hideViewer(context); + } + }).append('div').call(svgIcon('#iD-icon-close')); + + function preventDefault(d3_event) { + d3_event.preventDefault(); + } + + selection.append('button').attr('class', 'resize-handle-xy').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch, { + resizeOnX: true, + resizeOnY: true + })); + selection.append('button').attr('class', 'resize-handle-x').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch, { + resizeOnX: true + })); + selection.append('button').attr('class', 'resize-handle-y').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch, { + resizeOnY: true + })); + + function buildResizeListener(target, eventName, dispatch, options) { + var resizeOnX = !!options.resizeOnX; + var resizeOnY = !!options.resizeOnY; + var minHeight = options.minHeight || 240; + var minWidth = options.minWidth || 320; + var pointerId; + var startX; + var startY; + var startWidth; + var startHeight; + + function startResize(d3_event) { + if (pointerId !== (d3_event.pointerId || 'mouse')) return; + d3_event.preventDefault(); + d3_event.stopPropagation(); + var mapSize = context.map().dimensions(); + + if (resizeOnX) { + var maxWidth = mapSize[0]; + var newWidth = clamp(startWidth + d3_event.clientX - startX, minWidth, maxWidth); + target.style('width', newWidth + 'px'); + } + + if (resizeOnY) { + var maxHeight = mapSize[1] - 90; // preserve space at top/bottom of map + + var newHeight = clamp(startHeight + startY - d3_event.clientY, minHeight, maxHeight); + target.style('height', newHeight + 'px'); + } + + dispatch.call(eventName, target, utilGetDimensions(target, true)); + } + + function clamp(num, min, max) { + return Math.max(min, Math.min(num, max)); + } + + function stopResize(d3_event) { + if (pointerId !== (d3_event.pointerId || 'mouse')) return; + d3_event.preventDefault(); + d3_event.stopPropagation(); // remove all the listeners we added + + select(window).on('.' + eventName, null); + } + + return function initResize(d3_event) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + pointerId = d3_event.pointerId || 'mouse'; + startX = d3_event.clientX; + startY = d3_event.clientY; + var targetRect = target.node().getBoundingClientRect(); + startWidth = targetRect.width; + startHeight = targetRect.height; + select(window).on(_pointerPrefix + 'move.' + eventName, startResize, false).on(_pointerPrefix + 'up.' + eventName, stopResize, false); + + if (_pointerPrefix === 'pointer') { + select(window).on('pointercancel.' + eventName, stopResize, false); + } + }; + } + } + + photoviewer.onMapResize = function () { + var photoviewer = context.container().select('.photoviewer'); + var content = context.container().select('.main-content'); + var mapDimensions = utilGetDimensions(content, true); // shrink photo viewer if it is too big + // (-90 preserves space at top and bottom of map used by menus) + + var photoDimensions = utilGetDimensions(photoviewer, true); + + if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > mapDimensions[1] - 90) { + var setPhotoDimensions = [Math.min(photoDimensions[0], mapDimensions[0]), Math.min(photoDimensions[1], mapDimensions[1] - 90)]; + photoviewer.style('width', setPhotoDimensions[0] + 'px').style('height', setPhotoDimensions[1] + 'px'); + dispatch.call('resize', photoviewer, setPhotoDimensions); + } + }; + + return utilRebind(photoviewer, dispatch, 'on'); + } + + function uiRestore(context) { + return function (selection) { + if (!context.history().hasRestorableChanges()) return; + var modalSelection = uiModal(selection, true); + modalSelection.select('.modal').attr('class', 'modal fillL'); + var introModal = modalSelection.select('.content'); + introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('restore.heading')); + introModal.append('div').attr('class', 'modal-section').append('p').html(_t.html('restore.description')); + var buttonWrap = introModal.append('div').attr('class', 'modal-actions'); + var restore = buttonWrap.append('button').attr('class', 'restore').on('click', function () { + context.history().restore(); + modalSelection.remove(); + }); + restore.append('svg').attr('class', 'logo logo-restore').append('use').attr('xlink:href', '#iD-logo-restore'); + restore.append('div').html(_t.html('restore.restore')); + var reset = buttonWrap.append('button').attr('class', 'reset').on('click', function () { + context.history().clearSaved(); + modalSelection.remove(); + }); + reset.append('svg').attr('class', 'logo logo-reset').append('use').attr('xlink:href', '#iD-logo-reset'); + reset.append('div').html(_t.html('restore.reset')); + restore.node().focus(); + }; + } + + function uiScale(context) { + var projection = context.projection, + isImperial = !_mainLocalizer.usesMetric(), + maxLength = 180, + tickHeight = 8; + + function scaleDefs(loc1, loc2) { + var lat = (loc2[1] + loc1[1]) / 2, + conversion = isImperial ? 3.28084 : 1, + dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion, + scale = { + dist: 0, + px: 0, + text: '' + }, + buckets, + i, + val, + dLon; + + if (isImperial) { + buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1]; + } else { + buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1]; + } // determine a user-friendly endpoint for the scale + + + for (i = 0; i < buckets.length; i++) { + val = buckets[i]; + + if (dist >= val) { + scale.dist = Math.floor(dist / val) * val; + break; + } else { + scale.dist = +dist.toFixed(2); + } + } + + dLon = geoMetersToLon(scale.dist / conversion, lat); + scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]); + scale.text = displayLength(scale.dist / conversion, isImperial); + return scale; + } + + function update(selection) { + // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn) + var dims = context.map().dimensions(), + loc1 = projection.invert([0, dims[1]]), + loc2 = projection.invert([maxLength, dims[1]]), + scale = scaleDefs(loc1, loc2); + selection.select('.scale-path').attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight); + selection.select('.scale-text').style(_mainLocalizer.textDirection() === 'ltr' ? 'left' : 'right', scale.px + 16 + 'px').html(scale.text); + } + + return function (selection) { + function switchUnits() { + isImperial = !isImperial; + selection.call(update); + } + + var scalegroup = selection.append('svg').attr('class', 'scale').on('click', switchUnits).append('g').attr('transform', 'translate(10,11)'); + scalegroup.append('path').attr('class', 'scale-path'); + selection.append('div').attr('class', 'scale-text'); + selection.call(update); + context.map().on('move.scale', function () { + update(selection); + }); + }; + } + + function uiShortcuts(context) { + var detected = utilDetect(); + var _activeTab = 0; + + var _modalSelection; + + var _selection = select(null); + + var _dataShortcuts; + + function shortcutsModal(_modalSelection) { + _modalSelection.select('.modal').classed('modal-shortcuts', true); + + var content = _modalSelection.select('.content'); + + content.append('div').attr('class', 'modal-section').append('h3').html(_t.html('shortcuts.title')); + _mainFileFetcher.get('shortcuts').then(function (data) { + _dataShortcuts = data; + content.call(render); + })["catch"](function () { + /* ignore */ + }); + } + + function render(selection) { + if (!_dataShortcuts) return; + var wrapper = selection.selectAll('.wrapper').data([0]); + var wrapperEnter = wrapper.enter().append('div').attr('class', 'wrapper modal-section'); + var tabsBar = wrapperEnter.append('div').attr('class', 'tabs-bar'); + var shortcutsList = wrapperEnter.append('div').attr('class', 'shortcuts-list'); + wrapper = wrapper.merge(wrapperEnter); + var tabs = tabsBar.selectAll('.tab').data(_dataShortcuts); + var tabsEnter = tabs.enter().append('a').attr('class', 'tab').attr('href', '#').on('click', function (d3_event, d) { + d3_event.preventDefault(); + + var i = _dataShortcuts.indexOf(d); + + _activeTab = i; + render(selection); + }); + tabsEnter.append('span').html(function (d) { + return _t.html(d.text); + }); // Update + + wrapper.selectAll('.tab').classed('active', function (d, i) { + return i === _activeTab; + }); + var shortcuts = shortcutsList.selectAll('.shortcut-tab').data(_dataShortcuts); + var shortcutsEnter = shortcuts.enter().append('div').attr('class', function (d) { + return 'shortcut-tab shortcut-tab-' + d.tab; + }); + var columnsEnter = shortcutsEnter.selectAll('.shortcut-column').data(function (d) { + return d.columns; + }).enter().append('table').attr('class', 'shortcut-column'); + var rowsEnter = columnsEnter.selectAll('.shortcut-row').data(function (d) { + return d.rows; + }).enter().append('tr').attr('class', 'shortcut-row'); + var sectionRows = rowsEnter.filter(function (d) { + return !d.shortcuts; + }); + sectionRows.append('td'); + sectionRows.append('td').attr('class', 'shortcut-section').append('h3').html(function (d) { + return _t.html(d.text); + }); + var shortcutRows = rowsEnter.filter(function (d) { + return d.shortcuts; + }); + var shortcutKeys = shortcutRows.append('td').attr('class', 'shortcut-keys'); + var modifierKeys = shortcutKeys.filter(function (d) { + return d.modifiers; + }); + modifierKeys.selectAll('kbd.modifier').data(function (d) { + if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') { + return ['⌘']; + } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') { + return []; + } else { + return d.modifiers; + } + }).enter().each(function () { + var selection = select(this); + selection.append('kbd').attr('class', 'modifier').html(function (d) { + return uiCmd.display(d); + }); + selection.append('span').html('+'); + }); + shortcutKeys.selectAll('kbd.shortcut').data(function (d) { + var arr = d.shortcuts; + + if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') { + arr = ['Y']; + } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') { + arr = ['F11']; + } // replace translations + + + arr = arr.map(function (s) { + return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s); + }); + return utilArrayUniq(arr).map(function (s) { + return { + shortcut: s, + separator: d.separator, + suffix: d.suffix + }; + }); + }).enter().each(function (d, i, nodes) { + var selection = select(this); + var click = d.shortcut.toLowerCase().match(/(.*).click/); + + if (click && click[1]) { + // replace "left_click", "right_click" with mouse icon + selection.call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation')); + } else if (d.shortcut.toLowerCase() === 'long-press') { + selection.call(svgIcon('#iD-walkthrough-longpress', 'longpress operation')); + } else if (d.shortcut.toLowerCase() === 'tap') { + selection.call(svgIcon('#iD-walkthrough-tap', 'tap operation')); + } else { + selection.append('kbd').attr('class', 'shortcut').html(function (d) { + return d.shortcut; + }); + } + + if (i < nodes.length - 1) { + selection.append('span').html(d.separator || "\xA0" + _t.html('shortcuts.or') + "\xA0"); + } else if (i === nodes.length - 1 && d.suffix) { + selection.append('span').html(d.suffix); + } + }); + shortcutKeys.filter(function (d) { + return d.gesture; + }).each(function () { + var selection = select(this); + selection.append('span').html('+'); + selection.append('span').attr('class', 'gesture').html(function (d) { + return _t.html(d.gesture); + }); + }); + shortcutRows.append('td').attr('class', 'shortcut-desc').html(function (d) { + return d.text ? _t.html(d.text) : "\xA0"; + }); // Update + + wrapper.selectAll('.shortcut-tab').style('display', function (d, i) { + return i === _activeTab ? 'flex' : 'none'; + }); + } + + return function (selection, show) { + _selection = selection; + + if (show) { + _modalSelection = uiModal(selection); + + _modalSelection.call(shortcutsModal); + } else { + context.keybinding().on([_t('shortcuts.toggle.key'), '?'], function () { + if (context.container().selectAll('.modal-shortcuts').size()) { + // already showing + if (_modalSelection) { + _modalSelection.close(); + + _modalSelection = null; + } + } else { + _modalSelection = uiModal(_selection); + + _modalSelection.call(shortcutsModal); + } + }); + } + }; + } + + function uiDataHeader() { + var _datum; + + function dataHeader(selection) { + var header = selection.selectAll('.data-header').data(_datum ? [_datum] : [], function (d) { + return d.__featurehash__; + }); + header.exit().remove(); + var headerEnter = header.enter().append('div').attr('class', 'data-header'); + var iconEnter = headerEnter.append('div').attr('class', 'data-header-icon'); + iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-data', 'note-fill')); + headerEnter.append('div').attr('class', 'data-header-label').html(_t.html('map_data.layers.custom.title')); + } + + dataHeader.datum = function (val) { + if (!arguments.length) return _datum; + _datum = val; + return this; + }; + + return dataHeader; + } + + // It is keyed on the `value` of the entry. Data should be an array of objects like: + // [{ + // value: 'string value', // required + // display: 'label html' // optional + // title: 'hover text' // optional + // terms: ['search terms'] // optional + // }, ...] + + var _comboHideTimerID; + + function uiCombobox(context, klass) { + var dispatch = dispatch$8('accept', 'cancel'); + var container = context.container(); + var _suggestions = []; + var _data = []; + var _fetched = {}; + var _selected = null; + var _canAutocomplete = true; + var _caseSensitive = false; + var _cancelFetch = false; + var _minItems = 2; + var _tDown = 0; + + var _mouseEnterHandler, _mouseLeaveHandler; + + var _fetcher = function _fetcher(val, cb) { + cb(_data.filter(function (d) { + var terms = d.terms || []; + terms.push(d.value); + return terms.some(function (term) { + return term.toString().toLowerCase().indexOf(val.toLowerCase()) !== -1; + }); + })); + }; + + var combobox = function combobox(input, attachTo) { + if (!input || input.empty()) return; + input.classed('combobox-input', true).on('focus.combo-input', focus).on('blur.combo-input', blur).on('keydown.combo-input', keydown).on('keyup.combo-input', keyup).on('input.combo-input', change).on('mousedown.combo-input', mousedown).each(function () { + var parent = this.parentNode; + var sibling = this.nextSibling; + select(parent).selectAll('.combobox-caret').filter(function (d) { + return d === input.node(); + }).data([input.node()]).enter().insert('div', function () { + return sibling; + }).attr('class', 'combobox-caret').on('mousedown.combo-caret', function (d3_event) { + d3_event.preventDefault(); // don't steal focus from input + + input.node().focus(); // focus the input as if it was clicked + + mousedown(d3_event); + }).on('mouseup.combo-caret', function (d3_event) { + d3_event.preventDefault(); // don't steal focus from input + + mouseup(d3_event); + }); + }); + + function mousedown(d3_event) { + if (d3_event.button !== 0) return; // left click only + + _tDown = +new Date(); // clear selection + + var start = input.property('selectionStart'); + var end = input.property('selectionEnd'); + + if (start !== end) { + var val = utilGetSetValue(input); + input.node().setSelectionRange(val.length, val.length); + return; + } + + input.on('mouseup.combo-input', mouseup); + } + + function mouseup(d3_event) { + input.on('mouseup.combo-input', null); + if (d3_event.button !== 0) return; // left click only + + if (input.node() !== document.activeElement) return; // exit if this input is not focused + + var start = input.property('selectionStart'); + var end = input.property('selectionEnd'); + if (start !== end) return; // exit if user is selecting + // not showing or showing for a different field - try to show it. + + var combo = container.selectAll('.combobox'); + + if (combo.empty() || combo.datum() !== input.node()) { + var tOrig = _tDown; + window.setTimeout(function () { + if (tOrig !== _tDown) return; // exit if user double clicked + + fetchComboData('', function () { + show(); + render(); + }); + }, 250); + } else { + hide(); + } + } + + function focus() { + fetchComboData(''); // prefetch values (may warm taginfo cache) + } + + function blur() { + _comboHideTimerID = window.setTimeout(hide, 75); + } + + function show() { + hide(); // remove any existing + + container.insert('div', ':first-child').datum(input.node()).attr('class', 'combobox' + (klass ? ' combobox-' + klass : '')).style('position', 'absolute').style('display', 'block').style('left', '0px').on('mousedown.combo-container', function (d3_event) { + // prevent moving focus out of the input field + d3_event.preventDefault(); + }); + container.on('scroll.combo-scroll', render, true); + } + + function hide() { + if (_comboHideTimerID) { + window.clearTimeout(_comboHideTimerID); + _comboHideTimerID = undefined; + } + + container.selectAll('.combobox').remove(); + container.on('scroll.combo-scroll', null); + } + + function keydown(d3_event) { + var shown = !container.selectAll('.combobox').empty(); + var tagName = input.node() ? input.node().tagName.toLowerCase() : ''; + + switch (d3_event.keyCode) { + case 8: // ⌫ Backspace + + case 46: + // ⌦ Delete + d3_event.stopPropagation(); + _selected = null; + render(); + input.on('input.combo-input', function () { + var start = input.property('selectionStart'); + input.node().setSelectionRange(start, start); + input.on('input.combo-input', change); + }); + break; + + case 9: + // ⇥ Tab + accept(); + break; + + case 13: + // ↩ Return + d3_event.preventDefault(); + d3_event.stopPropagation(); + break; + + case 38: + // ↑ Up arrow + if (tagName === 'textarea' && !shown) return; + d3_event.preventDefault(); + + if (tagName === 'input' && !shown) { + show(); + } + + nav(-1); + break; + + case 40: + // ↓ Down arrow + if (tagName === 'textarea' && !shown) return; + d3_event.preventDefault(); + + if (tagName === 'input' && !shown) { + show(); + } + + nav(+1); + break; + } + } + + function keyup(d3_event) { + switch (d3_event.keyCode) { + case 27: + // ⎋ Escape + cancel(); + break; + + case 13: + // ↩ Return + accept(); + break; + } + } // Called whenever the input value is changed (e.g. on typing) + + + function change() { + fetchComboData(value(), function () { + _selected = null; + var val = input.property('value'); + + if (_suggestions.length) { + if (input.property('selectionEnd') === val.length) { + _selected = tryAutocomplete(); + } + + if (!_selected) { + _selected = val; + } + } + + if (val.length) { + var combo = container.selectAll('.combobox'); + + if (combo.empty()) { + show(); + } + } else { + hide(); + } + + render(); + }); + } // Called when the user presses up/down arrows to navigate the list + + + function nav(dir) { + if (_suggestions.length) { + // try to determine previously selected index.. + var index = -1; + + for (var i = 0; i < _suggestions.length; i++) { + if (_selected && _suggestions[i].value === _selected) { + index = i; + break; + } + } // pick new _selected + + + index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0); + _selected = _suggestions[index].value; + input.property('value', _selected); + } + + render(); + ensureVisible(); + } + + function ensureVisible() { + var combo = container.selectAll('.combobox'); + if (combo.empty()) return; + var containerRect = container.node().getBoundingClientRect(); + var comboRect = combo.node().getBoundingClientRect(); + + if (comboRect.bottom > containerRect.bottom) { + var node = attachTo ? attachTo.node() : input.node(); + node.scrollIntoView({ + behavior: 'instant', + block: 'center' + }); + render(); + } // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move + + + var selected = combo.selectAll('.combobox-option.selected').node(); + + if (selected) { + selected.scrollIntoView({ + behavior: 'smooth', + block: 'nearest' + }); + } + } + + function value() { + var value = input.property('value'); + var start = input.property('selectionStart'); + var end = input.property('selectionEnd'); + + if (start && end) { + value = value.substring(0, start); + } + + return value; + } + + function fetchComboData(v, cb) { + _cancelFetch = false; + + _fetcher.call(input, v, function (results) { + // already chose a value, don't overwrite or autocomplete it + if (_cancelFetch) return; + _suggestions = results; + results.forEach(function (d) { + _fetched[d.value] = d; + }); + + if (cb) { + cb(); + } + }); + } + + function tryAutocomplete() { + if (!_canAutocomplete) return; + var val = _caseSensitive ? value() : value().toLowerCase(); + if (!val) return; // Don't autocomplete if user is typing a number - #4935 + + if (!isNaN(parseFloat(val)) && isFinite(val)) return; + var bestIndex = -1; + + for (var i = 0; i < _suggestions.length; i++) { + var suggestion = _suggestions[i].value; + var compare = _caseSensitive ? suggestion : suggestion.toLowerCase(); // if search string matches suggestion exactly, pick it.. + + if (compare === val) { + bestIndex = i; + break; // otherwise lock in the first result that starts with the search string.. + } else if (bestIndex === -1 && compare.indexOf(val) === 0) { + bestIndex = i; + } + } + + if (bestIndex !== -1) { + var bestVal = _suggestions[bestIndex].value; + input.property('value', bestVal); + input.node().setSelectionRange(val.length, bestVal.length); + return bestVal; + } + } + + function render() { + if (_suggestions.length < _minItems || document.activeElement !== input.node()) { + hide(); + return; + } + + var shown = !container.selectAll('.combobox').empty(); + if (!shown) return; + var combo = container.selectAll('.combobox'); + var options = combo.selectAll('.combobox-option').data(_suggestions, function (d) { + return d.value; + }); + options.exit().remove(); // enter/update + + options.enter().append('a').attr('class', function (d) { + return 'combobox-option ' + (d.klass || ''); + }).attr('title', function (d) { + return d.title; + }).html(function (d) { + return d.display || d.value; + }).on('mouseenter', _mouseEnterHandler).on('mouseleave', _mouseLeaveHandler).merge(options).classed('selected', function (d) { + return d.value === _selected; + }).on('click.combo-option', accept).order(); + var node = attachTo ? attachTo.node() : input.node(); + var containerRect = container.node().getBoundingClientRect(); + var rect = node.getBoundingClientRect(); + combo.style('left', rect.left + 5 - containerRect.left + 'px').style('width', rect.width - 10 + 'px').style('top', rect.height + rect.top - containerRect.top + 'px'); + } // Dispatches an 'accept' event + // Then hides the combobox. + + + function accept(d3_event, d) { + _cancelFetch = true; + var thiz = input.node(); + + if (d) { + // user clicked on a suggestion + utilGetSetValue(input, d.value); // replace field contents + + utilTriggerEvent(input, 'change'); + } // clear (and keep) selection + + + var val = utilGetSetValue(input); + thiz.setSelectionRange(val.length, val.length); + d = _fetched[val]; + dispatch.call('accept', thiz, d, val); + hide(); + } // Dispatches an 'cancel' event + // Then hides the combobox. + + + function cancel() { + _cancelFetch = true; + var thiz = input.node(); // clear (and remove) selection, and replace field contents + + var val = utilGetSetValue(input); + var start = input.property('selectionStart'); + var end = input.property('selectionEnd'); + val = val.slice(0, start) + val.slice(end); + utilGetSetValue(input, val); + thiz.setSelectionRange(val.length, val.length); + dispatch.call('cancel', thiz); + hide(); + } + }; + + combobox.canAutocomplete = function (val) { + if (!arguments.length) return _canAutocomplete; + _canAutocomplete = val; + return combobox; + }; + + combobox.caseSensitive = function (val) { + if (!arguments.length) return _caseSensitive; + _caseSensitive = val; + return combobox; + }; + + combobox.data = function (val) { + if (!arguments.length) return _data; + _data = val; + return combobox; + }; + + combobox.fetcher = function (val) { + if (!arguments.length) return _fetcher; + _fetcher = val; + return combobox; + }; + + combobox.minItems = function (val) { + if (!arguments.length) return _minItems; + _minItems = val; + return combobox; + }; + + combobox.itemsMouseEnter = function (val) { + if (!arguments.length) return _mouseEnterHandler; + _mouseEnterHandler = val; + return combobox; + }; + + combobox.itemsMouseLeave = function (val) { + if (!arguments.length) return _mouseLeaveHandler; + _mouseLeaveHandler = val; + return combobox; + }; + + return utilRebind(combobox, dispatch, 'on'); + } + + uiCombobox.off = function (input, context) { + input.on('focus.combo-input', null).on('blur.combo-input', null).on('keydown.combo-input', null).on('keyup.combo-input', null).on('input.combo-input', null).on('mousedown.combo-input', null).on('mouseup.combo-input', null); + context.container().on('scroll.combo-scroll', null); + }; + + function uiDisclosure(context, key, expandedDefault) { + var dispatch = dispatch$8('toggled'); + + var _expanded; + + var _label = utilFunctor(''); + + var _updatePreference = true; + + var _content = function _content() {}; + + var disclosure = function disclosure(selection) { + if (_expanded === undefined || _expanded === null) { + // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)` + var preference = corePreferences('disclosure.' + key + '.expanded'); + _expanded = preference === null ? !!expandedDefault : preference === 'true'; + } + + var hideToggle = selection.selectAll('.hide-toggle-' + key).data([0]); // enter + + var hideToggleEnter = hideToggle.enter().append('a').attr('href', '#').attr('class', 'hide-toggle hide-toggle-' + key).call(svgIcon('', 'pre-text', 'hide-toggle-icon')); + hideToggleEnter.append('span').attr('class', 'hide-toggle-text'); // update + + hideToggle = hideToggleEnter.merge(hideToggle); + hideToggle.on('click', toggle).classed('expanded', _expanded); + hideToggle.selectAll('.hide-toggle-text').html(_label()); + hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'); + var wrap = selection.selectAll('.disclosure-wrap').data([0]); // enter/update + + wrap = wrap.enter().append('div').attr('class', 'disclosure-wrap disclosure-wrap-' + key).merge(wrap).classed('hide', !_expanded); + + if (_expanded) { + wrap.call(_content); + } + + function toggle(d3_event) { + d3_event.preventDefault(); + _expanded = !_expanded; + + if (_updatePreference) { + corePreferences('disclosure.' + key + '.expanded', _expanded); + } + + hideToggle.classed('expanded', _expanded); + hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'); + wrap.call(uiToggle(_expanded)); + + if (_expanded) { + wrap.call(_content); + } + + dispatch.call('toggled', this, _expanded); + } + }; + + disclosure.label = function (val) { + if (!arguments.length) return _label; + _label = utilFunctor(val); + return disclosure; + }; + + disclosure.expanded = function (val) { + if (!arguments.length) return _expanded; + _expanded = val; + return disclosure; + }; + + disclosure.updatePreference = function (val) { + if (!arguments.length) return _updatePreference; + _updatePreference = val; + return disclosure; + }; + + disclosure.content = function (val) { + if (!arguments.length) return _content; + _content = val; + return disclosure; + }; + + return utilRebind(disclosure, dispatch, 'on'); + } + + // Can be labeled and collapsible. + + function uiSection(id, context) { + var _classes = utilFunctor(''); + + var _shouldDisplay; + + var _content; + + var _disclosure; + + var _label; + + var _expandedByDefault = utilFunctor(true); + + var _disclosureContent; + + var _disclosureExpanded; + + var _containerSelection = select(null); + + var section = { + id: id + }; + + section.classes = function (val) { + if (!arguments.length) return _classes; + _classes = utilFunctor(val); + return section; + }; + + section.label = function (val) { + if (!arguments.length) return _label; + _label = utilFunctor(val); + return section; + }; + + section.expandedByDefault = function (val) { + if (!arguments.length) return _expandedByDefault; + _expandedByDefault = utilFunctor(val); + return section; + }; + + section.shouldDisplay = function (val) { + if (!arguments.length) return _shouldDisplay; + _shouldDisplay = utilFunctor(val); + return section; + }; + + section.content = function (val) { + if (!arguments.length) return _content; + _content = val; + return section; + }; + + section.disclosureContent = function (val) { + if (!arguments.length) return _disclosureContent; + _disclosureContent = val; + return section; + }; + + section.disclosureExpanded = function (val) { + if (!arguments.length) return _disclosureExpanded; + _disclosureExpanded = val; + return section; + }; // may be called multiple times + + + section.render = function (selection) { + _containerSelection = selection.selectAll('.section-' + id).data([0]); + + var sectionEnter = _containerSelection.enter().append('div').attr('class', 'section section-' + id + ' ' + (_classes && _classes() || '')); + + _containerSelection = sectionEnter.merge(_containerSelection); + + _containerSelection.call(renderContent); + }; + + section.reRender = function () { + _containerSelection.call(renderContent); + }; + + section.selection = function () { + return _containerSelection; + }; + + section.disclosure = function () { + return _disclosure; + }; // may be called multiple times + + + function renderContent(selection) { + if (_shouldDisplay) { + var shouldDisplay = _shouldDisplay(); + + selection.classed('hide', !shouldDisplay); + + if (!shouldDisplay) { + selection.html(''); + return; + } + } + + if (_disclosureContent) { + if (!_disclosure) { + _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault()).label(_label || '') + /*.on('toggled', function(expanded) { + if (expanded) { selection.node().parentNode.scrollTop += 200; } + })*/ + .content(_disclosureContent); + } + + if (_disclosureExpanded !== undefined) { + _disclosure.expanded(_disclosureExpanded); + + _disclosureExpanded = undefined; + } + + selection.call(_disclosure); + return; + } + + if (_content) { + selection.call(_content); + } + } + + return section; + } + + // { + // key: 'string', // required + // value: 'string' // optional + // } + // -or- + // { + // qid: 'string' // brand wikidata (e.g. 'Q37158') + // } + // + + function uiTagReference(what) { + var wikibase = what.qid ? services.wikidata : services.osmWikibase; + var tagReference = {}; + + var _button = select(null); + + var _body = select(null); + + var _loaded; + + var _showing; + + function load() { + if (!wikibase) return; + + _button.classed('tag-reference-loading', true); + + wikibase.getDocs(what, gotDocs); + } + + function gotDocs(err, docs) { + _body.html(''); + + if (!docs || !docs.title) { + _body.append('p').attr('class', 'tag-reference-description').html(_t.html('inspector.no_documentation_key')); + + done(); + return; + } + + if (docs.imageURL) { + _body.append('img').attr('class', 'tag-reference-wiki-image').attr('src', docs.imageURL).on('load', function () { + done(); + }).on('error', function () { + select(this).remove(); + done(); + }); + } else { + done(); + } + + _body.append('p').attr('class', 'tag-reference-description').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')); + + if (docs.wiki) { + _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)); + } // Add link to info about "good changeset comments" - #2923 + + + if (what.key === 'comment') { + _body.append('a').attr('class', 'tag-reference-comment-link').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).attr('href', _t('commit.about_changeset_comments_link')).append('span').html(_t.html('commit.about_changeset_comments')); + } + } + + function done() { + _loaded = true; + + _button.classed('tag-reference-loading', false); + + _body.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1'); + + _showing = true; + + _button.selectAll('svg.icon use').each(function () { + var iconUse = select(this); + + if (iconUse.attr('href') === '#iD-icon-info') { + iconUse.attr('href', '#iD-icon-info-filled'); + } + }); + } + + function hide() { + _body.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () { + _body.classed('expanded', false); + }); + + _showing = false; + + _button.selectAll('svg.icon use').each(function () { + var iconUse = select(this); + + if (iconUse.attr('href') === '#iD-icon-info-filled') { + iconUse.attr('href', '#iD-icon-info'); + } + }); + } + + tagReference.button = function (selection, klass, iconName) { + _button = selection.selectAll('.tag-reference-button').data([0]); + _button = _button.enter().append('button').attr('class', 'tag-reference-button ' + (klass || '')).attr('title', _t('icons.information')).call(svgIcon('#iD-icon-' + (iconName || 'inspect'))).merge(_button); + + _button.on('click', function (d3_event) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + this.blur(); // avoid keeping focus on the button - #4641 + + if (_showing) { + hide(); + } else if (_loaded) { + done(); + } else { + load(); + } + }); + }; + + tagReference.body = function (selection) { + var itemID = what.qid || what.key + '-' + (what.value || ''); + _body = selection.selectAll('.tag-reference-body').data([itemID], function (d) { + return d; + }); + + _body.exit().remove(); + + _body = _body.enter().append('div').attr('class', 'tag-reference-body').style('max-height', '0').style('opacity', '0').merge(_body); + + if (_showing === false) { + hide(); + } + }; + + tagReference.showing = function (val) { + if (!arguments.length) return _showing; + _showing = val; + return tagReference; + }; + + return tagReference; + } + + function uiSectionRawTagEditor(id, context) { + var section = uiSection(id, context).classes('raw-tag-editor').label(function () { + var count = Object.keys(_tags).filter(function (d) { + return d; + }).length; + return _t('inspector.title_count', { + title: _t.html('inspector.tags'), + count: count + }); + }).expandedByDefault(false).disclosureContent(renderDisclosureContent); + var taginfo = services.taginfo; + var dispatch = dispatch$8('change'); + var availableViews = [{ + id: 'list', + icon: '#fas-th-list' + }, { + id: 'text', + icon: '#fas-i-cursor' + }]; + + var _tagView = corePreferences('raw-tag-editor-view') || 'list'; // 'list, 'text' + + + var _readOnlyTags = []; // the keys in the order we want them to display + + var _orderedKeys = []; + var _showBlank = false; + var _pendingChange = null; + + var _state; + + var _presets; + + var _tags; + + var _entityIDs; + + var _didInteract = false; + + function interacted() { + _didInteract = true; + } + + function renderDisclosureContent(wrap) { + // remove deleted keys + _orderedKeys = _orderedKeys.filter(function (key) { + return _tags[key] !== undefined; + }); // When switching to a different entity or changing the state (hover/select) + // reorder the keys alphabetically. + // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here. + // Otherwise leave their order alone - #5857, #5927 + + var all = Object.keys(_tags).sort(); + var missingKeys = utilArrayDifference(all, _orderedKeys); + + for (var i in missingKeys) { + _orderedKeys.push(missingKeys[i]); + } // assemble row data + + + var rowData = _orderedKeys.map(function (key, i) { + return { + index: i, + key: key, + value: _tags[key] + }; + }); // append blank row last, if necessary + + + if (!rowData.length || _showBlank) { + _showBlank = false; + rowData.push({ + index: rowData.length, + key: '', + value: '' + }); + } // View Options + + + var options = wrap.selectAll('.raw-tag-options').data([0]); + options.exit().remove(); + var optionsEnter = options.enter().insert('div', ':first-child').attr('class', 'raw-tag-options'); + var optionEnter = optionsEnter.selectAll('.raw-tag-option').data(availableViews, function (d) { + return d.id; + }).enter(); + optionEnter.append('button').attr('class', function (d) { + return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : ''); + }).attr('title', function (d) { + return _t('icons.' + d.id); + }).on('click', function (d3_event, d) { + _tagView = d.id; + corePreferences('raw-tag-editor-view', d.id); + wrap.selectAll('.raw-tag-option').classed('selected', function (datum) { + return datum === d; + }); + wrap.selectAll('.tag-text').classed('hide', d.id !== 'text').each(setTextareaHeight); + wrap.selectAll('.tag-list, .add-row').classed('hide', d.id !== 'list'); + }).each(function (d) { + select(this).call(svgIcon(d.icon)); + }); // View as Text + + var textData = rowsToText(rowData); + var textarea = wrap.selectAll('.tag-text').data([0]); + textarea = textarea.enter().append('textarea').attr('class', 'tag-text' + (_tagView !== 'text' ? ' hide' : '')).call(utilNoAuto).attr('placeholder', _t('inspector.key_value')).attr('spellcheck', 'false').merge(textarea); + textarea.call(utilGetSetValue, textData).each(setTextareaHeight).on('input', setTextareaHeight).on('focus', interacted).on('blur', textChanged).on('change', textChanged); // View as List + + var list = wrap.selectAll('.tag-list').data([0]); + list = list.enter().append('ul').attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : '')).merge(list); // Container for the Add button + + var addRowEnter = wrap.selectAll('.add-row').data([0]).enter().append('div').attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : '')); + addRowEnter.append('button').attr('class', 'add-tag').call(svgIcon('#iD-icon-plus', 'light')).on('click', addTag); + addRowEnter.append('div').attr('class', 'space-value'); // preserve space + + addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space + // Tag list items + + var items = list.selectAll('.tag-row').data(rowData, function (d) { + return d.key; + }); + items.exit().each(unbind).remove(); // Enter + + var itemsEnter = items.enter().append('li').attr('class', 'tag-row').classed('readonly', isReadOnly); + var innerWrap = itemsEnter.append('div').attr('class', 'inner-wrap'); + innerWrap.append('div').attr('class', 'key-wrap').append('input').property('type', 'text').attr('class', 'key').call(utilNoAuto).on('focus', interacted).on('blur', keyChange).on('change', keyChange); + 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); + innerWrap.append('button').attr('class', 'form-field-button remove').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete')); // Update + + items = items.merge(itemsEnter).sort(function (a, b) { + return a.index - b.index; + }); + items.each(function (d) { + var row = select(this); + var key = row.select('input.key'); // propagate bound data + + var value = row.select('input.value'); // propagate bound data + + if (_entityIDs && taginfo && _state !== 'hover') { + bindTypeahead(key, value); + } + + var referenceOptions = { + key: d.key + }; + + if (typeof d.value === 'string') { + referenceOptions.value = d.value; + } + + var reference = uiTagReference(referenceOptions); + + if (_state === 'hover') { + reference.showing(false); + } + + row.select('.inner-wrap') // propagate bound data + .call(reference.button); + row.call(reference.body); + row.select('button.remove'); // propagate bound data + }); + items.selectAll('input.key').attr('title', function (d) { + return d.key; + }).call(utilGetSetValue, function (d) { + return d.key; + }).attr('readonly', function (d) { + return isReadOnly(d) || typeof d.value !== 'string' || null; + }); + items.selectAll('input.value').attr('title', function (d) { + return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value; + }).classed('mixed', function (d) { + return Array.isArray(d.value); + }).attr('placeholder', function (d) { + return typeof d.value === 'string' ? null : _t('inspector.multiple_values'); + }).call(utilGetSetValue, function (d) { + return typeof d.value === 'string' ? d.value : ''; + }).attr('readonly', function (d) { + return isReadOnly(d) || null; + }); + items.selectAll('button.remove').on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag); // 'click' fires too late - #5878 + } + + function isReadOnly(d) { + for (var i = 0; i < _readOnlyTags.length; i++) { + if (d.key.match(_readOnlyTags[i]) !== null) { + return true; + } + } + + return false; + } + + function setTextareaHeight() { + if (_tagView !== 'text') return; + var selection = select(this); + var matches = selection.node().value.match(/\n/g); + var lineCount = 2 + Number(matches && matches.length); + var lineHeight = 20; + selection.style('height', lineCount * lineHeight + 'px'); + } + + function stringify(s) { + return JSON.stringify(s).slice(1, -1); // without leading/trailing " + } + + function unstringify(s) { + var leading = ''; + var trailing = ''; + + if (s.length < 1 || s.charAt(0) !== '"') { + leading = '"'; + } + + if (s.length < 2 || s.charAt(s.length - 1) !== '"' || s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\') { + trailing = '"'; + } + + return JSON.parse(leading + s + trailing); + } + + function rowsToText(rows) { + var str = rows.filter(function (row) { + return row.key && row.key.trim() !== ''; + }).map(function (row) { + var rawVal = row.value; + if (typeof rawVal !== 'string') rawVal = '*'; + var val = rawVal ? stringify(rawVal) : ''; + return stringify(row.key) + '=' + val; + }).join('\n'); + + if (_state !== 'hover' && str.length) { + return str + '\n'; + } + + return str; + } + + function textChanged() { + var newText = this.value.trim(); + var newTags = {}; + newText.split('\n').forEach(function (row) { + var m = row.match(/^\s*([^=]+)=(.*)$/); + + if (m !== null) { + var k = context.cleanTagKey(unstringify(m[1].trim())); + var v = context.cleanTagValue(unstringify(m[2].trim())); + newTags[k] = v; + } + }); + var tagDiff = utilTagDiff(_tags, newTags); + if (!tagDiff.length) return; + _pendingChange = _pendingChange || {}; + tagDiff.forEach(function (change) { + if (isReadOnly({ + key: change.key + })) return; // skip unchanged multiselection placeholders + + if (change.newVal === '*' && typeof change.oldVal !== 'string') return; + + if (change.type === '-') { + _pendingChange[change.key] = undefined; + } else if (change.type === '+') { + _pendingChange[change.key] = change.newVal || ''; + } + }); + + if (Object.keys(_pendingChange).length === 0) { + _pendingChange = null; + return; + } + + scheduleChange(); + } + + function pushMore(d3_event) { + // if pressing Tab on the last value field with content, add a blank row + if (d3_event.keyCode === 9 && !d3_event.shiftKey && section.selection().selectAll('.tag-list li:last-child input.value').node() === this && utilGetSetValue(select(this))) { + addTag(); + } + } + + function bindTypeahead(key, value) { + if (isReadOnly(key.datum())) return; + + if (Array.isArray(value.datum().value)) { + value.call(uiCombobox(context, 'tag-value').minItems(1).fetcher(function (value, callback) { + var keyString = utilGetSetValue(key); + if (!_tags[keyString]) return; + + var data = _tags[keyString].filter(Boolean).map(function (tagValue) { + return { + value: tagValue, + title: tagValue + }; + }); + + callback(data); + })); + return; + } + + var geometry = context.graph().geometry(_entityIDs[0]); + key.call(uiCombobox(context, 'tag-key').fetcher(function (value, callback) { + taginfo.keys({ + debounce: true, + geometry: geometry, + query: value + }, function (err, data) { + if (!err) { + var filtered = data.filter(function (d) { + return _tags[d.value] === undefined; + }); + callback(sort(value, filtered)); + } + }); + })); + value.call(uiCombobox(context, 'tag-value').fetcher(function (value, callback) { + taginfo.values({ + debounce: true, + key: utilGetSetValue(key), + geometry: geometry, + query: value + }, function (err, data) { + if (!err) callback(sort(value, data)); + }); + })); + + function sort(value, data) { + var sameletter = []; + var other = []; + + for (var i = 0; i < data.length; i++) { + if (data[i].value.substring(0, value.length) === value) { + sameletter.push(data[i]); + } else { + other.push(data[i]); + } + } + + return sameletter.concat(other); + } + } + + function unbind() { + var row = select(this); + row.selectAll('input.key').call(uiCombobox.off, context); + row.selectAll('input.value').call(uiCombobox.off, context); + } + + function keyChange(d3_event, d) { + if (select(this).attr('readonly')) return; + var kOld = d.key; // exit if we are currently about to delete this row anyway - #6366 + + if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) return; + var kNew = context.cleanTagKey(this.value.trim()); // allow no change if the key should be readonly + + if (isReadOnly({ + key: kNew + })) { + this.value = kOld; + return; + } + + if (kNew && kNew !== kOld && _tags[kNew] !== undefined) { + // new key is already in use, switch focus to the existing row + this.value = kOld; // reset the key + + section.selection().selectAll('.tag-list input.value').each(function (d) { + if (d.key === kNew) { + // send focus to that other value combo instead + var input = select(this).node(); + input.focus(); + input.select(); + } + }); + return; + } + + var row = this.parentNode.parentNode; + var inputVal = select(row).selectAll('input.value'); + var vNew = context.cleanTagValue(utilGetSetValue(inputVal)); + _pendingChange = _pendingChange || {}; + + if (kOld) { + _pendingChange[kOld] = undefined; + } + + _pendingChange[kNew] = vNew; // update the ordered key index so this row doesn't change position + + var existingKeyIndex = _orderedKeys.indexOf(kOld); + + if (existingKeyIndex !== -1) _orderedKeys[existingKeyIndex] = kNew; + d.key = kNew; // update datum to avoid exit/enter on tag update + + d.value = vNew; + this.value = kNew; + utilGetSetValue(inputVal, vNew); + scheduleChange(); + } + + function valueChange(d3_event, d) { + if (isReadOnly(d)) return; // exit if this is a multiselection and no value was entered + + if (typeof d.value !== 'string' && !this.value) return; // exit if we are currently about to delete this row anyway - #6366 + + if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) return; + _pendingChange = _pendingChange || {}; + _pendingChange[d.key] = context.cleanTagValue(this.value); + scheduleChange(); + } + + function removeTag(d3_event, d) { + if (isReadOnly(d)) return; + + if (d.key === '') { + // removing the blank row + _showBlank = false; + section.reRender(); + } else { + // remove the key from the ordered key index + _orderedKeys = _orderedKeys.filter(function (key) { + return key !== d.key; + }); + _pendingChange = _pendingChange || {}; + _pendingChange[d.key] = undefined; + scheduleChange(); + } + } + + function addTag() { + // Delay render in case this click is blurring an edited combo. + // Without the setTimeout, the `content` render would wipe out the pending tag change. + window.setTimeout(function () { + _showBlank = true; + section.reRender(); + section.selection().selectAll('.tag-list li:last-child input.key').node().focus(); + }, 20); + } + + function scheduleChange() { + // Cache IDs in case the editor is reloaded before the change event is called. - #6028 + var entityIDs = _entityIDs; // Delay change in case this change is blurring an edited combo. - #5878 + + window.setTimeout(function () { + if (!_pendingChange) return; + dispatch.call('change', this, entityIDs, _pendingChange); + _pendingChange = null; + }, 10); + } + + section.state = function (val) { + if (!arguments.length) return _state; + + if (_state !== val) { + _orderedKeys = []; + _state = val; + } + + return section; + }; + + section.presets = function (val) { + if (!arguments.length) return _presets; + _presets = val; + + if (_presets && _presets.length && _presets[0].isFallback()) { + section.disclosureExpanded(true); // don't collapse the disclosure if the mapper used the raw tag editor - #1881 + } else if (!_didInteract) { + section.disclosureExpanded(null); + } + + return section; + }; + + section.tags = function (val) { + if (!arguments.length) return _tags; + _tags = val; + return section; + }; + + section.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + + if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) { + _entityIDs = val; + _orderedKeys = []; + } + + return section; + }; // pass an array of regular expressions to test against the tag key + + + section.readOnlyTags = function (val) { + if (!arguments.length) return _readOnlyTags; + _readOnlyTags = val; + return section; + }; + + return utilRebind(section, dispatch, 'on'); + } + + function uiDataEditor(context) { + var dataHeader = uiDataHeader(); + var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context).expandedByDefault(true).readOnlyTags([/./]); + + var _datum; + + function dataEditor(selection) { + var header = selection.selectAll('.header').data([0]); + var headerEnter = header.enter().append('div').attr('class', 'header fillL'); + headerEnter.append('button').attr('class', 'close').on('click', function () { + context.enter(modeBrowse(context)); + }).call(svgIcon('#iD-icon-close')); + headerEnter.append('h3').html(_t.html('map_data.title')); + var body = selection.selectAll('.body').data([0]); + body = body.enter().append('div').attr('class', 'body').merge(body); + var editor = body.selectAll('.data-editor').data([0]); // enter/update + + editor.enter().append('div').attr('class', 'modal-section data-editor').merge(editor).call(dataHeader.datum(_datum)); + var rte = body.selectAll('.raw-tag-editor').data([0]); // enter/update + + rte.enter().append('div').attr('class', 'raw-tag-editor data-editor').merge(rte).call(rawTagEditor.tags(_datum && _datum.properties || {}).state('hover').render).selectAll('textarea.tag-text').attr('readonly', true).classed('readonly', true); + } + + dataEditor.datum = function (val) { + if (!arguments.length) return _datum; + _datum = val; + return this; + }; + + return dataEditor; + } + + var sexagesimal = {exports: {}}; + + sexagesimal.exports = element; + var pair_1 = sexagesimal.exports.pair = pair; + sexagesimal.exports.format = format; + sexagesimal.exports.formatPair = formatPair; + sexagesimal.exports.coordToDMS = coordToDMS; + + function element(input, dims) { + var result = search(input, dims); + return result === null ? null : result.val; + } + + function formatPair(input) { + return format(input.lat, 'lat') + ' ' + format(input.lon, 'lon'); + } // Is 0 North or South? + + + function format(input, dim) { + var dms = coordToDMS(input, dim); + return dms.whole + '° ' + (dms.minutes ? dms.minutes + '\' ' : '') + (dms.seconds ? dms.seconds + '" ' : '') + dms.dir; + } + + function coordToDMS(input, dim) { + var dirs = { + lat: ['N', 'S'], + lon: ['E', 'W'] + }[dim] || ''; + var dir = dirs[input >= 0 ? 0 : 1]; + var abs = Math.abs(input); + var whole = Math.floor(abs); + var fraction = abs - whole; + var fractionMinutes = fraction * 60; + var minutes = Math.floor(fractionMinutes); + var seconds = Math.floor((fractionMinutes - minutes) * 60); + return { + whole: whole, + minutes: minutes, + seconds: seconds, + dir: dir + }; + } + + function search(input, dims) { + if (!dims) dims = 'NSEW'; + if (typeof input !== 'string') return null; + input = input.toUpperCase(); + var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/; + var m = input.match(regex); + if (!m) return null; // no match + + var matched = m[0]; // extract dimension.. m[1] = leading, m[5] = trailing + + var dim; + + if (m[1] && m[5]) { + // if matched both.. + dim = m[1]; // keep leading + + matched = matched.slice(0, -1); // remove trailing dimension from match + } else { + dim = m[1] || m[5]; + } // if unrecognized dimension + + + if (dim && dims.indexOf(dim) === -1) return null; // extract DMS + + var deg = m[2] ? parseFloat(m[2]) : 0; + var min = m[3] ? parseFloat(m[3]) / 60 : 0; + var sec = m[4] ? parseFloat(m[4]) / 3600 : 0; + var sign = deg < 0 ? -1 : 1; + if (dim === 'S' || dim === 'W') sign *= -1; + return { + val: (Math.abs(deg) + min + sec) * sign, + dim: dim, + matched: matched, + remain: input.slice(matched.length) + }; + } + + function pair(input, dims) { + input = input.trim(); + var one = search(input, dims); + if (!one) return null; + input = one.remain.trim(); + var two = search(input, dims); + if (!two || two.remain) return null; + + if (one.dim) { + return swapdim(one.val, two.val, one.dim); + } else { + return [one.val, two.val]; + } + } + + function swapdim(a, b, dim) { + if (dim === 'N' || dim === 'S') return [a, b]; + if (dim === 'W' || dim === 'E') return [b, a]; + } + + function uiFeatureList(context) { + var _geocodeResults; + + function featureList(selection) { + var header = selection.append('div').attr('class', 'header fillL'); + header.append('h3').html(_t.html('inspector.feature_list')); + var searchWrap = selection.append('div').attr('class', 'search-header'); + searchWrap.call(svgIcon('#iD-icon-search', 'pre-text')); + var search = searchWrap.append('input').attr('placeholder', _t('inspector.search')).attr('type', 'search').call(utilNoAuto).on('keypress', keypress).on('keydown', keydown).on('input', inputevent); + var listWrap = selection.append('div').attr('class', 'inspector-body'); + var list = listWrap.append('div').attr('class', 'feature-list'); + context.on('exit.feature-list', clearSearch); + context.map().on('drawn.feature-list', mapDrawn); + context.keybinding().on(uiCmd('⌘F'), focusSearch); + + function focusSearch(d3_event) { + var mode = context.mode() && context.mode().id; + if (mode !== 'browse') return; + d3_event.preventDefault(); + search.node().focus(); + } + + function keydown(d3_event) { + if (d3_event.keyCode === 27) { + // escape + search.node().blur(); + } + } + + function keypress(d3_event) { + var q = search.property('value'), + items = list.selectAll('.feature-list-item'); + + if (d3_event.keyCode === 13 && // ↩ Return + q.length && items.size()) { + click(d3_event, items.datum()); + } + } + + function inputevent() { + _geocodeResults = undefined; + drawList(); + } + + function clearSearch() { + search.property('value', ''); + drawList(); + } + + function mapDrawn(e) { + if (e.full) { + drawList(); + } + } + + function features() { + var result = []; + var graph = context.graph(); + var visibleCenter = context.map().extent().center(); + var q = search.property('value').toLowerCase(); + if (!q) return result; + var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/); + + if (locationMatch) { + var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])]; + result.push({ + id: -1, + geometry: 'point', + type: _t('inspector.location'), + name: dmsCoordinatePair([loc[1], loc[0]]), + location: loc + }); + } // A location search takes priority over an ID search + + + var idMatch = !locationMatch && q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i); + + if (idMatch) { + var elemType = idMatch[1].charAt(0); + var elemId = idMatch[2]; + result.push({ + id: elemType + elemId, + geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation', + type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'), + name: elemId + }); + } + + var allEntities = graph.entities; + var localResults = []; + + for (var id in allEntities) { + var entity = allEntities[id]; + if (!entity) continue; + var name = utilDisplayName(entity) || ''; + if (name.toLowerCase().indexOf(q) < 0) continue; + var matched = _mainPresetIndex.match(entity, graph); + var type = matched && matched.name() || utilDisplayType(entity.id); + var extent = entity.extent(graph); + var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0; + localResults.push({ + id: entity.id, + entity: entity, + geometry: entity.geometry(graph), + type: type, + name: name, + distance: distance + }); + if (localResults.length > 100) break; + } + + localResults = localResults.sort(function byDistance(a, b) { + return a.distance - b.distance; + }); + result = result.concat(localResults); + + (_geocodeResults || []).forEach(function (d) { + if (d.osm_type && d.osm_id) { + // some results may be missing these - #1890 + // Make a temporary osmEntity so we can preset match + // and better localize the search result - #4725 + var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id); + var tags = {}; + tags[d["class"]] = d.type; + var attrs = { + id: id, + type: d.osm_type, + tags: tags + }; + + if (d.osm_type === 'way') { + // for ways, add some fake closed nodes + attrs.nodes = ['a', 'a']; // so that geometry area is possible + } + + var tempEntity = osmEntity(attrs); + var tempGraph = coreGraph([tempEntity]); + var matched = _mainPresetIndex.match(tempEntity, tempGraph); + var type = matched && matched.name() || utilDisplayType(id); + result.push({ + id: tempEntity.id, + geometry: tempEntity.geometry(tempGraph), + type: type, + name: d.display_name, + extent: new geoExtent([parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])], [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])]) + }); + } + }); + + if (q.match(/^[0-9]+$/)) { + // if query is just a number, possibly an OSM ID without a prefix + result.push({ + id: 'n' + q, + geometry: 'point', + type: _t('inspector.node'), + name: q + }); + result.push({ + id: 'w' + q, + geometry: 'line', + type: _t('inspector.way'), + name: q + }); + result.push({ + id: 'r' + q, + geometry: 'relation', + type: _t('inspector.relation'), + name: q + }); + } + + return result; + } + + function drawList() { + var value = search.property('value'); + var results = features(); + list.classed('filtered', value.length); + var resultsIndicator = list.selectAll('.no-results-item').data([0]).enter().append('button').property('disabled', true).attr('class', 'no-results-item').call(svgIcon('#iD-icon-alert', 'pre-text')); + resultsIndicator.append('span').attr('class', 'entity-name'); + list.selectAll('.no-results-item .entity-name').html(_t.html('geocoder.no_results_worldwide')); + + if (services.geocoder) { + 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')); + } + + list.selectAll('.no-results-item').style('display', value.length && !results.length ? 'block' : 'none'); + list.selectAll('.geocode-item').style('display', value && _geocodeResults === undefined ? 'block' : 'none'); + list.selectAll('.feature-list-item').data([-1]).remove(); + var items = list.selectAll('.feature-list-item').data(results, function (d) { + return d.id; + }); + var enter = items.enter().insert('button', '.geocode-item').attr('class', 'feature-list-item').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click); + var label = enter.append('div').attr('class', 'label'); + label.each(function (d) { + select(this).call(svgIcon('#iD-icon-' + d.geometry, 'pre-text')); + }); + label.append('span').attr('class', 'entity-type').html(function (d) { + return d.type; + }); + label.append('span').attr('class', 'entity-name').html(function (d) { + return d.name; + }); + enter.style('opacity', 0).transition().style('opacity', 1); + items.order(); + items.exit().remove(); + } + + function mouseover(d3_event, d) { + if (d.id === -1) return; + utilHighlightEntities([d.id], true, context); + } + + function mouseout(d3_event, d) { + if (d.id === -1) return; + utilHighlightEntities([d.id], false, context); + } + + function click(d3_event, d) { + d3_event.preventDefault(); + + if (d.location) { + context.map().centerZoomEase([d.location[1], d.location[0]], 19); + } else if (d.entity) { + utilHighlightEntities([d.id], false, context); + context.enter(modeSelect(context, [d.entity.id])); + context.map().zoomToEase(d.entity); + } else { + // download, zoom to, and select the entity with the given ID + context.zoomToEntity(d.id); + } + } + + function geocoderSearch() { + services.geocoder.search(search.property('value'), function (err, resp) { + _geocodeResults = resp || []; + drawList(); + }); + } + } + + return featureList; + } + + var $$1 = _export; + var getOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f; + var toLength$1 = toLength$q; + var notARegExp$1 = notARegexp; + var requireObjectCoercible$1 = requireObjectCoercible$e; + var correctIsRegExpLogic$1 = correctIsRegexpLogic; + + // eslint-disable-next-line es/no-string-prototype-startswith -- safe + var $startsWith = ''.startsWith; + var min$1 = Math.min; + + var CORRECT_IS_REGEXP_LOGIC$1 = correctIsRegExpLogic$1('startsWith'); + // https://github.com/zloirock/core-js/pull/702 + var MDN_POLYFILL_BUG$1 = !CORRECT_IS_REGEXP_LOGIC$1 && !!function () { + var descriptor = getOwnPropertyDescriptor$1(String.prototype, 'startsWith'); + return descriptor && !descriptor.writable; + }(); + + // `String.prototype.startsWith` method + // https://tc39.es/ecma262/#sec-string.prototype.startswith + $$1({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG$1 && !CORRECT_IS_REGEXP_LOGIC$1 }, { + startsWith: function startsWith(searchString /* , position = 0 */) { + var that = String(requireObjectCoercible$1(this)); + notARegExp$1(searchString); + var index = toLength$1(min$1(arguments.length > 1 ? arguments[1] : undefined, that.length)); + var search = String(searchString); + return $startsWith + ? $startsWith.call(that, search, index) + : that.slice(index, index + search.length) === search; + } + }); + + function uiSectionEntityIssues(context) { + // Does the user prefer to expand the active issue? Useful for viewing tag diff. + // Expand by default so first timers see it - #6408, #8143 + var preference = corePreferences('entity-issues.reference.expanded'); + + var _expanded = preference === null ? true : preference === 'true'; + + var _entityIDs = []; + var _issues = []; + + var _activeIssueID; + + var section = uiSection('entity-issues', context).shouldDisplay(function () { + return _issues.length > 0; + }).label(function () { + return _t('inspector.title_count', { + title: _t.html('issues.list_title'), + count: _issues.length + }); + }).disclosureContent(renderDisclosureContent); + context.validator().on('validated.entity_issues', function () { + // Refresh on validated events + reloadIssues(); + section.reRender(); + }).on('focusedIssue.entity_issues', function (issue) { + makeActiveIssue(issue.id); + }); + + function reloadIssues() { + _issues = context.validator().getSharedEntityIssues(_entityIDs, { + includeDisabledRules: true + }); + } + + function makeActiveIssue(issueID) { + _activeIssueID = issueID; + section.selection().selectAll('.issue-container').classed('active', function (d) { + return d.id === _activeIssueID; + }); + } + + function renderDisclosureContent(selection) { + selection.classed('grouped-items-area', true); + _activeIssueID = _issues.length > 0 ? _issues[0].id : null; + var containers = selection.selectAll('.issue-container').data(_issues, function (d) { + return d.key; + }); // Exit + + containers.exit().remove(); // Enter + + var containersEnter = containers.enter().append('div').attr('class', 'issue-container'); + var itemsEnter = containersEnter.append('div').attr('class', function (d) { + return 'issue severity-' + d.severity; + }).on('mouseover.highlight', function (d3_event, d) { + // don't hover-highlight the selected entity + var ids = d.entityIds.filter(function (e) { + return _entityIDs.indexOf(e) === -1; + }); + utilHighlightEntities(ids, true, context); + }).on('mouseout.highlight', function (d3_event, d) { + var ids = d.entityIds.filter(function (e) { + return _entityIDs.indexOf(e) === -1; + }); + utilHighlightEntities(ids, false, context); + }); + var labelsEnter = itemsEnter.append('div').attr('class', 'issue-label'); + var textEnter = labelsEnter.append('button').attr('class', 'issue-text').on('click', function (d3_event, d) { + makeActiveIssue(d.id); // expand only the clicked item + + var extent = d.extent(context.graph()); + + if (extent) { + var setZoom = Math.max(context.map().zoom(), 19); + context.map().unobscuredCenterZoomEase(extent.center(), setZoom); + } + }); + textEnter.each(function (d) { + var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error'); + select(this).call(svgIcon(iconName, 'issue-icon')); + }); + textEnter.append('span').attr('class', 'issue-message'); + var infoButton = labelsEnter.append('button').attr('class', 'issue-info-button').attr('title', _t('icons.information')).call(svgIcon('#iD-icon-inspect')); + infoButton.on('click', function (d3_event) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + this.blur(); // avoid keeping focus on the button - #4641 + + var container = select(this.parentNode.parentNode.parentNode); + var info = container.selectAll('.issue-info'); + var isExpanded = info.classed('expanded'); + _expanded = !isExpanded; + corePreferences('entity-issues.reference.expanded', _expanded); // update preference + + if (isExpanded) { + info.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () { + info.classed('expanded', false); + }); + } else { + info.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1').on('end', function () { + info.style('max-height', null); + }); + } + }); + itemsEnter.append('ul').attr('class', 'issue-fix-list'); + containersEnter.append('div').attr('class', 'issue-info' + (_expanded ? ' expanded' : '')).style('max-height', _expanded ? null : '0').style('opacity', _expanded ? '1' : '0').each(function (d) { + if (typeof d.reference === 'function') { + select(this).call(d.reference); + } else { + select(this).html(_t.html('inspector.no_documentation_key')); + } + }); // Update + + containers = containers.merge(containersEnter).classed('active', function (d) { + return d.id === _activeIssueID; + }); + containers.selectAll('.issue-message').html(function (d) { + return d.message(context); + }); // fixes + + var fixLists = containers.selectAll('.issue-fix-list'); + var fixes = fixLists.selectAll('.issue-fix-item').data(function (d) { + return d.fixes ? d.fixes(context) : []; + }, function (fix) { + return fix.id; + }); + fixes.exit().remove(); + var fixesEnter = fixes.enter().append('li').attr('class', 'issue-fix-item'); + var buttons = fixesEnter.append('button').on('click', function (d3_event, d) { + // not all fixes are actionable + if (select(this).attr('disabled') || !d.onClick) return; // Don't run another fix for this issue within a second of running one + // (Necessary for "Select a feature type" fix. Most fixes should only ever run once) + + if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) return; + d.issue.dateLastRanFix = new Date(); // remove hover-highlighting + + utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context); + new Promise(function (resolve, reject) { + d.onClick(context, resolve, reject); + + if (d.onClick.length <= 1) { + // if the fix doesn't take any completion parameters then consider it resolved + resolve(); + } + }).then(function () { + // revalidate whenever the fix has finished running successfully + context.validator().validate(); + }); + }).on('mouseover.highlight', function (d3_event, d) { + utilHighlightEntities(d.entityIds, true, context); + }).on('mouseout.highlight', function (d3_event, d) { + utilHighlightEntities(d.entityIds, false, context); + }); + buttons.each(function (d) { + var iconName = d.icon || 'iD-icon-wrench'; + + if (iconName.startsWith('maki')) { + iconName += '-15'; + } + + select(this).call(svgIcon('#' + iconName, 'fix-icon')); + }); + buttons.append('span').attr('class', 'fix-message').html(function (d) { + return d.title; + }); + fixesEnter.merge(fixes).selectAll('button').classed('actionable', function (d) { + return d.onClick; + }).attr('disabled', function (d) { + return d.onClick ? null : 'true'; + }).attr('title', function (d) { + if (d.disabledReason) { + return d.disabledReason; + } + + return null; + }); + } + + section.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + + if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) { + _entityIDs = val; + _activeIssueID = null; + reloadIssues(); + } + + return section; + }; + + return section; + } + + function uiPresetIcon() { + var _preset; + + var _geometry; + + var _sizeClass = 'medium'; + + function isSmall() { + return _sizeClass === 'small'; + } + + function presetIcon(selection) { + selection.each(render); + } + + function getIcon(p, geom) { + if (isSmall() && p.isFallback && p.isFallback()) return 'iD-icon-' + p.id; + if (p.icon) return p.icon; + if (geom === 'line') return 'iD-other-line'; + if (geom === 'vertex') return p.isFallback() ? '' : 'temaki-vertex'; + if (isSmall() && geom === 'point') return ''; + return 'maki-marker-stroked'; + } + + function renderPointBorder(container, drawPoint) { + var pointBorder = container.selectAll('.preset-icon-point-border').data(drawPoint ? [0] : []); + pointBorder.exit().remove(); + var pointBorderEnter = pointBorder.enter(); + var w = 40; + var h = 40; + 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'); + pointBorder = pointBorderEnter.merge(pointBorder); + } + + function renderCategoryBorder(container, category) { + var categoryBorder = container.selectAll('.preset-icon-category-border').data(category ? [0] : []); + categoryBorder.exit().remove(); + var categoryBorderEnter = categoryBorder.enter(); + var d = 60; + 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)); + ['fill', 'stroke'].forEach(function (klass) { + 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'); + }); + categoryBorder = categoryBorderEnter.merge(categoryBorder); + + if (category) { + var tagClasses = svgTagClasses().getClassesString(category.members.collection[0].addTags, ''); + categoryBorder.selectAll('path.stroke').attr('class', "area stroke ".concat(tagClasses)); + categoryBorder.selectAll('path.fill').attr('class', "area fill ".concat(tagClasses)); + } + } + + function renderCircleFill(container, drawVertex) { + var vertexFill = container.selectAll('.preset-icon-fill-vertex').data(drawVertex ? [0] : []); + vertexFill.exit().remove(); + var vertexFillEnter = vertexFill.enter(); + var w = 60; + var h = 60; + var d = 40; + 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); + vertexFill = vertexFillEnter.merge(vertexFill); + } + + function renderSquareFill(container, drawArea, tagClasses) { + var fill = container.selectAll('.preset-icon-fill-area').data(drawArea ? [0] : []); + fill.exit().remove(); + var fillEnter = fill.enter(); + var d = isSmall() ? 40 : 60; + var w = d; + var h = d; + var l = d * 2 / 3; + var c1 = (w - l) / 2; + var c2 = c1 + l; + fillEnter = fillEnter.append('svg').attr('class', 'preset-icon-fill preset-icon-fill-area').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h)); + ['fill', 'stroke'].forEach(function (klass) { + 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)); + }); + var rVertex = 2.5; + [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(function (point) { + fillEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', rVertex); + }); + + if (!isSmall()) { + var rMidpoint = 1.25; + [[c1, w / 2], [c2, w / 2], [h / 2, c1], [h / 2, c2]].forEach(function (point) { + fillEnter.append('circle').attr('class', 'midpoint').attr('cx', point[0]).attr('cy', point[1]).attr('r', rMidpoint); + }); + } + + fill = fillEnter.merge(fill); + fill.selectAll('path.stroke').attr('class', "area stroke ".concat(tagClasses)); + fill.selectAll('path.fill').attr('class', "area fill ".concat(tagClasses)); + } + + function renderLine(container, drawLine, tagClasses) { + var line = container.selectAll('.preset-icon-line').data(drawLine ? [0] : []); + line.exit().remove(); + var lineEnter = line.enter(); + var d = isSmall() ? 40 : 60; // draw the line parametrically + + var w = d; + var h = d; + var y = Math.round(d * 0.72); + var l = Math.round(d * 0.6); + var r = 2.5; + var x1 = (w - l) / 2; + var x2 = x1 + l; + lineEnter = lineEnter.append('svg').attr('class', 'preset-icon-line').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h)); + ['casing', 'stroke'].forEach(function (klass) { + lineEnter.append('path').attr('d', "M".concat(x1, " ").concat(y, " L").concat(x2, " ").concat(y)).attr('class', "line ".concat(klass)); + }); + [[x1 - 1, y], [x2 + 1, y]].forEach(function (point) { + lineEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r); + }); + line = lineEnter.merge(line); + line.selectAll('path.stroke').attr('class', "line stroke ".concat(tagClasses)); + line.selectAll('path.casing').attr('class', "line casing ".concat(tagClasses)); + } + + function renderRoute(container, drawRoute, p) { + var route = container.selectAll('.preset-icon-route').data(drawRoute ? [0] : []); + route.exit().remove(); + var routeEnter = route.enter(); + var d = isSmall() ? 40 : 60; // draw the route parametrically + + var w = d; + var h = d; + var y1 = Math.round(d * 0.80); + var y2 = Math.round(d * 0.68); + var l = Math.round(d * 0.6); + var r = 2; + var x1 = (w - l) / 2; + var x2 = x1 + l / 3; + var x3 = x2 + l / 3; + var x4 = x3 + l / 3; + routeEnter = routeEnter.append('svg').attr('class', 'preset-icon-route').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h)); + ['casing', 'stroke'].forEach(function (klass) { + routeEnter.append('path').attr('d', "M".concat(x1, " ").concat(y1, " L").concat(x2, " ").concat(y2)).attr('class', "segment0 line ".concat(klass)); + routeEnter.append('path').attr('d', "M".concat(x2, " ").concat(y2, " L").concat(x3, " ").concat(y1)).attr('class', "segment1 line ".concat(klass)); + routeEnter.append('path').attr('d', "M".concat(x3, " ").concat(y1, " L").concat(x4, " ").concat(y2)).attr('class', "segment2 line ".concat(klass)); + }); + [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(function (point) { + routeEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r); + }); + route = routeEnter.merge(route); + + if (drawRoute) { + var routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route; + var segmentPresetIDs = routeSegments[routeType]; + + for (var i in segmentPresetIDs) { + var segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]); + var segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, ''); + route.selectAll("path.stroke.segment".concat(i)).attr('class', "segment".concat(i, " line stroke ").concat(segmentTagClasses)); + route.selectAll("path.casing.segment".concat(i)).attr('class', "segment".concat(i, " line casing ").concat(segmentTagClasses)); + } + } + } + + function renderSvgIcon(container, picon, geom, isFramed, category, tagClasses) { + var isMaki = picon && /^maki-/.test(picon); + var isTemaki = picon && /^temaki-/.test(picon); + var isFa = picon && /^fa[srb]-/.test(picon); + var isiDIcon = picon && !(isMaki || isTemaki || isFa); + var icon = container.selectAll('.preset-icon').data(picon ? [0] : []); + icon.exit().remove(); + icon = icon.enter().append('div').attr('class', 'preset-icon').call(svgIcon('')).merge(icon); + icon.attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')).classed('category', category).classed('framed', isFramed).classed('preset-icon-iD', isiDIcon); + icon.selectAll('svg').attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line' ? '' : tagClasses)); + var suffix = ''; + + if (isMaki) { + suffix = isSmall() && geom === 'point' ? '-11' : '-15'; + } + + icon.selectAll('use').attr('href', '#' + picon + suffix); + } + + function renderImageIcon(container, imageURL) { + var imageIcon = container.selectAll('img.image-icon').data(imageURL ? [0] : []); + imageIcon.exit().remove(); + imageIcon = imageIcon.enter().append('img').attr('class', 'image-icon').on('load', function () { + return container.classed('showing-img', true); + }).on('error', function () { + return container.classed('showing-img', false); + }).merge(imageIcon); + imageIcon.attr('src', imageURL); + } // Route icons are drawn with a zigzag annotation underneath: + // o o + // / \ / + // o o + // This dataset defines the styles that are used to draw the zigzag segments. + + + var routeSegments = { + bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'], + bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'], + trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'], + detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'], + ferry: ['route/ferry', 'route/ferry', 'route/ferry'], + foot: ['highway/footway', 'highway/footway', 'highway/footway'], + hiking: ['highway/path', 'highway/path', 'highway/path'], + horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'], + light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'], + monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'], + mtb: ['highway/path', 'highway/track', 'highway/bridleway'], + pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'], + piste: ['piste/downhill', 'piste/hike', 'piste/nordic'], + power: ['power/line', 'power/line', 'power/line'], + road: ['highway/secondary', 'highway/primary', 'highway/trunk'], + subway: ['railway/subway', 'railway/subway', 'railway/subway'], + train: ['railway/rail', 'railway/rail', 'railway/rail'], + tram: ['railway/tram', 'railway/tram', 'railway/tram'], + waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream'] + }; + + function render() { + var p = _preset.apply(this, arguments); + + var geom = _geometry ? _geometry.apply(this, arguments) : null; + + if (geom === 'relation' && p.tags && (p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route] || p.tags.type === 'waterway')) { + geom = 'route'; + } + + var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true'; + var isFallback = isSmall() && p.isFallback && p.isFallback(); + var imageURL = showThirdPartyIcons === 'true' && p.imageURL; + var picon = getIcon(p, geom); + var isCategory = !p.setTags; + var drawPoint = picon && geom === 'point' && isSmall() && !isFallback; + var drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback); + var drawLine = picon && geom === 'line' && !isFallback && !isCategory; + var drawArea = picon && geom === 'area' && !isFallback && !isCategory; + var drawRoute = picon && geom === 'route'; + var isFramed = drawVertex || drawArea || drawLine || drawRoute || isCategory; + var tags = !isCategory ? p.setTags({}, geom) : {}; + + for (var k in tags) { + if (tags[k] === '*') { + tags[k] = 'yes'; + } + } + + var tagClasses = svgTagClasses().getClassesString(tags, ''); + var selection = select(this); + var container = selection.selectAll('.preset-icon-container').data([0]); + container = container.enter().append('div').attr('class', "preset-icon-container ".concat(_sizeClass)).merge(container); + container.classed('showing-img', !!imageURL).classed('fallback', isFallback); + renderCategoryBorder(container, isCategory && p); + renderPointBorder(container, drawPoint); + renderCircleFill(container, drawVertex); + renderSquareFill(container, drawArea, tagClasses); + renderLine(container, drawLine, tagClasses); + renderRoute(container, drawRoute, p); + renderSvgIcon(container, picon, geom, isFramed, isCategory, tagClasses); + renderImageIcon(container, imageURL); + } + + presetIcon.preset = function (val) { + if (!arguments.length) return _preset; + _preset = utilFunctor(val); + return presetIcon; + }; + + presetIcon.geometry = function (val) { + if (!arguments.length) return _geometry; + _geometry = utilFunctor(val); + return presetIcon; + }; + + presetIcon.sizeClass = function (val) { + if (!arguments.length) return _sizeClass; + _sizeClass = val; + return presetIcon; + }; + + return presetIcon; + } + + function uiSectionFeatureType(context) { + var dispatch = dispatch$8('choose'); + var _entityIDs = []; + var _presets = []; + + var _tagReference; + + var section = uiSection('feature-type', context).label(_t.html('inspector.feature_type')).disclosureContent(renderDisclosureContent); + + function renderDisclosureContent(selection) { + selection.classed('preset-list-item', true); + selection.classed('mixed-types', _presets.length > 1); + var presetButtonWrap = selection.selectAll('.preset-list-button-wrap').data([0]).enter().append('div').attr('class', 'preset-list-button-wrap'); + var presetButton = presetButtonWrap.append('button').attr('class', 'preset-list-button preset-reset').call(uiTooltip().title(_t.html('inspector.back_tooltip')).placement('bottom')); + presetButton.append('div').attr('class', 'preset-icon-container'); + presetButton.append('div').attr('class', 'label').append('div').attr('class', 'label-inner'); + presetButtonWrap.append('div').attr('class', 'accessory-buttons'); + var tagReferenceBodyWrap = selection.selectAll('.tag-reference-body-wrap').data([0]); + tagReferenceBodyWrap = tagReferenceBodyWrap.enter().append('div').attr('class', 'tag-reference-body-wrap').merge(tagReferenceBodyWrap); // update header + + if (_tagReference) { + selection.selectAll('.preset-list-button-wrap .accessory-buttons').style('display', _presets.length === 1 ? null : 'none').call(_tagReference.button); + tagReferenceBodyWrap.style('display', _presets.length === 1 ? null : 'none').call(_tagReference.body); + } + + selection.selectAll('.preset-reset').on('click', function () { + dispatch.call('choose', this, _presets); + }).on('pointerdown pointerup mousedown mouseup', function (d3_event) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + }); + var geometries = entityGeometries(); + selection.select('.preset-list-item button').call(uiPresetIcon().geometry(_presets.length === 1 ? geometries.length === 1 && geometries[0] : null).preset(_presets.length === 1 ? _presets[0] : _mainPresetIndex.item('point'))); + var names = _presets.length === 1 ? [_presets[0].nameLabel(), _presets[0].subtitleLabel()].filter(Boolean) : [_t('inspector.multiple_types')]; + var label = selection.select('.label-inner'); + var nameparts = label.selectAll('.namepart').data(names, function (d) { + return d; + }); + nameparts.exit().remove(); + nameparts.enter().append('div').attr('class', 'namepart').html(function (d) { + return d; + }); + } + + section.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + _entityIDs = val; + return section; + }; + + section.presets = function (val) { + if (!arguments.length) return _presets; // don't reload the same preset + + if (!utilArrayIdentical(val, _presets)) { + _presets = val; + + if (_presets.length === 1) { + _tagReference = uiTagReference(_presets[0].reference()).showing(false); + } + } + + return section; + }; + + function entityGeometries() { + var counts = {}; + + for (var i in _entityIDs) { + var geometry = context.graph().geometry(_entityIDs[i]); + if (!counts[geometry]) counts[geometry] = 0; + counts[geometry] += 1; + } + + return Object.keys(counts).sort(function (geom1, geom2) { + return counts[geom2] - counts[geom1]; + }); + } + + return utilRebind(section, dispatch, 'on'); + } + + // It borrows some code from uiHelp + + function uiFieldHelp(context, fieldName) { + var fieldHelp = {}; + + var _inspector = select(null); + + var _wrap = select(null); + + var _body = select(null); + + var fieldHelpKeys = { + restrictions: [['about', ['about', 'from_via_to', 'maxdist', 'maxvia']], ['inspecting', ['about', 'from_shadow', 'allow_shadow', 'restrict_shadow', 'only_shadow', 'restricted', 'only']], ['modifying', ['about', 'indicators', 'allow_turn', 'restrict_turn', 'only_turn']], ['tips', ['simple', 'simple_example', 'indirect', 'indirect_example', 'indirect_noedit']]] + }; + var fieldHelpHeadings = {}; + var replacements = { + distField: _t.html('restriction.controls.distance'), + viaField: _t.html('restriction.controls.via'), + fromShadow: icon('#iD-turn-shadow', 'inline shadow from'), + allowShadow: icon('#iD-turn-shadow', 'inline shadow allow'), + restrictShadow: icon('#iD-turn-shadow', 'inline shadow restrict'), + onlyShadow: icon('#iD-turn-shadow', 'inline shadow only'), + allowTurn: icon('#iD-turn-yes', 'inline turn'), + restrictTurn: icon('#iD-turn-no', 'inline turn'), + onlyTurn: icon('#iD-turn-only', 'inline turn') + }; // For each section, squash all the texts into a single markdown document + + var docs = fieldHelpKeys[fieldName].map(function (key) { + var helpkey = 'help.field.' + fieldName + '.' + key[0]; + var text = key[1].reduce(function (all, part) { + var subkey = helpkey + '.' + part; + var depth = fieldHelpHeadings[subkey]; // is this subkey a heading? + + var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s + + return all + hhh + _t.html(subkey, replacements) + '\n\n'; + }, ''); + return { + key: helpkey, + title: _t.html(helpkey + '.title'), + html: marked_1(text.trim()) + }; + }); + + function show() { + updatePosition(); + + _body.classed('hide', false).style('opacity', '0').transition().duration(200).style('opacity', '1'); + } + + function hide() { + _body.classed('hide', true).transition().duration(200).style('opacity', '0').on('end', function () { + _body.classed('hide', true); + }); + } + + function clickHelp(index) { + var d = docs[index]; + var tkeys = fieldHelpKeys[fieldName][index][1]; + + _body.selectAll('.field-help-nav-item').classed('active', function (d, i) { + return i === index; + }); + + var content = _body.selectAll('.field-help-content').html(d.html); // class the paragraphs so we can find and style them + + + content.selectAll('p').attr('class', function (d, i) { + return tkeys[i]; + }); // insert special content for certain help sections + + if (d.key === 'help.field.restrictions.inspecting') { + content.insert('img', 'p.from_shadow').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_inspect.gif')); + } else if (d.key === 'help.field.restrictions.modifying') { + content.insert('img', 'p.allow_turn').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_modify.gif')); + } + } + + fieldHelp.button = function (selection) { + if (_body.empty()) return; + var button = selection.selectAll('.field-help-button').data([0]); // enter/update + + button.enter().append('button').attr('class', 'field-help-button').call(svgIcon('#iD-icon-help')).merge(button).on('click', function (d3_event) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + + if (_body.classed('hide')) { + show(); + } else { + hide(); + } + }); + }; + + function updatePosition() { + var wrap = _wrap.node(); + + var inspector = _inspector.node(); + + var wRect = wrap.getBoundingClientRect(); + var iRect = inspector.getBoundingClientRect(); + + _body.style('top', wRect.top + inspector.scrollTop - iRect.top + 'px'); + } + + fieldHelp.body = function (selection) { + // This control expects the field to have a form-field-input-wrap div + _wrap = selection.selectAll('.form-field-input-wrap'); + if (_wrap.empty()) return; // absolute position relative to the inspector, so it "floats" above the fields + + _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body'); + if (_inspector.empty()) return; + _body = _inspector.selectAll('.field-help-body').data([0]); + + var enter = _body.enter().append('div').attr('class', 'field-help-body hide'); // initially hidden + + + var titleEnter = enter.append('div').attr('class', 'field-help-title cf'); + titleEnter.append('h2').attr('class', _mainLocalizer.textDirection() === 'rtl' ? 'fr' : 'fl').html(_t.html('help.field.' + fieldName + '.title')); + titleEnter.append('button').attr('class', 'fr close').on('click', function (d3_event) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + hide(); + }).call(svgIcon('#iD-icon-close')); + var navEnter = enter.append('div').attr('class', 'field-help-nav cf'); + var titles = docs.map(function (d) { + return d.title; + }); + navEnter.selectAll('.field-help-nav-item').data(titles).enter().append('div').attr('class', 'field-help-nav-item').html(function (d) { + return d; + }).on('click', function (d3_event, d) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + clickHelp(titles.indexOf(d)); + }); + enter.append('div').attr('class', 'field-help-content'); + _body = _body.merge(enter); + clickHelp(0); + }; + + return fieldHelp; + } + + function uiFieldCheck(field, context) { + var dispatch = dispatch$8('change'); + var options = field.options; + var values = []; + var texts = []; + + var _tags; + + var input = select(null); + var text = select(null); + var label = select(null); + var reverser = select(null); + + var _impliedYes; + + var _entityIDs = []; + + var _value; + + if (options) { + for (var i in options) { + var v = options[i]; + values.push(v === 'undefined' ? undefined : v); + texts.push(field.t.html('options.' + v, { + 'default': v + })); + } + } else { + values = [undefined, 'yes']; + texts = [_t.html('inspector.unknown'), _t.html('inspector.check.yes')]; + + if (field.type !== 'defaultCheck') { + values.push('no'); + texts.push(_t.html('inspector.check.no')); + } + } // Checks tags to see whether an undefined value is "Assumed to be Yes" + + + function checkImpliedYes() { + _impliedYes = field.id === 'oneway_yes'; // hack: pretend `oneway` field is a `oneway_yes` field + // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841 + + if (field.id === 'oneway') { + var entity = context.entity(_entityIDs[0]); + + for (var key in entity.tags) { + if (key in osmOneWayTags && entity.tags[key] in osmOneWayTags[key]) { + _impliedYes = true; + texts[0] = _t.html('_tagging.presets.fields.oneway_yes.options.undefined'); + break; + } + } + } + } + + function reverserHidden() { + if (!context.container().select('div.inspector-hover').empty()) return true; + return !(_value === 'yes' || _impliedYes && !_value); + } + + function reverserSetText(selection) { + var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]); + if (reverserHidden() || !entity) return selection; + var first = entity.first(); + var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last(); + var pseudoDirection = first < last; + var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward'; + selection.selectAll('.reverser-span').html(_t.html('inspector.check.reverser')).call(svgIcon(icon, 'inline')); + return selection; + } + + var check = function check(selection) { + checkImpliedYes(); + label = selection.selectAll('.form-field-input-wrap').data([0]); + var enter = label.enter().append('label').attr('class', 'form-field-input-wrap form-field-input-check'); + enter.append('input').property('indeterminate', field.type !== 'defaultCheck').attr('type', 'checkbox').attr('id', field.domId); + enter.append('span').html(texts[0]).attr('class', 'value'); + + if (field.type === 'onewayCheck') { + enter.append('button').attr('class', 'reverser' + (reverserHidden() ? ' hide' : '')).append('span').attr('class', 'reverser-span'); + } + + label = label.merge(enter); + input = label.selectAll('input'); + text = label.selectAll('span.value'); + input.on('click', function (d3_event) { + d3_event.stopPropagation(); + var t = {}; + + if (Array.isArray(_tags[field.key])) { + if (values.indexOf('yes') !== -1) { + t[field.key] = 'yes'; + } else { + t[field.key] = values[0]; + } + } else { + t[field.key] = values[(values.indexOf(_value) + 1) % values.length]; + } // Don't cycle through `alternating` or `reversible` states - #4970 + // (They are supported as translated strings, but should not toggle with clicks) + + + if (t[field.key] === 'reversible' || t[field.key] === 'alternating') { + t[field.key] = values[0]; + } + + dispatch.call('change', this, t); + }); + + if (field.type === 'onewayCheck') { + reverser = label.selectAll('.reverser'); + reverser.call(reverserSetText).on('click', function (d3_event) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + context.perform(function (graph) { + for (var i in _entityIDs) { + graph = actionReverse(_entityIDs[i])(graph); + } + + return graph; + }, _t('operations.reverse.annotation.line', { + n: 1 + })); // must manually revalidate since no 'change' event was called + + context.validator().validate(); + select(this).call(reverserSetText); + }); + } + }; + + check.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + _entityIDs = val; + return check; + }; + + check.tags = function (tags) { + _tags = tags; + + function isChecked(val) { + return val !== 'no' && val !== '' && val !== undefined && val !== null; + } + + function textFor(val) { + if (val === '') val = undefined; + var index = values.indexOf(val); + return index !== -1 ? texts[index] : '"' + val + '"'; + } + + checkImpliedYes(); + var isMixed = Array.isArray(tags[field.key]); + _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase(); + + if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) { + _value = 'yes'; + } + + input.property('indeterminate', isMixed || field.type !== 'defaultCheck' && !_value).property('checked', isChecked(_value)); + text.html(isMixed ? _t.html('inspector.multiple_values') : textFor(_value)).classed('mixed', isMixed); + label.classed('set', !!_value); + + if (field.type === 'onewayCheck') { + reverser.classed('hide', reverserHidden()).call(reverserSetText); + } + }; + + check.focus = function () { + input.node().focus(); + }; + + return utilRebind(check, dispatch, 'on'); + } + + function uiFieldCombo(field, context) { + var dispatch = dispatch$8('change'); + + var _isMulti = field.type === 'multiCombo' || field.type === 'manyCombo'; + + var _isNetwork = field.type === 'networkCombo'; + + var _isSemi = field.type === 'semiCombo'; + + var _optarray = field.options; + + var _showTagInfoSuggestions = field.type !== 'manyCombo' && field.autoSuggestions !== false; + + var _allowCustomValues = field.type !== 'manyCombo' && field.customValues !== false; + + var _snake_case = field.snake_case || field.snake_case === undefined; + + var _combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(field.caseSensitive).minItems(_isMulti || _isSemi ? 1 : 2); + + var _container = select(null); + + var _inputWrap = select(null); + + var _input = select(null); + + var _comboData = []; + var _multiData = []; + var _entityIDs = []; + + var _tags; + + var _countryCode; + + var _staticPlaceholder; // initialize deprecated tags array + + + var _dataDeprecated = []; + _mainFileFetcher.get('deprecated').then(function (d) { + _dataDeprecated = d; + })["catch"](function () { + /* ignore */ + }); // ensure multiCombo field.key ends with a ':' + + if (_isMulti && field.key && /[^:]$/.test(field.key)) { + field.key += ':'; + } + + function snake(s) { + return s.replace(/\s+/g, '_').toLowerCase(); + } + + function clean(s) { + return s.split(';').map(function (s) { + return s.trim(); + }).join(';'); + } // returns the tag value for a display value + // (for multiCombo, dval should be the key suffix, not the entire key) + + + function tagValue(dval) { + dval = clean(dval || ''); + + var found = _comboData.find(function (o) { + return o.key && clean(o.value) === dval; + }); + + if (found) return found.key; + + if (field.type === 'typeCombo' && !dval) { + return 'yes'; + } + + return (_snake_case ? snake(dval) : dval) || undefined; + } // returns the display value for a tag value + // (for multiCombo, tval should be the key suffix, not the entire key) + + + function displayValue(tval) { + tval = tval || ''; + + if (field.hasTextForStringId('options.' + tval)) { + return field.t('options.' + tval, { + "default": tval + }); + } + + if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') { + return ''; + } + + return tval; + } // Compute the difference between arrays of objects by `value` property + // + // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}]) + // > [{value:1}, {value:3}] + // + + + function objectDifference(a, b) { + return a.filter(function (d1) { + return !b.some(function (d2) { + return !d2.isMixed && d1.value === d2.value; + }); + }); + } + + function initCombo(selection, attachTo) { + if (!_allowCustomValues) { + selection.attr('readonly', 'readonly'); + } + + if (_showTagInfoSuggestions && services.taginfo) { + selection.call(_combobox.fetcher(setTaginfoValues), attachTo); + setTaginfoValues('', setPlaceholder); + } else { + selection.call(_combobox, attachTo); + setStaticValues(setPlaceholder); + } + } + + function setStaticValues(callback) { + if (!_optarray) return; + _comboData = _optarray.map(function (v) { + return { + key: v, + value: field.t('options.' + v, { + "default": v + }), + title: v, + display: field.t.html('options.' + v, { + "default": v + }), + klass: field.hasTextForStringId('options.' + v) ? '' : 'raw-option' + }; + }); + + _combobox.data(objectDifference(_comboData, _multiData)); + + if (callback) callback(_comboData); + } + + function setTaginfoValues(q, callback) { + var fn = _isMulti ? 'multikeys' : 'values'; + var query = (_isMulti ? field.key : '') + q; + var hasCountryPrefix = _isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0; + + if (hasCountryPrefix) { + query = _countryCode + ':'; + } + + var params = { + debounce: q !== '', + key: field.key, + query: query + }; + + if (_entityIDs.length) { + params.geometry = context.graph().geometry(_entityIDs[0]); + } + + services.taginfo[fn](params, function (err, data) { + if (err) return; + data = data.filter(function (d) { + if (field.type === 'typeCombo' && d.value === 'yes') { + // don't show the fallback value + return false; + } // don't show values with very low usage + + + return !d.count || d.count > 10; + }); + var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key]; + + if (deprecatedValues) { + // don't suggest deprecated tag values + data = data.filter(function (d) { + return deprecatedValues.indexOf(d.value) === -1; + }); + } + + if (hasCountryPrefix) { + data = data.filter(function (d) { + return d.value.toLowerCase().indexOf(_countryCode + ':') === 0; + }); + } // hide the caret if there are no suggestions + + + _container.classed('empty-combobox', data.length === 0); + + _comboData = data.map(function (d) { + var k = d.value; + if (_isMulti) k = k.replace(field.key, ''); + var label = field.t('options.' + k, { + "default": k + }); + return { + key: k, + value: label, + display: field.t.html('options.' + k, { + "default": k + }), + title: d.title || label, + klass: field.hasTextForStringId('options.' + k) ? '' : 'raw-option' + }; + }); + _comboData = objectDifference(_comboData, _multiData); + if (callback) callback(_comboData); + }); + } + + function setPlaceholder(values) { + if (_isMulti || _isSemi) { + _staticPlaceholder = field.placeholder() || _t('inspector.add'); + } else { + var vals = values.map(function (d) { + return d.value; + }).filter(function (s) { + return s.length < 20; + }); + var placeholders = vals.length > 1 ? vals : values.map(function (d) { + return d.key; + }); + _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', '); + } + + if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) { + _staticPlaceholder += '…'; + } + + var ph; + + if (!_isMulti && !_isSemi && _tags && Array.isArray(_tags[field.key])) { + ph = _t('inspector.multiple_values'); + } else { + ph = _staticPlaceholder; + } + + _container.selectAll('input').attr('placeholder', ph); + } + + function change() { + var t = {}; + var val; + + if (_isMulti || _isSemi) { + val = tagValue(utilGetSetValue(_input).replace(/,/g, ';')) || ''; + + _container.classed('active', false); + + utilGetSetValue(_input, ''); + var vals = val.split(';').filter(Boolean); + if (!vals.length) return; + + if (_isMulti) { + utilArrayUniq(vals).forEach(function (v) { + var key = (field.key || '') + v; + + if (_tags) { + // don't set a multicombo value to 'yes' if it already has a non-'no' value + // e.g. `language:de=main` + var old = _tags[key]; + if (typeof old === 'string' && old.toLowerCase() !== 'no') return; + } + + key = context.cleanTagKey(key); + field.keys.push(key); + t[key] = 'yes'; + }); + } else if (_isSemi) { + var arr = _multiData.map(function (d) { + return d.key; + }); + + arr = arr.concat(vals); + t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';')); + } + + window.setTimeout(function () { + _input.node().focus(); + }, 10); + } else { + var rawValue = utilGetSetValue(_input); // don't override multiple values with blank string + + if (!rawValue && Array.isArray(_tags[field.key])) return; + val = context.cleanTagValue(tagValue(rawValue)); + t[field.key] = val || undefined; + } + + dispatch.call('change', this, t); + } + + function removeMultikey(d3_event, d) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + var t = {}; + + if (_isMulti) { + t[d.key] = undefined; + } else if (_isSemi) { + var arr = _multiData.map(function (md) { + return md.key === d.key ? null : md.key; + }).filter(Boolean); + + arr = utilArrayUniq(arr); + t[field.key] = arr.length ? arr.join(';') : undefined; + } + + dispatch.call('change', this, t); + } + + function combo(selection) { + _container = selection.selectAll('.form-field-input-wrap').data([0]); + var type = _isMulti || _isSemi ? 'multicombo' : 'combo'; + _container = _container.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + type).merge(_container); + + if (_isMulti || _isSemi) { + _container = _container.selectAll('.chiplist').data([0]); + var listClass = 'chiplist'; // Use a separate line for each value in the Destinations and Via fields + // to mimic highway exit signs + + if (field.key === 'destination' || field.key === 'via') { + listClass += ' full-line-chips'; + } + + _container = _container.enter().append('ul').attr('class', listClass).on('click', function () { + window.setTimeout(function () { + _input.node().focus(); + }, 10); + }).merge(_container); + _inputWrap = _container.selectAll('.input-wrap').data([0]); + _inputWrap = _inputWrap.enter().append('li').attr('class', 'input-wrap').merge(_inputWrap); + _input = _inputWrap.selectAll('input').data([0]); + } else { + _input = _container.selectAll('input').data([0]); + } + + _input = _input.enter().append('input').attr('type', 'text').attr('id', field.domId).call(utilNoAuto).call(initCombo, selection).merge(_input); + + if (_isNetwork) { + var extent = combinedEntityExtent(); + var countryCode = extent && iso1A2Code(extent.center()); + _countryCode = countryCode && countryCode.toLowerCase(); + } + + _input.on('change', change).on('blur', change); + + _input.on('keydown.field', function (d3_event) { + switch (d3_event.keyCode) { + case 13: + // ↩ Return + _input.node().blur(); // blurring also enters the value + + + d3_event.stopPropagation(); + break; + } + }); + + if (_isMulti || _isSemi) { + _combobox.on('accept', function () { + _input.node().blur(); + + _input.node().focus(); + }); + + _input.on('focus', function () { + _container.classed('active', true); + }); + } + } + + combo.tags = function (tags) { + _tags = tags; + + if (_isMulti || _isSemi) { + _multiData = []; + var maxLength; + + if (_isMulti) { + // Build _multiData array containing keys already set.. + for (var k in tags) { + if (field.key && k.indexOf(field.key) !== 0) continue; + if (!field.key && field.keys.indexOf(k) === -1) continue; + var v = tags[k]; + if (!v || typeof v === 'string' && v.toLowerCase() === 'no') continue; + var suffix = field.key ? k.substr(field.key.length) : k; + + _multiData.push({ + key: k, + value: displayValue(suffix), + isMixed: Array.isArray(v) + }); + } + + if (field.key) { + // Set keys for form-field modified (needed for undo and reset buttons).. + field.keys = _multiData.map(function (d) { + return d.key; + }); // limit the input length so it fits after prepending the key prefix + + maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key); + } else { + maxLength = context.maxCharsForTagKey(); + } + } else if (_isSemi) { + var allValues = []; + var commonValues; + + if (Array.isArray(tags[field.key])) { + tags[field.key].forEach(function (tagVal) { + var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean); + allValues = allValues.concat(thisVals); + + if (!commonValues) { + commonValues = thisVals; + } else { + commonValues = commonValues.filter(function (value) { + return thisVals.includes(value); + }); + } + }); + allValues = utilArrayUniq(allValues).filter(Boolean); + } else { + allValues = utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean); + commonValues = allValues; + } + + _multiData = allValues.map(function (v) { + return { + key: v, + value: displayValue(v), + isMixed: !commonValues.includes(v) + }; + }); + var currLength = utilUnicodeCharsCount(commonValues.join(';')); // limit the input length to the remaining available characters + + maxLength = context.maxCharsForTagValue() - currLength; + + if (currLength > 0) { + // account for the separator if a new value will be appended to existing + maxLength -= 1; + } + } // a negative maxlength doesn't make sense + + + maxLength = Math.max(0, maxLength); + var allowDragAndDrop = _isSemi // only semiCombo values are ordered + && !Array.isArray(tags[field.key]); // Exclude existing multikeys from combo options.. + + var available = objectDifference(_comboData, _multiData); + + _combobox.data(available); // Hide 'Add' button if this field uses fixed set of + // options and they're all currently used, + // or if the field is already at its character limit + + + var hideAdd = !_allowCustomValues && !available.length || maxLength <= 0; + + _container.selectAll('.chiplist .input-wrap').style('display', hideAdd ? 'none' : null); // Render chips + + + var chips = _container.selectAll('.chip').data(_multiData); + + chips.exit().remove(); + var enter = chips.enter().insert('li', '.input-wrap').attr('class', 'chip'); + enter.append('span'); + enter.append('a'); + chips = chips.merge(enter).order().classed('raw-value', function (d) { + var k = d.key; + if (_isMulti) k = k.replace(field.key, ''); + return !field.hasTextForStringId('options.' + k); + }).classed('draggable', allowDragAndDrop).classed('mixed', function (d) { + return d.isMixed; + }).attr('title', function (d) { + return d.isMixed ? _t('inspector.unshared_value_tooltip') : null; + }); + + if (allowDragAndDrop) { + registerDragAndDrop(chips); + } + + chips.select('span').html(function (d) { + return d.value; + }); + chips.select('a').attr('href', '#').on('click', removeMultikey).attr('class', 'remove').html('×'); + } else { + var isMixed = Array.isArray(tags[field.key]); + var mixedValues = isMixed && tags[field.key].map(function (val) { + return displayValue(val); + }).filter(Boolean); + var showsValue = !isMixed && tags[field.key] && !(field.type === 'typeCombo' && tags[field.key] === 'yes'); + var isRawValue = showsValue && !field.hasTextForStringId('options.' + tags[field.key]); + var isKnownValue = showsValue && !isRawValue; + var isReadOnly = !_allowCustomValues || isKnownValue; + 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) { + if (isReadOnly && isKnownValue && (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + var t = {}; + t[field.key] = undefined; + dispatch.call('change', this, t); + } + }); + } + }; + + function registerDragAndDrop(selection) { + // allow drag and drop re-ordering of chips + var dragOrigin, targetIndex; + selection.call(d3_drag().on('start', function (d3_event) { + dragOrigin = { + x: d3_event.x, + y: d3_event.y + }; + targetIndex = null; + }).on('drag', function (d3_event) { + var x = d3_event.x - dragOrigin.x, + y = d3_event.y - dragOrigin.y; + if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold + Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return; + var index = selection.nodes().indexOf(this); + select(this).classed('dragging', true); + targetIndex = null; + var targetIndexOffsetTop = null; + var draggedTagWidth = select(this).node().offsetWidth; + + if (field.key === 'destination' || field.key === 'via') { + // meaning tags are full width + _container.selectAll('.chip').style('transform', function (d2, index2) { + var node = select(this).node(); + + if (index === index2) { + return 'translate(' + x + 'px, ' + y + 'px)'; // move the dragged tag up the order + } else if (index2 > index && d3_event.y > node.offsetTop) { + if (targetIndex === null || index2 > targetIndex) { + targetIndex = index2; + } + + return 'translateY(-100%)'; // move the dragged tag down the order + } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) { + if (targetIndex === null || index2 < targetIndex) { + targetIndex = index2; + } + + return 'translateY(100%)'; + } + + return null; + }); + } else { + _container.selectAll('.chip').each(function (d2, index2) { + var node = select(this).node(); // check the cursor is in the bounding box + + if (index !== index2 && 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) { + targetIndex = index2; + targetIndexOffsetTop = node.offsetTop; + } + }).style('transform', function (d2, index2) { + var node = select(this).node(); + + if (index === index2) { + return 'translate(' + x + 'px, ' + y + 'px)'; + } // only translate tags in the same row + + + if (node.offsetTop === targetIndexOffsetTop) { + if (index2 < index && index2 >= targetIndex) { + return 'translateX(' + draggedTagWidth + 'px)'; + } else if (index2 > index && index2 <= targetIndex) { + return 'translateX(-' + draggedTagWidth + 'px)'; + } + } + + return null; + }); + } + }).on('end', function () { + if (!select(this).classed('dragging')) { + return; + } + + var index = selection.nodes().indexOf(this); + select(this).classed('dragging', false); + + _container.selectAll('.chip').style('transform', null); + + if (typeof targetIndex === 'number') { + var element = _multiData[index]; + + _multiData.splice(index, 1); + + _multiData.splice(targetIndex, 0, element); + + var t = {}; + + if (_multiData.length) { + t[field.key] = _multiData.map(function (element) { + return element.key; + }).join(';'); + } else { + t[field.key] = undefined; + } + + dispatch.call('change', this, t); + } + + dragOrigin = undefined; + targetIndex = undefined; + })); + } + + combo.focus = function () { + _input.node().focus(); + }; + + combo.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + _entityIDs = val; + return combo; + }; + + function combinedEntityExtent() { + return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph()); + } + + return utilRebind(combo, dispatch, 'on'); + } + + function uiFieldText(field, context) { + var dispatch = dispatch$8('change'); + var input = select(null); + var outlinkButton = select(null); + var _entityIDs = []; + + var _tags; + + var _phoneFormats = {}; + + if (field.type === 'tel') { + _mainFileFetcher.get('phone_formats').then(function (d) { + _phoneFormats = d; + updatePhonePlaceholder(); + })["catch"](function () { + /* ignore */ + }); + } + + function calcLocked() { + // Protect certain fields that have a companion `*:wikidata` value + var isLocked = (field.id === 'brand' || field.id === 'network' || field.id === 'operator' || field.id === 'flag') && _entityIDs.length && _entityIDs.some(function (entityID) { + var entity = context.graph().hasEntity(entityID); + if (!entity) return false; // Features linked to Wikidata are likely important and should be protected + + if (entity.tags.wikidata) return true; + var preset = _mainPresetIndex.match(entity, context.graph()); + var isSuggestion = preset && preset.suggestion; // Lock the field if there is a value and a companion `*:wikidata` value + + var which = field.id; // 'brand', 'network', 'operator', 'flag' + + return isSuggestion && !!entity.tags[which] && !!entity.tags[which + ':wikidata']; + }); + + field.locked(isLocked); + } + + function i(selection) { + calcLocked(); + var isLocked = field.locked(); + var wrap = selection.selectAll('.form-field-input-wrap').data([0]); + wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap); + input = wrap.selectAll('input').data([0]); + input = input.enter().append('input').attr('type', field.type === 'identifier' ? 'text' : field.type).attr('id', field.domId).classed(field.type, true).call(utilNoAuto).merge(input); + input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change()); + + if (field.type === 'tel') { + updatePhonePlaceholder(); + } else if (field.type === 'number') { + var rtl = _mainLocalizer.textDirection() === 'rtl'; + input.attr('type', 'text'); + var inc = field.increment; + var buttons = wrap.selectAll('.increment, .decrement').data(rtl ? [inc, -inc] : [-inc, inc]); + buttons.enter().append('button').attr('class', function (d) { + var which = d > 0 ? 'increment' : 'decrement'; + return 'form-field-button ' + which; + }).merge(buttons).on('click', function (d3_event, d) { + d3_event.preventDefault(); + var raw_vals = input.node().value || '0'; + var vals = raw_vals.split(';'); + vals = vals.map(function (v) { + var num = parseFloat(v.trim(), 10); + return isFinite(num) ? clamped(num + d) : v.trim(); + }); + input.node().value = vals.join(';'); + change()(); + }); + } else if (field.type === 'identifier' && field.urlFormat && field.pattern) { + input.attr('type', 'text'); + outlinkButton = wrap.selectAll('.foreign-id-permalink').data([0]); + outlinkButton.enter().append('button').call(svgIcon('#iD-icon-out-link')).attr('class', 'form-field-button foreign-id-permalink').attr('title', function () { + var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat); + + if (domainResults.length >= 2 && domainResults[1]) { + var domain = domainResults[1]; + return _t('icons.view_on', { + domain: domain + }); + } + + return ''; + }).on('click', function (d3_event) { + d3_event.preventDefault(); + var value = validIdentifierValueForLink(); + + if (value) { + var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value)); + window.open(url, '_blank'); + } + }).merge(outlinkButton); + } else if (field.type === 'url') { + input.attr('type', 'text'); + outlinkButton = wrap.selectAll('.foreign-id-permalink').data([0]); + outlinkButton.enter().append('button').call(svgIcon('#iD-icon-out-link')).attr('class', 'form-field-button foreign-id-permalink').attr('title', function () { + return _t('icons.visit_website'); + }).on('click', function (d3_event) { + d3_event.preventDefault(); + var value = validIdentifierValueForLink(); + if (value) window.open(value, '_blank'); + }).merge(outlinkButton); + } + } + + function updatePhonePlaceholder() { + if (input.empty() || !Object.keys(_phoneFormats).length) return; + var extent = combinedEntityExtent(); + var countryCode = extent && iso1A2Code(extent.center()); + + var format = countryCode && _phoneFormats[countryCode.toLowerCase()]; + + if (format) input.attr('placeholder', format); + } + + function validIdentifierValueForLink() { + var value = utilGetSetValue(input).trim().split(';')[0]; + if (field.type === 'url' && value) return value; + + if (field.type === 'identifier' && field.pattern) { + return value && value.match(new RegExp(field.pattern)); + } + + return null; + } // clamp number to min/max + + + function clamped(num) { + if (field.minValue !== undefined) { + num = Math.max(num, field.minValue); + } + + if (field.maxValue !== undefined) { + num = Math.min(num, field.maxValue); + } + + return num; + } + + function change(onInput) { + return function () { + var t = {}; + var val = utilGetSetValue(input); + if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string + + if (!val && Array.isArray(_tags[field.key])) return; + + if (!onInput) { + if (field.type === 'number' && val) { + var vals = val.split(';'); + vals = vals.map(function (v) { + var num = parseFloat(v.trim(), 10); + return isFinite(num) ? clamped(num) : v.trim(); + }); + val = vals.join(';'); + } + + utilGetSetValue(input, val); + } + + t[field.key] = val || undefined; + dispatch.call('change', this, t, onInput); + }; + } + + i.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + _entityIDs = val; + return i; + }; + + i.tags = function (tags) { + _tags = tags; + var isMixed = Array.isArray(tags[field.key]); + utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '').attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined).attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder() || _t('inspector.unknown')).classed('mixed', isMixed); + + if (outlinkButton && !outlinkButton.empty()) { + var disabled = !validIdentifierValueForLink(); + outlinkButton.classed('disabled', disabled); + } + }; + + i.focus = function () { + var node = input.node(); + if (node) node.focus(); + }; + + function combinedEntityExtent() { + return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph()); + } + + return utilRebind(i, dispatch, 'on'); + } + + function uiFieldAccess(field, context) { + var dispatch = dispatch$8('change'); + var items = select(null); + + var _tags; + + function access(selection) { + var wrap = selection.selectAll('.form-field-input-wrap').data([0]); + wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap); + var list = wrap.selectAll('ul').data([0]); + list = list.enter().append('ul').attr('class', 'rows').merge(list); + items = list.selectAll('li').data(field.keys); // Enter + + var enter = items.enter().append('li').attr('class', function (d) { + return 'labeled-input preset-access-' + d; + }); + enter.append('span').attr('class', 'label preset-label-access').attr('for', function (d) { + return 'preset-input-access-' + d; + }).html(function (d) { + return field.t.html('types.' + d); + }); + enter.append('div').attr('class', 'preset-input-access-wrap').append('input').attr('type', 'text').attr('class', function (d) { + return 'preset-input-access preset-input-access-' + d; + }).call(utilNoAuto).each(function (d) { + select(this).call(uiCombobox(context, 'access-' + d).data(access.options(d))); + }); // Update + + items = items.merge(enter); + wrap.selectAll('.preset-input-access').on('change', change).on('blur', change); + } + + function change(d3_event, d) { + var tag = {}; + var value = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string + + if (!value && typeof _tags[d] !== 'string') return; + tag[d] = value || undefined; + dispatch.call('change', this, tag); + } + + access.options = function (type) { + var options = ['no', 'permissive', 'private', 'permit', 'destination']; + + if (type !== 'access') { + options.unshift('yes'); + options.push('designated'); + + if (type === 'bicycle') { + options.push('dismount'); + } + } + + return options.map(function (option) { + return { + title: field.t('options.' + option + '.description'), + value: option + }; + }); + }; + + var placeholdersByHighway = { + footway: { + foot: 'designated', + motor_vehicle: 'no' + }, + steps: { + foot: 'yes', + motor_vehicle: 'no', + bicycle: 'no', + horse: 'no' + }, + pedestrian: { + foot: 'yes', + motor_vehicle: 'no' + }, + cycleway: { + motor_vehicle: 'no', + bicycle: 'designated' + }, + bridleway: { + motor_vehicle: 'no', + horse: 'designated' + }, + path: { + foot: 'yes', + motor_vehicle: 'no', + bicycle: 'yes', + horse: 'yes' + }, + motorway: { + foot: 'no', + motor_vehicle: 'yes', + bicycle: 'no', + horse: 'no' + }, + trunk: { + motor_vehicle: 'yes' + }, + primary: { + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' + }, + secondary: { + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' + }, + tertiary: { + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' + }, + residential: { + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' + }, + unclassified: { + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' + }, + service: { + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' + }, + motorway_link: { + foot: 'no', + motor_vehicle: 'yes', + bicycle: 'no', + horse: 'no' + }, + trunk_link: { + motor_vehicle: 'yes' + }, + primary_link: { + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' + }, + secondary_link: { + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' + }, + tertiary_link: { + foot: 'yes', + motor_vehicle: 'yes', + bicycle: 'yes', + horse: 'yes' + } + }; + + access.tags = function (tags) { + _tags = tags; + utilGetSetValue(items.selectAll('.preset-input-access'), function (d) { + return typeof tags[d] === 'string' ? tags[d] : ''; + }).classed('mixed', function (d) { + return tags[d] && Array.isArray(tags[d]); + }).attr('title', function (d) { + return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n'); + }).attr('placeholder', function (d) { + if (tags[d] && Array.isArray(tags[d])) { + return _t('inspector.multiple_values'); + } + + if (d === 'access') { + return 'yes'; + } + + if (tags.access && typeof tags.access === 'string') { + return tags.access; + } + + if (tags.highway) { + if (typeof tags.highway === 'string') { + if (placeholdersByHighway[tags.highway] && placeholdersByHighway[tags.highway][d]) { + return placeholdersByHighway[tags.highway][d]; + } + } else { + var impliedAccesses = tags.highway.filter(Boolean).map(function (highwayVal) { + return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d]; + }).filter(Boolean); + + if (impliedAccesses.length === tags.highway.length && new Set(impliedAccesses).size === 1) { + // if all the highway values have the same implied access for this type then use that + return impliedAccesses[0]; + } + } + } + + return field.placeholder(); + }); + }; + + access.focus = function () { + items.selectAll('.preset-input-access').node().focus(); + }; + + return utilRebind(access, dispatch, 'on'); + } + + function uiFieldAddress(field, context) { + var dispatch = dispatch$8('change'); + + var _selection = select(null); + + var _wrap = select(null); + + var addrField = _mainPresetIndex.field('address'); // needed for placeholder strings + + var _entityIDs = []; + + var _tags; + + var _countryCode; + + var _addressFormats = [{ + format: [['housenumber', 'street'], ['city', 'postcode']] + }]; + _mainFileFetcher.get('address_formats').then(function (d) { + _addressFormats = d; + + if (!_selection.empty()) { + _selection.call(address); + } + })["catch"](function () { + /* ignore */ + }); + + function getNearStreets() { + var extent = combinedEntityExtent(); + var l = extent.center(); + var box = geoExtent(l).padByMeters(200); + var streets = context.history().intersects(box).filter(isAddressable).map(function (d) { + var loc = context.projection([(extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2]); + var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection); + return { + title: d.tags.name, + value: d.tags.name, + dist: choice.distance + }; + }).sort(function (a, b) { + return a.dist - b.dist; + }); + return utilArrayUniqBy(streets, 'value'); + + function isAddressable(d) { + return d.tags.highway && d.tags.name && d.type === 'way'; + } + } + + function getNearCities() { + var extent = combinedEntityExtent(); + var l = extent.center(); + var box = geoExtent(l).padByMeters(200); + var cities = context.history().intersects(box).filter(isAddressable).map(function (d) { + return { + title: d.tags['addr:city'] || d.tags.name, + value: d.tags['addr:city'] || d.tags.name, + dist: geoSphericalDistance(d.extent(context.graph()).center(), l) + }; + }).sort(function (a, b) { + return a.dist - b.dist; + }); + return utilArrayUniqBy(cities, 'value'); + + function isAddressable(d) { + if (d.tags.name) { + if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative') return true; + if (d.tags.border_type === 'city') return true; + if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village') return true; + } + + if (d.tags['addr:city']) return true; + return false; + } + } + + function getNearValues(key) { + var extent = combinedEntityExtent(); + var l = extent.center(); + var box = geoExtent(l).padByMeters(200); + var results = context.history().intersects(box).filter(function hasTag(d) { + return _entityIDs.indexOf(d.id) === -1 && d.tags[key]; + }).map(function (d) { + return { + title: d.tags[key], + value: d.tags[key], + dist: geoSphericalDistance(d.extent(context.graph()).center(), l) + }; + }).sort(function (a, b) { + return a.dist - b.dist; + }); + return utilArrayUniqBy(results, 'value'); + } + + function updateForCountryCode() { + if (!_countryCode) return; + var addressFormat; + + for (var i = 0; i < _addressFormats.length; i++) { + var format = _addressFormats[i]; + + if (!format.countryCodes) { + addressFormat = format; // choose the default format, keep going + } else if (format.countryCodes.indexOf(_countryCode) !== -1) { + addressFormat = format; // choose the country format, stop here + + break; + } + } + + var dropdowns = addressFormat.dropdowns || ['city', 'county', 'country', 'district', 'hamlet', 'neighbourhood', 'place', 'postcode', 'province', 'quarter', 'state', 'street', 'subdistrict', 'suburb']; + var widths = addressFormat.widths || { + housenumber: 1 / 3, + street: 2 / 3, + city: 2 / 3, + state: 1 / 4, + postcode: 1 / 3 + }; + + function row(r) { + // Normalize widths. + var total = r.reduce(function (sum, key) { + return sum + (widths[key] || 0.5); + }, 0); + return r.map(function (key) { + return { + id: key, + width: (widths[key] || 0.5) / total + }; + }); + } + + var rows = _wrap.selectAll('.addr-row').data(addressFormat.format, function (d) { + return d.toString(); + }); + + rows.exit().remove(); + rows.enter().append('div').attr('class', 'addr-row').selectAll('input').data(row).enter().append('input').property('type', 'text').call(updatePlaceholder).attr('class', function (d) { + return 'addr-' + d.id; + }).call(utilNoAuto).each(addDropdown).style('width', function (d) { + return d.width * 100 + '%'; + }); + + function addDropdown(d) { + if (dropdowns.indexOf(d.id) === -1) return; // not a dropdown + + var nearValues = d.id === 'street' ? getNearStreets : d.id === 'city' ? getNearCities : getNearValues; + select(this).call(uiCombobox(context, 'address-' + d.id).minItems(1).caseSensitive(true).fetcher(function (value, callback) { + callback(nearValues('addr:' + d.id)); + })); + } + + _wrap.selectAll('input').on('blur', change()).on('change', change()); + + _wrap.selectAll('input:not(.combobox-input)').on('input', change(true)); + + if (_tags) updateTags(_tags); + } + + function address(selection) { + _selection = selection; + _wrap = selection.selectAll('.form-field-input-wrap').data([0]); + _wrap = _wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(_wrap); + var extent = combinedEntityExtent(); + + if (extent) { + var countryCode; + + if (context.inIntro()) { + // localize the address format for the walkthrough + countryCode = _t('intro.graph.countrycode'); + } else { + var center = extent.center(); + countryCode = iso1A2Code(center); + } + + if (countryCode) { + _countryCode = countryCode.toLowerCase(); + updateForCountryCode(); + } + } + } + + function change(onInput) { + return function () { + var tags = {}; + + _wrap.selectAll('input').each(function (subfield) { + var key = field.key + ':' + subfield.id; + var value = this.value; + if (!onInput) value = context.cleanTagValue(value); // don't override multiple values with blank string + + if (Array.isArray(_tags[key]) && !value) return; + tags[key] = value || undefined; + }); + + dispatch.call('change', this, tags, onInput); + }; + } + + function updatePlaceholder(inputSelection) { + return inputSelection.attr('placeholder', function (subfield) { + if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) { + return _t('inspector.multiple_values'); + } + + if (_countryCode) { + var localkey = subfield.id + '!' + _countryCode; + var tkey = addrField.hasTextForStringId('placeholders.' + localkey) ? localkey : subfield.id; + return addrField.t('placeholders.' + tkey); + } + }); + } + + function updateTags(tags) { + utilGetSetValue(_wrap.selectAll('input'), function (subfield) { + var val = tags[field.key + ':' + subfield.id]; + return typeof val === 'string' ? val : ''; + }).attr('title', function (subfield) { + var val = tags[field.key + ':' + subfield.id]; + return val && Array.isArray(val) && val.filter(Boolean).join('\n'); + }).classed('mixed', function (subfield) { + return Array.isArray(tags[field.key + ':' + subfield.id]); + }).call(updatePlaceholder); + } + + function combinedEntityExtent() { + return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph()); + } + + address.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + _entityIDs = val; + return address; + }; + + address.tags = function (tags) { + _tags = tags; + updateTags(tags); + }; + + address.focus = function () { + var node = _wrap.selectAll('input').node(); + + if (node) node.focus(); + }; + + return utilRebind(address, dispatch, 'on'); + } + + function uiFieldCycleway(field, context) { + var dispatch = dispatch$8('change'); + var items = select(null); + var wrap = select(null); + + var _tags; + + function cycleway(selection) { + function stripcolon(s) { + return s.replace(':', ''); + } + + wrap = selection.selectAll('.form-field-input-wrap').data([0]); + wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap); + var div = wrap.selectAll('ul').data([0]); + div = div.enter().append('ul').attr('class', 'rows').merge(div); + var keys = ['cycleway:left', 'cycleway:right']; + items = div.selectAll('li').data(keys); + var enter = items.enter().append('li').attr('class', function (d) { + return 'labeled-input preset-cycleway-' + stripcolon(d); + }); + enter.append('span').attr('class', 'label preset-label-cycleway').attr('for', function (d) { + return 'preset-input-cycleway-' + stripcolon(d); + }).html(function (d) { + return field.t.html('types.' + d); + }); + enter.append('div').attr('class', 'preset-input-cycleway-wrap').append('input').attr('type', 'text').attr('class', function (d) { + return 'preset-input-cycleway preset-input-' + stripcolon(d); + }).call(utilNoAuto).each(function (d) { + select(this).call(uiCombobox(context, 'cycleway-' + stripcolon(d)).data(cycleway.options(d))); + }); + items = items.merge(enter); // Update + + wrap.selectAll('.preset-input-cycleway').on('change', change).on('blur', change); + } + + function change(d3_event, key) { + var newValue = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string + + if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) return; + + if (newValue === 'none' || newValue === '') { + newValue = undefined; + } + + var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left'; + var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey]; + + if (otherValue && Array.isArray(otherValue)) { + // we must always have an explicit value for comparison + otherValue = otherValue[0]; + } + + if (otherValue === 'none' || otherValue === '') { + otherValue = undefined; + } + + var tag = {}; // If the left and right tags match, use the cycleway tag to tag both + // sides the same way + + if (newValue === otherValue) { + tag = { + cycleway: newValue, + 'cycleway:left': undefined, + 'cycleway:right': undefined + }; + } else { + // Always set both left and right as changing one can affect the other + tag = { + cycleway: undefined + }; + tag[key] = newValue; + tag[otherKey] = otherValue; + } + + dispatch.call('change', this, tag); + } + + cycleway.options = function () { + return field.options.map(function (option) { + return { + title: field.t('options.' + option + '.description'), + value: option + }; + }); + }; + + cycleway.tags = function (tags) { + _tags = tags; // If cycleway is set, use that instead of individual values + + var commonValue = typeof tags.cycleway === 'string' && tags.cycleway; + utilGetSetValue(items.selectAll('.preset-input-cycleway'), function (d) { + if (commonValue) return commonValue; + return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : ''; + }).attr('title', function (d) { + if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) { + var vals = []; + + if (Array.isArray(tags.cycleway)) { + vals = vals.concat(tags.cycleway); + } + + if (Array.isArray(tags[d])) { + vals = vals.concat(tags[d]); + } + + return vals.filter(Boolean).join('\n'); + } + + return null; + }).attr('placeholder', function (d) { + if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) { + return _t('inspector.multiple_values'); + } + + return field.placeholder(); + }).classed('mixed', function (d) { + return Array.isArray(tags.cycleway) || Array.isArray(tags[d]); + }); + }; + + cycleway.focus = function () { + var node = wrap.selectAll('input').node(); + if (node) node.focus(); + }; + + return utilRebind(cycleway, dispatch, 'on'); + } + + function uiFieldLanes(field, context) { + var dispatch = dispatch$8('change'); + var LANE_WIDTH = 40; + var LANE_HEIGHT = 200; + var _entityIDs = []; + + function lanes(selection) { + var lanesData = context.entity(_entityIDs[0]).lanes(); + + if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { + selection.call(lanes.off); + return; + } + + var wrap = selection.selectAll('.form-field-input-wrap').data([0]); + wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap); + var surface = wrap.selectAll('.surface').data([0]); + var d = utilGetDimensions(wrap); + var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5; + surface = surface.enter().append('svg').attr('width', d[0]).attr('height', 300).attr('class', 'surface').merge(surface); + var lanesSelection = surface.selectAll('.lanes').data([0]); + lanesSelection = lanesSelection.enter().append('g').attr('class', 'lanes').merge(lanesSelection); + lanesSelection.attr('transform', function () { + return 'translate(' + freeSpace / 2 + ', 0)'; + }); + var lane = lanesSelection.selectAll('.lane').data(lanesData.lanes); + lane.exit().remove(); + var enter = lane.enter().append('g').attr('class', 'lane'); + enter.append('g').append('rect').attr('y', 50).attr('width', LANE_WIDTH).attr('height', LANE_HEIGHT); + enter.append('g').attr('class', 'forward').append('text').attr('y', 40).attr('x', 14).html('▲'); + enter.append('g').attr('class', 'bothways').append('text').attr('y', 40).attr('x', 14).html('▲▼'); + enter.append('g').attr('class', 'backward').append('text').attr('y', 40).attr('x', 14).html('▼'); + lane = lane.merge(enter); + lane.attr('transform', function (d) { + return 'translate(' + LANE_WIDTH * d.index * 1.5 + ', 0)'; + }); + lane.select('.forward').style('visibility', function (d) { + return d.direction === 'forward' ? 'visible' : 'hidden'; + }); + lane.select('.bothways').style('visibility', function (d) { + return d.direction === 'bothways' ? 'visible' : 'hidden'; + }); + lane.select('.backward').style('visibility', function (d) { + return d.direction === 'backward' ? 'visible' : 'hidden'; + }); + } + + lanes.entityIDs = function (val) { + _entityIDs = val; + }; + + lanes.tags = function () {}; + + lanes.focus = function () {}; + + lanes.off = function () {}; + + return utilRebind(lanes, dispatch, 'on'); + } + uiFieldLanes.supportsMultiselection = false; + + var _languagesArray = []; + function uiFieldLocalized(field, context) { + var dispatch = dispatch$8('change', 'input'); + var wikipedia = services.wikipedia; + var input = select(null); + var localizedInputs = select(null); + + var _countryCode; + + var _tags; // A concern here in switching to async data means that _languagesArray will not + // be available the first time through, so things like the fetchers and + // the language() function will not work immediately. + + + _mainFileFetcher.get('languages').then(loadLanguagesArray)["catch"](function () { + /* ignore */ + }); + var _territoryLanguages = {}; + _mainFileFetcher.get('territory_languages').then(function (d) { + _territoryLanguages = d; + })["catch"](function () { + /* ignore */ + }); // reuse these combos + + var langCombo = uiCombobox(context, 'localized-lang').fetcher(fetchLanguages).minItems(0); + + var _selection = select(null); + + var _multilingual = []; + + var _buttonTip = uiTooltip().title(_t.html('translate.translate')).placement('left'); + + var _wikiTitles; + + var _entityIDs = []; + + function loadLanguagesArray(dataLanguages) { + if (_languagesArray.length !== 0) return; // some conversion is needed to ensure correct OSM tags are used + + var replacements = { + sr: 'sr-Cyrl', + // in OSM, `sr` implies Cyrillic + 'sr-Cyrl': false // `sr-Cyrl` isn't used in OSM + + }; + + for (var code in dataLanguages) { + if (replacements[code] === false) continue; + var metaCode = code; + if (replacements[code]) metaCode = replacements[code]; + + _languagesArray.push({ + localName: _mainLocalizer.languageName(metaCode, { + localOnly: true + }), + nativeName: dataLanguages[metaCode].nativeName, + code: code, + label: _mainLocalizer.languageName(metaCode) + }); + } + } + + function calcLocked() { + // Protect name field for suggestion presets that don't display a brand/operator field + var isLocked = field.id === 'name' && _entityIDs.length && _entityIDs.some(function (entityID) { + var entity = context.graph().hasEntity(entityID); + if (!entity) return false; // Features linked to Wikidata are likely important and should be protected + + if (entity.tags.wikidata) return true; // Assume the name has already been confirmed if its source has been researched + + if (entity.tags['name:etymology:wikidata']) return true; // Lock the `name` if this is a suggestion preset that assigns the name, + // and the preset does not display a `brand` or `operator` field. + // (For presets like hotels, car dealerships, post offices, the `name` should remain editable) + // see also similar logic in `outdated_tags.js` + + var preset = _mainPresetIndex.match(entity, context.graph()); + + if (preset) { + var isSuggestion = preset.suggestion; + var fields = preset.fields(); + var showsBrandField = fields.some(function (d) { + return d.id === 'brand'; + }); + var showsOperatorField = fields.some(function (d) { + return d.id === 'operator'; + }); + var setsName = preset.addTags.name; + var setsBrandWikidata = preset.addTags['brand:wikidata']; + var setsOperatorWikidata = preset.addTags['operator:wikidata']; + return isSuggestion && setsName && (setsBrandWikidata && !showsBrandField || setsOperatorWikidata && !showsOperatorField); + } + + return false; + }); + + field.locked(isLocked); + } // update _multilingual, maintaining the existing order + + + function calcMultilingual(tags) { + var existingLangsOrdered = _multilingual.map(function (item) { + return item.lang; + }); + + var existingLangs = new Set(existingLangsOrdered.filter(Boolean)); + + for (var k in tags) { + var m = k.match(/^(.*):(.*)$/); + + if (m && m[1] === field.key && m[2]) { + var item = { + lang: m[2], + value: tags[k] + }; + + if (existingLangs.has(item.lang)) { + // update the value + _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value; + existingLangs["delete"](item.lang); + } else { + _multilingual.push(item); + } + } + } // Don't remove items based on deleted tags, since this makes the UI + // disappear unexpectedly when clearing values - #8164 + + + _multilingual.forEach(function (item) { + if (item.lang && existingLangs.has(item.lang)) { + item.value = ''; + } + }); + } + + function localized(selection) { + _selection = selection; + calcLocked(); + var isLocked = field.locked(); + var wrap = selection.selectAll('.form-field-input-wrap').data([0]); // enter/update + + wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap); + input = wrap.selectAll('.localized-main').data([0]); // enter/update + + input = input.enter().append('input').attr('type', 'text').attr('id', field.domId).attr('class', 'localized-main').call(utilNoAuto).merge(input); + input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change()); + var translateButton = wrap.selectAll('.localized-add').data([0]); + translateButton = translateButton.enter().append('button').attr('class', 'localized-add form-field-button').call(svgIcon('#iD-icon-plus')).merge(translateButton); + translateButton.classed('disabled', !!isLocked).call(isLocked ? _buttonTip.destroy : _buttonTip).on('click', addNew); + + if (_tags && !_multilingual.length) { + calcMultilingual(_tags); + } + + localizedInputs = selection.selectAll('.localized-multilingual').data([0]); + localizedInputs = localizedInputs.enter().append('div').attr('class', 'localized-multilingual').merge(localizedInputs); + localizedInputs.call(renderMultilingual); + localizedInputs.selectAll('button, input').classed('disabled', !!isLocked).attr('readonly', isLocked || null); + + function addNew(d3_event) { + d3_event.preventDefault(); + if (field.locked()) return; + var defaultLang = _mainLocalizer.languageCode().toLowerCase(); + + var langExists = _multilingual.find(function (datum) { + return datum.lang === defaultLang; + }); + + var isLangEn = defaultLang.indexOf('en') > -1; + + if (isLangEn || langExists) { + defaultLang = ''; + langExists = _multilingual.find(function (datum) { + return datum.lang === defaultLang; + }); + } + + if (!langExists) { + // prepend the value so it appears at the top + _multilingual.unshift({ + lang: defaultLang, + value: '' + }); + + localizedInputs.call(renderMultilingual); + } + } + + function change(onInput) { + return function (d3_event) { + if (field.locked()) { + d3_event.preventDefault(); + return; + } + + var val = utilGetSetValue(select(this)); + if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string + + if (!val && Array.isArray(_tags[field.key])) return; + var t = {}; + t[field.key] = val || undefined; + dispatch.call('change', this, t, onInput); + }; + } + } + + function key(lang) { + return field.key + ':' + lang; + } + + function changeLang(d3_event, d) { + var tags = {}; // make sure unrecognized suffixes are lowercase - #7156 + + var lang = utilGetSetValue(select(this)).toLowerCase(); + + var language = _languagesArray.find(function (d) { + return d.label.toLowerCase() === lang || d.localName && d.localName.toLowerCase() === lang || d.nativeName && d.nativeName.toLowerCase() === lang; + }); + + if (language) lang = language.code; + + if (d.lang && d.lang !== lang) { + tags[key(d.lang)] = undefined; + } + + var newKey = lang && context.cleanTagKey(key(lang)); + var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value')); + + if (newKey && value) { + tags[newKey] = value; + } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) { + tags[newKey] = _wikiTitles[d.lang]; + } + + d.lang = lang; + dispatch.call('change', this, tags); + } + + function changeValue(d3_event, d) { + if (!d.lang) return; + var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined; // don't override multiple values with blank string + + if (!value && Array.isArray(d.value)) return; + var t = {}; + t[key(d.lang)] = value; + d.value = value; + dispatch.call('change', this, t); + } + + function fetchLanguages(value, cb) { + var v = value.toLowerCase(); // show the user's language first + + var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()]; + + if (_countryCode && _territoryLanguages[_countryCode]) { + langCodes = langCodes.concat(_territoryLanguages[_countryCode]); + } + + var langItems = []; + langCodes.forEach(function (code) { + var langItem = _languagesArray.find(function (item) { + return item.code === code; + }); + + if (langItem) langItems.push(langItem); + }); + langItems = utilArrayUniq(langItems.concat(_languagesArray)); + cb(langItems.filter(function (d) { + return d.label.toLowerCase().indexOf(v) >= 0 || d.localName && d.localName.toLowerCase().indexOf(v) >= 0 || d.nativeName && d.nativeName.toLowerCase().indexOf(v) >= 0 || d.code.toLowerCase().indexOf(v) >= 0; + }).map(function (d) { + return { + value: d.label + }; + })); + } + + function renderMultilingual(selection) { + var entries = selection.selectAll('div.entry').data(_multilingual, function (d) { + return d.lang; + }); + entries.exit().style('top', '0').style('max-height', '240px').transition().duration(200).style('opacity', '0').style('max-height', '0px').remove(); + var entriesEnter = entries.enter().append('div').attr('class', 'entry').each(function (_, index) { + var wrap = select(this); + var domId = utilUniqueDomId(index); + var label = wrap.append('label').attr('class', 'field-label').attr('for', domId); + var text = label.append('span').attr('class', 'label-text'); + text.append('span').attr('class', 'label-textvalue').html(_t.html('translate.localized_translation_label')); + text.append('span').attr('class', 'label-textannotation'); + label.append('button').attr('class', 'remove-icon-multilingual').on('click', function (d3_event, d) { + if (field.locked()) return; + d3_event.preventDefault(); // remove the UI item manually + + _multilingual.splice(_multilingual.indexOf(d), 1); + + var langKey = d.lang && key(d.lang); + + if (langKey && langKey in _tags) { + delete _tags[langKey]; // remove from entity tags + + var t = {}; + t[langKey] = undefined; + dispatch.call('change', this, t); + return; + } + + renderMultilingual(selection); + }).call(svgIcon('#iD-operation-delete')); + wrap.append('input').attr('class', 'localized-lang').attr('id', domId).attr('type', 'text').attr('placeholder', _t('translate.localized_translation_language')).on('blur', changeLang).on('change', changeLang).call(langCombo); + wrap.append('input').attr('type', 'text').attr('class', 'localized-value').on('blur', changeValue).on('change', changeValue); + }); + entriesEnter.style('margin-top', '0px').style('max-height', '0px').style('opacity', '0').transition().duration(200).style('margin-top', '10px').style('max-height', '240px').style('opacity', '1').on('end', function () { + select(this).style('max-height', '').style('overflow', 'visible'); + }); + entries = entries.merge(entriesEnter); + entries.order(); // allow removing the entry UIs even if there isn't a tag to remove + + entries.classed('present', true); + utilGetSetValue(entries.select('.localized-lang'), function (d) { + var langItem = _languagesArray.find(function (item) { + return item.code === d.lang; + }); + + if (langItem) return langItem.label; + return d.lang; + }); + utilGetSetValue(entries.select('.localized-value'), function (d) { + return typeof d.value === 'string' ? d.value : ''; + }).attr('title', function (d) { + return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null; + }).attr('placeholder', function (d) { + return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name'); + }).classed('mixed', function (d) { + return Array.isArray(d.value); + }); + } + + localized.tags = function (tags) { + _tags = tags; // Fetch translations from wikipedia + + if (typeof tags.wikipedia === 'string' && !_wikiTitles) { + _wikiTitles = {}; + var wm = tags.wikipedia.match(/([^:]+):(.+)/); + + if (wm && wm[0] && wm[1]) { + wikipedia.translations(wm[1], wm[2], function (err, d) { + if (err || !d) return; + _wikiTitles = d; + }); + } + } + + var isMixed = Array.isArray(tags[field.key]); + utilGetSetValue(input, typeof tags[field.key] === 'string' ? tags[field.key] : '').attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined).attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder()).classed('mixed', isMixed); + calcMultilingual(tags); + + _selection.call(localized); + }; + + localized.focus = function () { + input.node().focus(); + }; + + localized.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + _entityIDs = val; + _multilingual = []; + loadCountryCode(); + return localized; + }; + + function loadCountryCode() { + var extent = combinedEntityExtent(); + var countryCode = extent && iso1A2Code(extent.center()); + _countryCode = countryCode && countryCode.toLowerCase(); + } + + function combinedEntityExtent() { + return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph()); + } + + return utilRebind(localized, dispatch, 'on'); + } + + function uiFieldRoadheight(field, context) { + var dispatch = dispatch$8('change'); + var primaryUnitInput = select(null); + var primaryInput = select(null); + var secondaryInput = select(null); + var secondaryUnitInput = select(null); + var _entityIDs = []; + + var _tags; + + var _isImperial; + + var primaryUnits = [{ + value: 'm', + title: _t('inspector.roadheight.meter') + }, { + value: 'ft', + title: _t('inspector.roadheight.foot') + }]; + var unitCombo = uiCombobox(context, 'roadheight-unit').data(primaryUnits); + + function roadheight(selection) { + var wrap = selection.selectAll('.form-field-input-wrap').data([0]); + wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap); + primaryInput = wrap.selectAll('input.roadheight-number').data([0]); + primaryInput = primaryInput.enter().append('input').attr('type', 'text').attr('class', 'roadheight-number').attr('id', field.domId).call(utilNoAuto).merge(primaryInput); + primaryInput.on('change', change).on('blur', change); + var loc = combinedEntityExtent().center(); + _isImperial = roadHeightUnit(loc) === 'ft'; + primaryUnitInput = wrap.selectAll('input.roadheight-unit').data([0]); + primaryUnitInput = primaryUnitInput.enter().append('input').attr('type', 'text').attr('class', 'roadheight-unit').call(unitCombo).merge(primaryUnitInput); + primaryUnitInput.on('blur', changeUnits).on('change', changeUnits); + secondaryInput = wrap.selectAll('input.roadheight-secondary-number').data([0]); + secondaryInput = secondaryInput.enter().append('input').attr('type', 'text').attr('class', 'roadheight-secondary-number').call(utilNoAuto).merge(secondaryInput); + secondaryInput.on('change', change).on('blur', change); + secondaryUnitInput = wrap.selectAll('input.roadheight-secondary-unit').data([0]); + secondaryUnitInput = secondaryUnitInput.enter().append('input').attr('type', 'text').call(utilNoAuto).classed('disabled', true).classed('roadheight-secondary-unit', true).attr('readonly', 'readonly').merge(secondaryUnitInput); + + function changeUnits() { + _isImperial = utilGetSetValue(primaryUnitInput) === 'ft'; + utilGetSetValue(primaryUnitInput, _isImperial ? 'ft' : 'm'); + setUnitSuggestions(); + change(); + } + } + + function setUnitSuggestions() { + utilGetSetValue(primaryUnitInput, _isImperial ? 'ft' : 'm'); + } + + function change() { + var tag = {}; + var primaryValue = utilGetSetValue(primaryInput).trim(); + var secondaryValue = utilGetSetValue(secondaryInput).trim(); // don't override multiple values with blank string + + if (!primaryValue && !secondaryValue && Array.isArray(_tags[field.key])) return; + + if (!primaryValue && !secondaryValue) { + tag[field.key] = undefined; + } else if (isNaN(primaryValue) || isNaN(secondaryValue) || !_isImperial) { + tag[field.key] = context.cleanTagValue(primaryValue); + } else { + if (primaryValue !== '') { + primaryValue = context.cleanTagValue(primaryValue + '\''); + } + + if (secondaryValue !== '') { + secondaryValue = context.cleanTagValue(secondaryValue + '"'); + } + + tag[field.key] = primaryValue + secondaryValue; + } + + dispatch.call('change', this, tag); + } + + roadheight.tags = function (tags) { + _tags = tags; + var primaryValue = tags[field.key]; + var secondaryValue; + var isMixed = Array.isArray(primaryValue); + + if (!isMixed) { + if (primaryValue && (primaryValue.indexOf('\'') >= 0 || primaryValue.indexOf('"') >= 0)) { + secondaryValue = primaryValue.match(/(-?[\d.]+)"/); + + if (secondaryValue !== null) { + secondaryValue = secondaryValue[1]; + } + + primaryValue = primaryValue.match(/(-?[\d.]+)'/); + + if (primaryValue !== null) { + primaryValue = primaryValue[1]; + } + + _isImperial = true; + } else if (primaryValue) { + _isImperial = false; + } + } + + setUnitSuggestions(); + utilGetSetValue(primaryInput, typeof primaryValue === 'string' ? primaryValue : '').attr('title', isMixed ? primaryValue.filter(Boolean).join('\n') : null).attr('placeholder', isMixed ? _t('inspector.multiple_values') : _t('inspector.unknown')).classed('mixed', isMixed); + utilGetSetValue(secondaryInput, typeof secondaryValue === 'string' ? secondaryValue : '').attr('placeholder', isMixed ? _t('inspector.multiple_values') : _isImperial ? '0' : null).classed('mixed', isMixed).classed('disabled', !_isImperial).attr('readonly', _isImperial ? null : 'readonly'); + secondaryUnitInput.attr('value', _isImperial ? _t('inspector.roadheight.inch') : null); + }; + + roadheight.focus = function () { + primaryInput.node().focus(); + }; + + roadheight.entityIDs = function (val) { + _entityIDs = val; + }; + + function combinedEntityExtent() { + return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph()); + } + + return utilRebind(roadheight, dispatch, 'on'); + } + + function uiFieldRoadspeed(field, context) { + var dispatch = dispatch$8('change'); + var unitInput = select(null); + var input = select(null); + var _entityIDs = []; + + var _tags; + + var _isImperial; + + var speedCombo = uiCombobox(context, 'roadspeed'); + var unitCombo = uiCombobox(context, 'roadspeed-unit').data(['km/h', 'mph'].map(comboValues)); + var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]; + var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80]; + + function roadspeed(selection) { + var wrap = selection.selectAll('.form-field-input-wrap').data([0]); + wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap); + input = wrap.selectAll('input.roadspeed-number').data([0]); + input = input.enter().append('input').attr('type', 'text').attr('class', 'roadspeed-number').attr('id', field.domId).call(utilNoAuto).call(speedCombo).merge(input); + input.on('change', change).on('blur', change); + var loc = combinedEntityExtent().center(); + _isImperial = roadSpeedUnit(loc) === 'mph'; + unitInput = wrap.selectAll('input.roadspeed-unit').data([0]); + unitInput = unitInput.enter().append('input').attr('type', 'text').attr('class', 'roadspeed-unit').call(unitCombo).merge(unitInput); + unitInput.on('blur', changeUnits).on('change', changeUnits); + + function changeUnits() { + _isImperial = utilGetSetValue(unitInput) === 'mph'; + utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h'); + setUnitSuggestions(); + change(); + } + } + + function setUnitSuggestions() { + speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues)); + utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h'); + } + + function comboValues(d) { + return { + value: d.toString(), + title: d.toString() + }; + } + + function change() { + var tag = {}; + var value = utilGetSetValue(input).trim(); // don't override multiple values with blank string + + if (!value && Array.isArray(_tags[field.key])) return; + + if (!value) { + tag[field.key] = undefined; + } else if (isNaN(value) || !_isImperial) { + tag[field.key] = context.cleanTagValue(value); + } else { + tag[field.key] = context.cleanTagValue(value + ' mph'); + } + + dispatch.call('change', this, tag); + } + + roadspeed.tags = function (tags) { + _tags = tags; + var value = tags[field.key]; + var isMixed = Array.isArray(value); + + if (!isMixed) { + if (value && value.indexOf('mph') >= 0) { + value = parseInt(value, 10).toString(); + _isImperial = true; + } else if (value) { + _isImperial = false; + } + } + + setUnitSuggestions(); + utilGetSetValue(input, typeof value === 'string' ? value : '').attr('title', isMixed ? value.filter(Boolean).join('\n') : null).attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder()).classed('mixed', isMixed); + }; + + roadspeed.focus = function () { + input.node().focus(); + }; + + roadspeed.entityIDs = function (val) { + _entityIDs = val; + }; + + function combinedEntityExtent() { + return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph()); + } + + return utilRebind(roadspeed, dispatch, 'on'); + } + + function uiFieldRadio(field, context) { + var dispatch = dispatch$8('change'); + var placeholder = select(null); + var wrap = select(null); + var labels = select(null); + var radios = select(null); + var radioData = (field.options || field.keys).slice(); // shallow copy + + var typeField; + var layerField; + var _oldType = {}; + var _entityIDs = []; + + function selectedKey() { + var node = wrap.selectAll('.form-field-input-radio label.active input'); + return !node.empty() && node.datum(); + } + + function radio(selection) { + selection.classed('preset-radio', true); + wrap = selection.selectAll('.form-field-input-wrap').data([0]); + var enter = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-radio'); + enter.append('span').attr('class', 'placeholder'); + wrap = wrap.merge(enter); + placeholder = wrap.selectAll('.placeholder'); + labels = wrap.selectAll('label').data(radioData); + enter = labels.enter().append('label'); + enter.append('input').attr('type', 'radio').attr('name', field.id).attr('value', function (d) { + return field.t('options.' + d, { + 'default': d + }); + }).attr('checked', false); + enter.append('span').html(function (d) { + return field.t.html('options.' + d, { + 'default': d + }); + }); + labels = labels.merge(enter); + radios = labels.selectAll('input').on('change', changeRadio); + } + + function structureExtras(selection, tags) { + var selected = selectedKey() || tags.layer !== undefined; + var type = _mainPresetIndex.field(selected); + var layer = _mainPresetIndex.field('layer'); + var showLayer = selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined; + var extrasWrap = selection.selectAll('.structure-extras-wrap').data(selected ? [0] : []); + extrasWrap.exit().remove(); + extrasWrap = extrasWrap.enter().append('div').attr('class', 'structure-extras-wrap').merge(extrasWrap); + var list = extrasWrap.selectAll('ul').data([0]); + list = list.enter().append('ul').attr('class', 'rows').merge(list); // Type + + if (type) { + if (!typeField || typeField.id !== selected) { + typeField = uiField(context, type, _entityIDs, { + wrap: false + }).on('change', changeType); + } + + typeField.tags(tags); + } else { + typeField = null; + } + + var typeItem = list.selectAll('.structure-type-item').data(typeField ? [typeField] : [], function (d) { + return d.id; + }); // Exit + + typeItem.exit().remove(); // Enter + + var typeEnter = typeItem.enter().insert('li', ':first-child').attr('class', 'labeled-input structure-type-item'); + typeEnter.append('span').attr('class', 'label structure-label-type').attr('for', 'preset-input-' + selected).html(_t.html('inspector.radio.structure.type')); + typeEnter.append('div').attr('class', 'structure-input-type-wrap'); // Update + + typeItem = typeItem.merge(typeEnter); + + if (typeField) { + typeItem.selectAll('.structure-input-type-wrap').call(typeField.render); + } // Layer + + + if (layer && showLayer) { + if (!layerField) { + layerField = uiField(context, layer, _entityIDs, { + wrap: false + }).on('change', changeLayer); + } + + layerField.tags(tags); + field.keys = utilArrayUnion(field.keys, ['layer']); + } else { + layerField = null; + field.keys = field.keys.filter(function (k) { + return k !== 'layer'; + }); + } + + var layerItem = list.selectAll('.structure-layer-item').data(layerField ? [layerField] : []); // Exit + + layerItem.exit().remove(); // Enter + + var layerEnter = layerItem.enter().append('li').attr('class', 'labeled-input structure-layer-item'); + layerEnter.append('span').attr('class', 'label structure-label-layer').attr('for', 'preset-input-layer').html(_t.html('inspector.radio.structure.layer')); + layerEnter.append('div').attr('class', 'structure-input-layer-wrap'); // Update + + layerItem = layerItem.merge(layerEnter); + + if (layerField) { + layerItem.selectAll('.structure-input-layer-wrap').call(layerField.render); + } + } + + function changeType(t, onInput) { + var key = selectedKey(); + if (!key) return; + var val = t[key]; + + if (val !== 'no') { + _oldType[key] = val; + } + + if (field.type === 'structureRadio') { + // remove layer if it should not be set + if (val === 'no' || key !== 'bridge' && key !== 'tunnel' || key === 'tunnel' && val === 'building_passage') { + t.layer = undefined; + } // add layer if it should be set + + + if (t.layer === undefined) { + if (key === 'bridge' && val !== 'no') { + t.layer = '1'; + } + + if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') { + t.layer = '-1'; + } + } + } + + dispatch.call('change', this, t, onInput); + } + + function changeLayer(t, onInput) { + if (t.layer === '0') { + t.layer = undefined; + } + + dispatch.call('change', this, t, onInput); + } + + function changeRadio() { + var t = {}; + var activeKey; + + if (field.key) { + t[field.key] = undefined; + } + + radios.each(function (d) { + var active = select(this).property('checked'); + if (active) activeKey = d; + + if (field.key) { + if (active) t[field.key] = d; + } else { + var val = _oldType[activeKey] || 'yes'; + t[d] = active ? val : undefined; + } + }); + + if (field.type === 'structureRadio') { + if (activeKey === 'bridge') { + t.layer = '1'; + } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') { + t.layer = '-1'; + } else { + t.layer = undefined; + } + } + + dispatch.call('change', this, t); + } + + radio.tags = function (tags) { + radios.property('checked', function (d) { + if (field.key) { + return tags[field.key] === d; + } + + return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no'); + }); + + function isMixed(d) { + if (field.key) { + return Array.isArray(tags[field.key]) && tags[field.key].includes(d); + } + + return Array.isArray(tags[d]); + } + + labels.classed('active', function (d) { + if (field.key) { + return Array.isArray(tags[field.key]) && tags[field.key].includes(d) || tags[field.key] === d; + } + + return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no'); + }).classed('mixed', isMixed).attr('title', function (d) { + return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null; + }); + var selection = radios.filter(function () { + return this.checked; + }); + + if (selection.empty()) { + placeholder.html(_t.html('inspector.none')); + } else { + placeholder.html(selection.attr('value')); + _oldType[selection.datum()] = tags[selection.datum()]; + } + + if (field.type === 'structureRadio') { + // For waterways without a tunnel tag, set 'culvert' as + // the _oldType to default to if the user picks 'tunnel' + if (!!tags.waterway && !_oldType.tunnel) { + _oldType.tunnel = 'culvert'; + } + + wrap.call(structureExtras, tags); + } + }; + + radio.focus = function () { + radios.node().focus(); + }; + + radio.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + _entityIDs = val; + _oldType = {}; + return radio; + }; + + radio.isAllowed = function () { + return _entityIDs.length === 1; + }; + + return utilRebind(radio, dispatch, 'on'); + } + + function uiFieldRestrictions(field, context) { + var dispatch = dispatch$8('change'); + var breathe = behaviorBreathe(); + corePreferences('turn-restriction-via-way', null); // remove old key + + var storedViaWay = corePreferences('turn-restriction-via-way0'); // use new key #6922 + + var storedDistance = corePreferences('turn-restriction-distance'); + + var _maxViaWay = storedViaWay !== null ? +storedViaWay : 0; + + var _maxDistance = storedDistance ? +storedDistance : 30; + + var _initialized = false; + + var _parent = select(null); // the entire field + + + var _container = select(null); // just the map + + + var _oldTurns; + + var _graph; + + var _vertexID; + + var _intersection; + + var _fromWayID; + + var _lastXPos; + + function restrictions(selection) { + _parent = selection; // try to reuse the intersection, but always rebuild it if the graph has changed + + if (_vertexID && (context.graph() !== _graph || !_intersection)) { + _graph = context.graph(); + _intersection = osmIntersection(_graph, _vertexID, _maxDistance); + } // It's possible for there to be no actual intersection here. + // for example, a vertex of two `highway=path` + // In this case, hide the field. + + + var isOK = _intersection && _intersection.vertices.length && // has vertices + _intersection.vertices // has the vertex that the user selected + .filter(function (vertex) { + return vertex.id === _vertexID; + }).length && _intersection.ways.length > 2 && // has more than 2 ways + _intersection.ways // has more than 1 TO way + .filter(function (way) { + return way.__to; + }).length > 1; // Also hide in the case where + + select(selection.node().parentNode).classed('hide', !isOK); // if form field is hidden or has detached from dom, clean up. + + if (!isOK || !context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode || !selection.node().parentNode.parentNode) { + selection.call(restrictions.off); + return; + } + + var wrap = selection.selectAll('.form-field-input-wrap').data([0]); + wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap); + var container = wrap.selectAll('.restriction-container').data([0]); // enter + + var containerEnter = container.enter().append('div').attr('class', 'restriction-container'); + containerEnter.append('div').attr('class', 'restriction-help'); // update + + _container = containerEnter.merge(container).call(renderViewer); + var controls = wrap.selectAll('.restriction-controls').data([0]); // enter/update + + controls.enter().append('div').attr('class', 'restriction-controls-container').append('div').attr('class', 'restriction-controls').merge(controls).call(renderControls); + } + + function renderControls(selection) { + var distControl = selection.selectAll('.restriction-distance').data([0]); + distControl.exit().remove(); + var distControlEnter = distControl.enter().append('div').attr('class', 'restriction-control restriction-distance'); + distControlEnter.append('span').attr('class', 'restriction-control-label restriction-distance-label').html(_t.html('restriction.controls.distance') + ':'); + distControlEnter.append('input').attr('class', 'restriction-distance-input').attr('type', 'range').attr('min', '20').attr('max', '50').attr('step', '5'); + distControlEnter.append('span').attr('class', 'restriction-distance-text'); // update + + selection.selectAll('.restriction-distance-input').property('value', _maxDistance).on('input', function () { + var val = select(this).property('value'); + _maxDistance = +val; + _intersection = null; + + _container.selectAll('.layer-osm .layer-turns *').remove(); + + corePreferences('turn-restriction-distance', _maxDistance); + + _parent.call(restrictions); + }); + selection.selectAll('.restriction-distance-text').html(displayMaxDistance(_maxDistance)); + var viaControl = selection.selectAll('.restriction-via-way').data([0]); + viaControl.exit().remove(); + var viaControlEnter = viaControl.enter().append('div').attr('class', 'restriction-control restriction-via-way'); + viaControlEnter.append('span').attr('class', 'restriction-control-label restriction-via-way-label').html(_t.html('restriction.controls.via') + ':'); + viaControlEnter.append('input').attr('class', 'restriction-via-way-input').attr('type', 'range').attr('min', '0').attr('max', '2').attr('step', '1'); + viaControlEnter.append('span').attr('class', 'restriction-via-way-text'); // update + + selection.selectAll('.restriction-via-way-input').property('value', _maxViaWay).on('input', function () { + var val = select(this).property('value'); + _maxViaWay = +val; + + _container.selectAll('.layer-osm .layer-turns *').remove(); + + corePreferences('turn-restriction-via-way0', _maxViaWay); + + _parent.call(restrictions); + }); + selection.selectAll('.restriction-via-way-text').html(displayMaxVia(_maxViaWay)); + } + + function renderViewer(selection) { + if (!_intersection) return; + var vgraph = _intersection.graph; + var filter = utilFunctor(true); + var projection = geoRawMercator(); // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect` + // Instead of asking the restriction-container for its dimensions, + // we can ask the .sidebar, which can have its dimensions cached. + // width: calc as sidebar - padding + // height: hardcoded (from `80_app.css`) + // var d = utilGetDimensions(selection); + + var sdims = utilGetDimensions(context.container().select('.sidebar')); + var d = [sdims[0] - 50, 370]; + var c = geoVecScale(d, 0.5); + var z = 22; + projection.scale(geoZoomToScale(z)); // Calculate extent of all key vertices + + var extent = geoExtent(); + + for (var i = 0; i < _intersection.vertices.length; i++) { + extent._extend(_intersection.vertices[i].extent()); + } // If this is a large intersection, adjust zoom to fit extent + + + if (_intersection.vertices.length > 1) { + var padding = 180; // in z22 pixels + + var tl = projection([extent[0][0], extent[1][1]]); + var br = projection([extent[1][0], extent[0][1]]); + var hFactor = (br[0] - tl[0]) / (d[0] - padding); + var vFactor = (br[1] - tl[1]) / (d[1] - padding); + var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2; + var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2; + z = z - Math.max(hZoomDiff, vZoomDiff); + projection.scale(geoZoomToScale(z)); + } + + var padTop = 35; // reserve top space for hint text + + var extentCenter = projection(extent.center()); + extentCenter[1] = extentCenter[1] - padTop; + projection.translate(geoVecSubtract(c, extentCenter)).clipExtent([[0, 0], d]); + var drawLayers = svgLayers(projection, context).only(['osm', 'touch']).dimensions(d); + var drawVertices = svgVertices(projection, context); + var drawLines = svgLines(projection, context); + var drawTurns = svgTurns(projection, context); + var firstTime = selection.selectAll('.surface').empty(); + selection.call(drawLayers); + var surface = selection.selectAll('.surface').classed('tr', true); + + if (firstTime) { + _initialized = true; + surface.call(breathe); + } // This can happen if we've lowered the detail while a FROM way + // is selected, and that way is no longer part of the intersection. + + + if (_fromWayID && !vgraph.hasEntity(_fromWayID)) { + _fromWayID = null; + _oldTurns = null; + } + + surface.call(utilSetDimensions, d).call(drawVertices, vgraph, _intersection.vertices, filter, extent, z).call(drawLines, vgraph, _intersection.ways, filter).call(drawTurns, vgraph, _intersection.turns(_fromWayID, _maxViaWay)); + surface.on('click.restrictions', click).on('mouseover.restrictions', mouseover); + surface.selectAll('.selected').classed('selected', false); + surface.selectAll('.related').classed('related', false); + var way; + + if (_fromWayID) { + way = vgraph.entity(_fromWayID); + surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true); + } + + document.addEventListener('resizeWindow', function () { + utilSetDimensions(_container, null); + redraw(1); + }, false); + updateHints(null); + + function click(d3_event) { + surface.call(breathe.off).call(breathe); + var datum = d3_event.target.__data__; + var entity = datum && datum.properties && datum.properties.entity; + + if (entity) { + datum = entity; + } + + if (datum instanceof osmWay && (datum.__from || datum.__via)) { + _fromWayID = datum.id; + _oldTurns = null; + redraw(); + } else if (datum instanceof osmTurn) { + var actions, extraActions, turns, i; + var restrictionType = osmInferRestriction(vgraph, datum, projection); + + if (datum.restrictionID && !datum.direct) { + return; + } else if (datum.restrictionID && !datum.only) { + // NO -> ONLY + var seen = {}; + var datumOnly = JSON.parse(JSON.stringify(datum)); // deep clone the datum + + datumOnly.only = true; // but change this property + + restrictionType = restrictionType.replace(/^no/, 'only'); // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA. + // We will remember them in _oldTurns, and restore them if the user clicks again. + + turns = _intersection.turns(_fromWayID, 2); + extraActions = []; + _oldTurns = []; + + for (i = 0; i < turns.length; i++) { + var turn = turns[i]; + if (seen[turn.restrictionID]) continue; // avoid deleting the turn twice (#4968, #4928) + + if (turn.direct && turn.path[1] === datum.path[1]) { + seen[turns[i].restrictionID] = true; + turn.restrictionType = osmInferRestriction(vgraph, turn, projection); + + _oldTurns.push(turn); + + extraActions.push(actionUnrestrictTurn(turn)); + } + } + + actions = _intersection.actions.concat(extraActions, [actionRestrictTurn(datumOnly, restrictionType), _t('operations.restriction.annotation.create')]); + } else if (datum.restrictionID) { + // ONLY -> Allowed + // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state. + // This relies on the assumption that the intersection was already split up when we + // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed. + turns = _oldTurns || []; + extraActions = []; + + for (i = 0; i < turns.length; i++) { + if (turns[i].key !== datum.key) { + extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType)); + } + } + + _oldTurns = null; + actions = _intersection.actions.concat(extraActions, [actionUnrestrictTurn(datum), _t('operations.restriction.annotation.delete')]); + } else { + // Allowed -> NO + actions = _intersection.actions.concat([actionRestrictTurn(datum, restrictionType), _t('operations.restriction.annotation.create')]); + } + + context.perform.apply(context, actions); // At this point the datum will be changed, but will have same key.. + // Refresh it and update the help.. + + var s = surface.selectAll('.' + datum.key); + datum = s.empty() ? null : s.datum(); + updateHints(datum); + } else { + _fromWayID = null; + _oldTurns = null; + redraw(); + } + } + + function mouseover(d3_event) { + var datum = d3_event.target.__data__; + updateHints(datum); + } + + _lastXPos = _lastXPos || sdims[0]; + + function redraw(minChange) { + var xPos = -1; + + if (minChange) { + xPos = utilGetDimensions(context.container().select('.sidebar'))[0]; + } + + if (!minChange || minChange && Math.abs(xPos - _lastXPos) >= minChange) { + if (context.hasEntity(_vertexID)) { + _lastXPos = xPos; + + _container.call(renderViewer); + } + } + } + + function highlightPathsFrom(wayID) { + surface.selectAll('.related').classed('related', false).classed('allow', false).classed('restrict', false).classed('only', false); + surface.selectAll('.' + wayID).classed('related', true); + + if (wayID) { + var turns = _intersection.turns(wayID, _maxViaWay); + + for (var i = 0; i < turns.length; i++) { + var turn = turns[i]; + var ids = [turn.to.way]; + var klass = turn.no ? 'restrict' : turn.only ? 'only' : 'allow'; + + if (turn.only || turns.length === 1) { + if (turn.via.ways) { + ids = ids.concat(turn.via.ways); + } + } else if (turn.to.way === wayID) { + continue; + } + + surface.selectAll(utilEntitySelector(ids)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only'); + } + } + } + + function updateHints(datum) { + var help = _container.selectAll('.restriction-help').html(''); + + var placeholders = {}; + ['from', 'via', 'to'].forEach(function (k) { + placeholders[k] = '' + _t('restriction.help.' + k) + ''; + }); + var entity = datum && datum.properties && datum.properties.entity; + + if (entity) { + datum = entity; + } + + if (_fromWayID) { + way = vgraph.entity(_fromWayID); + surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true); + } // Hovering a way + + + if (datum instanceof osmWay && datum.__from) { + way = datum; + highlightPathsFrom(_fromWayID ? null : way.id); + surface.selectAll('.' + way.id).classed('related', true); + var clickSelect = !_fromWayID || _fromWayID !== way.id; + help.append('div') // "Click to select FROM {fromName}." / "FROM {fromName}" + .html(_t.html('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), { + from: placeholders.from, + fromName: displayName(way.id, vgraph) + })); // Hovering a turn arrow + } else if (datum instanceof osmTurn) { + var restrictionType = osmInferRestriction(vgraph, datum, projection); + var turnType = restrictionType.replace(/^(only|no)\_/, ''); + var indirect = datum.direct === false ? _t.html('restriction.help.indirect') : ''; + var klass, turnText, nextText; + + if (datum.no) { + klass = 'restrict'; + turnText = _t.html('restriction.help.turn.no_' + turnType, { + indirect: indirect + }); + nextText = _t.html('restriction.help.turn.only_' + turnType, { + indirect: '' + }); + } else if (datum.only) { + klass = 'only'; + turnText = _t.html('restriction.help.turn.only_' + turnType, { + indirect: indirect + }); + nextText = _t.html('restriction.help.turn.allowed_' + turnType, { + indirect: '' + }); + } else { + klass = 'allow'; + turnText = _t.html('restriction.help.turn.allowed_' + turnType, { + indirect: indirect + }); + nextText = _t.html('restriction.help.turn.no_' + turnType, { + indirect: '' + }); + } + + help.append('div') // "NO Right Turn (indirect)" + .attr('class', 'qualifier ' + klass).html(turnText); + help.append('div') // "FROM {fromName} TO {toName}" + .html(_t.html('restriction.help.from_name_to_name', { + from: placeholders.from, + fromName: displayName(datum.from.way, vgraph), + to: placeholders.to, + toName: displayName(datum.to.way, vgraph) + })); + + if (datum.via.ways && datum.via.ways.length) { + var names = []; + + for (var i = 0; i < datum.via.ways.length; i++) { + var prev = names[names.length - 1]; + var curr = displayName(datum.via.ways[i], vgraph); + + if (!prev || curr !== prev) { + // collapse identical names + names.push(curr); + } + } + + help.append('div') // "VIA {viaNames}" + .html(_t.html('restriction.help.via_names', { + via: placeholders.via, + viaNames: names.join(', ') + })); + } + + if (!indirect) { + help.append('div') // Click for "No Right Turn" + .html(_t.html('restriction.help.toggle', { + turn: nextText.trim() + })); + } + + highlightPathsFrom(null); + var alongIDs = datum.path.slice(); + surface.selectAll(utilEntitySelector(alongIDs)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only'); // Hovering empty surface + } else { + highlightPathsFrom(null); + + if (_fromWayID) { + help.append('div') // "FROM {fromName}" + .html(_t.html('restriction.help.from_name', { + from: placeholders.from, + fromName: displayName(_fromWayID, vgraph) + })); + } else { + help.append('div') // "Click to select a FROM segment." + .html(_t.html('restriction.help.select_from', { + from: placeholders.from + })); + } + } + } + } + + function displayMaxDistance(maxDist) { + var isImperial = !_mainLocalizer.usesMetric(); + var opts; + + if (isImperial) { + var distToFeet = { + // imprecise conversion for prettier display + 20: 70, + 25: 85, + 30: 100, + 35: 115, + 40: 130, + 45: 145, + 50: 160 + }[maxDist]; + opts = { + distance: _t('units.feet', { + quantity: distToFeet + }) + }; + } else { + opts = { + distance: _t('units.meters', { + quantity: maxDist + }) + }; + } + + return _t.html('restriction.controls.distance_up_to', opts); + } + + function displayMaxVia(maxVia) { + 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'); + } + + function displayName(entityID, graph) { + var entity = graph.entity(entityID); + var name = utilDisplayName(entity) || ''; + var matched = _mainPresetIndex.match(entity, graph); + var type = matched && matched.name() || utilDisplayType(entity.id); + return name || type; + } + + restrictions.entityIDs = function (val) { + _intersection = null; + _fromWayID = null; + _oldTurns = null; + _vertexID = val[0]; + }; + + restrictions.tags = function () {}; + + restrictions.focus = function () {}; + + restrictions.off = function (selection) { + if (!_initialized) return; + selection.selectAll('.surface').call(breathe.off).on('click.restrictions', null).on('mouseover.restrictions', null); + select(window).on('resize.restrictions', null); + }; + + return utilRebind(restrictions, dispatch, 'on'); + } + uiFieldRestrictions.supportsMultiselection = false; + + function uiFieldTextarea(field, context) { + var dispatch = dispatch$8('change'); + var input = select(null); + + var _tags; + + function textarea(selection) { + var wrap = selection.selectAll('.form-field-input-wrap').data([0]); + wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap); + input = wrap.selectAll('textarea').data([0]); + input = input.enter().append('textarea').attr('id', field.domId).call(utilNoAuto).on('input', change(true)).on('blur', change()).on('change', change()).merge(input); + } + + function change(onInput) { + return function () { + var val = utilGetSetValue(input); + if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string + + if (!val && Array.isArray(_tags[field.key])) return; + var t = {}; + t[field.key] = val || undefined; + dispatch.call('change', this, t, onInput); + }; + } + + textarea.tags = function (tags) { + _tags = tags; + var isMixed = Array.isArray(tags[field.key]); + utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '').attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined).attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder() || _t('inspector.unknown')).classed('mixed', isMixed); + }; + + textarea.focus = function () { + input.node().focus(); + }; + + return utilRebind(textarea, dispatch, 'on'); + } + + var $ = _export; + var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f; + var toLength = toLength$q; + var notARegExp = notARegexp; + var requireObjectCoercible = requireObjectCoercible$e; + var correctIsRegExpLogic = correctIsRegexpLogic; + + // eslint-disable-next-line es/no-string-prototype-endswith -- safe + var $endsWith = ''.endsWith; + var min = Math.min; + + var CORRECT_IS_REGEXP_LOGIC = correctIsRegExpLogic('endsWith'); + // https://github.com/zloirock/core-js/pull/702 + var MDN_POLYFILL_BUG = !CORRECT_IS_REGEXP_LOGIC && !!function () { + var descriptor = getOwnPropertyDescriptor(String.prototype, 'endsWith'); + return descriptor && !descriptor.writable; + }(); + + // `String.prototype.endsWith` method + // https://tc39.es/ecma262/#sec-string.prototype.endswith + $({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG && !CORRECT_IS_REGEXP_LOGIC }, { + endsWith: function endsWith(searchString /* , endPosition = @length */) { + var that = String(requireObjectCoercible(this)); + notARegExp(searchString); + var endPosition = arguments.length > 1 ? arguments[1] : undefined; + var len = toLength(that.length); + var end = endPosition === undefined ? len : min(toLength(endPosition), len); + var search = String(searchString); + return $endsWith + ? $endsWith.call(that, search, end) + : that.slice(end - search.length, end) === search; + } + }); + + function uiFieldWikidata(field, context) { + var wikidata = services.wikidata; + var dispatch = dispatch$8('change'); + + var _selection = select(null); + + var _searchInput = select(null); + + var _qid = null; + var _wikidataEntity = null; + var _wikiURL = ''; + var _entityIDs = []; + + var _wikipediaKey = field.keys && field.keys.find(function (key) { + return key.includes('wikipedia'); + }), + _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0]; + + var combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(true).minItems(1); + + function wiki(selection) { + _selection = selection; + var wrap = selection.selectAll('.form-field-input-wrap').data([0]); + wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap); + var list = wrap.selectAll('ul').data([0]); + list = list.enter().append('ul').attr('class', 'rows').merge(list); + var searchRow = list.selectAll('li.wikidata-search').data([0]); + var searchRowEnter = searchRow.enter().append('li').attr('class', 'wikidata-search'); + searchRowEnter.append('input').attr('type', 'text').attr('id', field.domId).style('flex', '1').call(utilNoAuto).on('focus', function () { + var node = select(this).node(); + node.setSelectionRange(0, node.value.length); + }).on('blur', function () { + setLabelForEntity(); + }).call(combobox.fetcher(fetchWikidataItems)); + combobox.on('accept', function (d) { + if (d) { + _qid = d.id; + change(); + } + }).on('cancel', function () { + setLabelForEntity(); + }); + searchRowEnter.append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', { + domain: 'wikidata.org' + })).call(svgIcon('#iD-icon-out-link')).on('click', function (d3_event) { + d3_event.preventDefault(); + if (_wikiURL) window.open(_wikiURL, '_blank'); + }); + searchRow = searchRow.merge(searchRowEnter); + _searchInput = searchRow.select('input'); + var wikidataProperties = ['description', 'identifier']; + var items = list.selectAll('li.labeled-input').data(wikidataProperties); // Enter + + var enter = items.enter().append('li').attr('class', function (d) { + return 'labeled-input preset-wikidata-' + d; + }); + enter.append('span').attr('class', 'label').html(function (d) { + return _t.html('wikidata.' + d); + }); + enter.append('input').attr('type', 'text').call(utilNoAuto).classed('disabled', 'true').attr('readonly', 'true'); + enter.append('button').attr('class', 'form-field-button').attr('title', _t('icons.copy')).call(svgIcon('#iD-operation-copy')).on('click', function (d3_event) { + d3_event.preventDefault(); + select(this.parentNode).select('input').node().select(); + document.execCommand('copy'); + }); + } + + function fetchWikidataItems(q, callback) { + if (!q && _hintKey) { + // other tags may be good search terms + for (var i in _entityIDs) { + var entity = context.hasEntity(_entityIDs[i]); + + if (entity.tags[_hintKey]) { + q = entity.tags[_hintKey]; + break; + } + } + } + + wikidata.itemsForSearchQuery(q, function (err, data) { + if (err) return; + + for (var i in data) { + data[i].value = data[i].label + ' (' + data[i].id + ')'; + data[i].title = data[i].description; + } + + if (callback) callback(data); + }); + } + + function change() { + var syncTags = {}; + syncTags[field.key] = _qid; + dispatch.call('change', this, syncTags); // attempt asynchronous update of wikidata tag.. + + var initGraph = context.graph(); + var initEntityIDs = _entityIDs; + wikidata.entityByQID(_qid, function (err, entity) { + if (err) return; // If graph has changed, we can't apply this update. + + if (context.graph() !== initGraph) return; + if (!entity.sitelinks) return; + var langs = wikidata.languagesToQuery(); // use the label and description languages as fallbacks + + ['labels', 'descriptions'].forEach(function (key) { + if (!entity[key]) return; + var valueLangs = Object.keys(entity[key]); + if (valueLangs.length === 0) return; + var valueLang = valueLangs[0]; + + if (langs.indexOf(valueLang) === -1) { + langs.push(valueLang); + } + }); + var newWikipediaValue; + + if (_wikipediaKey) { + var foundPreferred; + + for (var i in langs) { + var lang = langs[i]; + var siteID = lang.replace('-', '_') + 'wiki'; + + if (entity.sitelinks[siteID]) { + foundPreferred = true; + newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title; // use the first match + + break; + } + } + + if (!foundPreferred) { + // No wikipedia sites available in the user's language or the fallback languages, + // default to any wikipedia sitelink + var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function (site) { + return site.endsWith('wiki'); + }); + + if (wikiSiteKeys.length === 0) { + // if no wikipedia pages are linked to this wikidata entity, delete that tag + newWikipediaValue = null; + } else { + var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-'); + var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title; + newWikipediaValue = wikiLang + ':' + wikiTitle; + } + } + } + + if (newWikipediaValue) { + newWikipediaValue = context.cleanTagValue(newWikipediaValue); + } + + if (typeof newWikipediaValue === 'undefined') return; + var actions = initEntityIDs.map(function (entityID) { + var entity = context.hasEntity(entityID); + if (!entity) return null; + var currTags = Object.assign({}, entity.tags); // shallow copy + + if (newWikipediaValue === null) { + if (!currTags[_wikipediaKey]) return null; + delete currTags[_wikipediaKey]; + } else { + currTags[_wikipediaKey] = newWikipediaValue; + } + + return actionChangeTags(entityID, currTags); + }).filter(Boolean); + if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change + + context.overwrite(function actionUpdateWikipediaTags(graph) { + actions.forEach(function (action) { + graph = action(graph); + }); + return graph; + }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor + // changeTags() is not intended to be called asynchronously + }); + } + + function setLabelForEntity() { + var label = ''; + + if (_wikidataEntity) { + label = entityPropertyForDisplay(_wikidataEntity, 'labels'); + + if (label.length === 0) { + label = _wikidataEntity.id.toString(); + } + } + + utilGetSetValue(_searchInput, label); + } + + wiki.tags = function (tags) { + var isMixed = Array.isArray(tags[field.key]); + + _searchInput.attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null).attr('placeholder', isMixed ? _t('inspector.multiple_values') : '').classed('mixed', isMixed); + + _qid = typeof tags[field.key] === 'string' && tags[field.key] || ''; + + if (!/^Q[0-9]*$/.test(_qid)) { + // not a proper QID + unrecognized(); + return; + } // QID value in correct format + + + _wikiURL = 'https://wikidata.org/wiki/' + _qid; + wikidata.entityByQID(_qid, function (err, entity) { + if (err) { + unrecognized(); + return; + } + + _wikidataEntity = entity; + setLabelForEntity(); + var description = entityPropertyForDisplay(entity, 'descriptions'); + + _selection.select('button.wiki-link').classed('disabled', false); + + _selection.select('.preset-wikidata-description').style('display', function () { + return description.length > 0 ? 'flex' : 'none'; + }).select('input').attr('value', description); + + _selection.select('.preset-wikidata-identifier').style('display', function () { + return entity.id ? 'flex' : 'none'; + }).select('input').attr('value', entity.id); + }); // not a proper QID + + function unrecognized() { + _wikidataEntity = null; + setLabelForEntity(); + + _selection.select('.preset-wikidata-description').style('display', 'none'); + + _selection.select('.preset-wikidata-identifier').style('display', 'none'); + + _selection.select('button.wiki-link').classed('disabled', true); + + if (_qid && _qid !== '') { + _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid; + } else { + _wikiURL = ''; + } + } + }; + + function entityPropertyForDisplay(wikidataEntity, propKey) { + if (!wikidataEntity[propKey]) return ''; + var propObj = wikidataEntity[propKey]; + var langKeys = Object.keys(propObj); + if (langKeys.length === 0) return ''; // sorted by priority, since we want to show the user's language first if possible + + var langs = wikidata.languagesToQuery(); + + for (var i in langs) { + var lang = langs[i]; + var valueObj = propObj[lang]; + if (valueObj && valueObj.value && valueObj.value.length > 0) return valueObj.value; + } // default to any available value + + + return propObj[langKeys[0]].value; + } + + wiki.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + _entityIDs = val; + return wiki; + }; + + wiki.focus = function () { + _searchInput.node().focus(); + }; + + return utilRebind(wiki, dispatch, 'on'); + } + + function uiFieldWikipedia(field, context) { + var _arguments = arguments; + var dispatch = dispatch$8('change'); + var wikipedia = services.wikipedia; + var wikidata = services.wikidata; + + var _langInput = select(null); + + var _titleInput = select(null); + + var _wikiURL = ''; + + var _entityIDs; + + var _tags; + + var _dataWikipedia = []; + _mainFileFetcher.get('wmf_sitematrix').then(function (d) { + _dataWikipedia = d; + if (_tags) updateForTags(_tags); + })["catch"](function () { + /* ignore */ + }); + var langCombo = uiCombobox(context, 'wikipedia-lang').fetcher(function (value, callback) { + var v = value.toLowerCase(); + callback(_dataWikipedia.filter(function (d) { + return d[0].toLowerCase().indexOf(v) >= 0 || d[1].toLowerCase().indexOf(v) >= 0 || d[2].toLowerCase().indexOf(v) >= 0; + }).map(function (d) { + return { + value: d[1] + }; + })); + }); + var titleCombo = uiCombobox(context, 'wikipedia-title').fetcher(function (value, callback) { + if (!value) { + value = ''; + + for (var i in _entityIDs) { + var entity = context.hasEntity(_entityIDs[i]); + + if (entity.tags.name) { + value = entity.tags.name; + break; + } + } + } + + var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions; + searchfn(language()[2], value, function (query, data) { + callback(data.map(function (d) { + return { + value: d + }; + })); + }); + }); + + function wiki(selection) { + var wrap = selection.selectAll('.form-field-input-wrap').data([0]); + wrap = wrap.enter().append('div').attr('class', "form-field-input-wrap form-field-input-".concat(field.type)).merge(wrap); + var langContainer = wrap.selectAll('.wiki-lang-container').data([0]); + langContainer = langContainer.enter().append('div').attr('class', 'wiki-lang-container').merge(langContainer); + _langInput = langContainer.selectAll('input.wiki-lang').data([0]); + _langInput = _langInput.enter().append('input').attr('type', 'text').attr('class', 'wiki-lang').attr('placeholder', _t('translate.localized_translation_language')).call(utilNoAuto).call(langCombo).merge(_langInput); + + _langInput.on('blur', changeLang).on('change', changeLang); + + var titleContainer = wrap.selectAll('.wiki-title-container').data([0]); + titleContainer = titleContainer.enter().append('div').attr('class', 'wiki-title-container').merge(titleContainer); + _titleInput = titleContainer.selectAll('input.wiki-title').data([0]); + _titleInput = _titleInput.enter().append('input').attr('type', 'text').attr('class', 'wiki-title').attr('id', field.domId).call(utilNoAuto).call(titleCombo).merge(_titleInput); + + _titleInput.on('blur', function () { + change(true); + }).on('change', function () { + change(false); + }); + + var link = titleContainer.selectAll('.wiki-link').data([0]); + link = link.enter().append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', { + domain: 'wikipedia.org' + })).call(svgIcon('#iD-icon-out-link')).merge(link); + link.on('click', function (d3_event) { + d3_event.preventDefault(); + if (_wikiURL) window.open(_wikiURL, '_blank'); + }); + } + + function defaultLanguageInfo(skipEnglishFallback) { + var langCode = _mainLocalizer.languageCode().toLowerCase(); + + for (var i in _dataWikipedia) { + var d = _dataWikipedia[i]; // default to the language of iD's current locale + + if (d[2] === langCode) return d; + } // fallback to English + + + return skipEnglishFallback ? ['', '', ''] : ['English', 'English', 'en']; + } + + function language(skipEnglishFallback) { + var value = utilGetSetValue(_langInput).toLowerCase(); + + for (var i in _dataWikipedia) { + var d = _dataWikipedia[i]; // return the language already set in the UI, if supported + + if (d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value) return d; + } // fallback to English + + + return defaultLanguageInfo(skipEnglishFallback); + } + + function changeLang() { + utilGetSetValue(_langInput, language()[1]); + change(true); + } + + function change(skipWikidata) { + var value = utilGetSetValue(_titleInput); + var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/); + + var langInfo = m && _dataWikipedia.find(function (d) { + return m[1] === d[2]; + }); + + var syncTags = {}; + + if (langInfo) { + var nativeLangName = langInfo[1]; // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization + + value = decodeURIComponent(m[2]).replace(/_/g, ' '); + + if (m[3]) { + var anchor; // try { + // leave this out for now - #6232 + // Best-effort `anchordecode:` implementation + // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1')); + // } catch (e) { + + anchor = decodeURIComponent(m[3]); // } + + value += '#' + anchor.replace(/_/g, ' '); + } + + value = value.slice(0, 1).toUpperCase() + value.slice(1); + utilGetSetValue(_langInput, nativeLangName); + utilGetSetValue(_titleInput, value); + } + + if (value) { + syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value); + } else { + syncTags.wikipedia = undefined; + } + + dispatch.call('change', this, syncTags); + if (skipWikidata || !value || !language()[2]) return; // attempt asynchronous update of wikidata tag.. + + var initGraph = context.graph(); + var initEntityIDs = _entityIDs; + wikidata.itemsByTitle(language()[2], value, function (err, data) { + if (err || !data || !Object.keys(data).length) return; // If graph has changed, we can't apply this update. + + if (context.graph() !== initGraph) return; + var qids = Object.keys(data); + var value = qids && qids.find(function (id) { + return id.match(/^Q\d+$/); + }); + var actions = initEntityIDs.map(function (entityID) { + var entity = context.entity(entityID).tags; + var currTags = Object.assign({}, entity); // shallow copy + + if (currTags.wikidata !== value) { + currTags.wikidata = value; + return actionChangeTags(entityID, currTags); + } + + return null; + }).filter(Boolean); + if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change + + context.overwrite(function actionUpdateWikidataTags(graph) { + actions.forEach(function (action) { + graph = action(graph); + }); + return graph; + }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor + // changeTags() is not intended to be called asynchronously + }); + } + + wiki.tags = function (tags) { + _tags = tags; + updateForTags(tags); + }; + + function updateForTags(tags) { + var value = typeof tags[field.key] === 'string' ? tags[field.key] : ''; // Expect tag format of `tagLang:tagArticleTitle`, e.g. `fr:Paris`, with + // optional suffix of `#anchor` + + var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/); + var tagLang = m && m[1]; + var tagArticleTitle = m && m[2]; + var anchor = m && m[3]; + + var tagLangInfo = tagLang && _dataWikipedia.find(function (d) { + return tagLang === d[2]; + }); // value in correct format + + + if (tagLangInfo) { + var nativeLangName = tagLangInfo[1]; + utilGetSetValue(_langInput, nativeLangName); + utilGetSetValue(_titleInput, tagArticleTitle + (anchor ? '#' + anchor : '')); + + if (anchor) { + try { + // Best-effort `anchorencode:` implementation + anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.'); + } catch (e) { + anchor = anchor.replace(/ /g, '_'); + } + } + + _wikiURL = 'https://' + tagLang + '.wikipedia.org/wiki/' + tagArticleTitle.replace(/ /g, '_') + (anchor ? '#' + anchor : ''); // unrecognized value format + } else { + utilGetSetValue(_titleInput, value); + + if (value && value !== '') { + utilGetSetValue(_langInput, ''); + var defaultLangInfo = defaultLanguageInfo(); + _wikiURL = "https://".concat(defaultLangInfo[2], ".wikipedia.org/w/index.php?fulltext=1&search=").concat(value); + } else { + var shownOrDefaultLangInfo = language(true + /* skipEnglishFallback */ + ); + utilGetSetValue(_langInput, shownOrDefaultLangInfo[1]); + _wikiURL = ''; + } + } + } + + wiki.entityIDs = function (val) { + if (!_arguments.length) return _entityIDs; + _entityIDs = val; + return wiki; + }; + + wiki.focus = function () { + _titleInput.node().focus(); + }; + + return utilRebind(wiki, dispatch, 'on'); + } + uiFieldWikipedia.supportsMultiselection = false; + + var uiFields = { + access: uiFieldAccess, + address: uiFieldAddress, + check: uiFieldCheck, + combo: uiFieldCombo, + cycleway: uiFieldCycleway, + defaultCheck: uiFieldCheck, + email: uiFieldText, + identifier: uiFieldText, + lanes: uiFieldLanes, + localized: uiFieldLocalized, + roadheight: uiFieldRoadheight, + roadspeed: uiFieldRoadspeed, + manyCombo: uiFieldCombo, + multiCombo: uiFieldCombo, + networkCombo: uiFieldCombo, + number: uiFieldText, + onewayCheck: uiFieldCheck, + radio: uiFieldRadio, + restrictions: uiFieldRestrictions, + semiCombo: uiFieldCombo, + structureRadio: uiFieldRadio, + tel: uiFieldText, + text: uiFieldText, + textarea: uiFieldTextarea, + typeCombo: uiFieldCombo, + url: uiFieldText, + wikidata: uiFieldWikidata, + wikipedia: uiFieldWikipedia + }; + + function uiField(context, presetField, entityIDs, options) { + options = Object.assign({ + show: true, + wrap: true, + remove: true, + revert: true, + info: true + }, options); + var dispatch = dispatch$8('change', 'revert'); + var field = Object.assign({}, presetField); // shallow copy + + field.domId = utilUniqueDomId('form-field-' + field.safeid); + var _show = options.show; + var _state = ''; + var _tags = {}; + + var _entityExtent; + + if (entityIDs && entityIDs.length) { + _entityExtent = entityIDs.reduce(function (extent, entityID) { + var entity = context.graph().entity(entityID); + return extent.extend(entity.extent(context.graph())); + }, geoExtent()); + } + + var _locked = false; + + var _lockedTip = uiTooltip().title(_t.html('inspector.lock.suggestion', { + label: field.label + })).placement('bottom'); + + field.keys = field.keys || [field.key]; // only create the fields that are actually being shown + + if (_show && !field.impl) { + createField(); + } // Creates the field.. This is done lazily, + // once we know that the field will be shown. + + + function createField() { + field.impl = uiFields[field.type](field, context).on('change', function (t, onInput) { + dispatch.call('change', field, t, onInput); + }); + + if (entityIDs) { + field.entityIDs = entityIDs; // if this field cares about the entities, pass them along + + if (field.impl.entityIDs) { + field.impl.entityIDs(entityIDs); + } + } + } + + function isModified() { + if (!entityIDs || !entityIDs.length) return false; + return entityIDs.some(function (entityID) { + var original = context.graph().base().entities[entityID]; + var latest = context.graph().entity(entityID); + return field.keys.some(function (key) { + return original ? latest.tags[key] !== original.tags[key] : latest.tags[key]; + }); + }); + } + + function tagsContainFieldKey() { + return field.keys.some(function (key) { + if (field.type === 'multiCombo') { + for (var tagKey in _tags) { + if (tagKey.indexOf(key) === 0) { + return true; + } + } + + return false; + } + + return _tags[key] !== undefined; + }); + } + + function revert(d3_event, d) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + if (!entityIDs || _locked) return; + dispatch.call('revert', d, d.keys); + } + + function remove(d3_event, d) { + d3_event.stopPropagation(); + d3_event.preventDefault(); + if (_locked) return; + var t = {}; + d.keys.forEach(function (key) { + t[key] = undefined; + }); + dispatch.call('change', d, t); + } + + field.render = function (selection) { + var container = selection.selectAll('.form-field').data([field]); // Enter + + var enter = container.enter().append('div').attr('class', function (d) { + return 'form-field form-field-' + d.safeid; + }).classed('nowrap', !options.wrap); + + if (options.wrap) { + var labelEnter = enter.append('label').attr('class', 'field-label').attr('for', function (d) { + return d.domId; + }); + var textEnter = labelEnter.append('span').attr('class', 'label-text'); + textEnter.append('span').attr('class', 'label-textvalue').html(function (d) { + return d.label(); + }); + textEnter.append('span').attr('class', 'label-textannotation'); + + if (options.remove) { + labelEnter.append('button').attr('class', 'remove-icon').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete')); + } + + if (options.revert) { + labelEnter.append('button').attr('class', 'modified-icon').attr('title', _t('icons.undo')).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo')); + } + } // Update + + + container = container.merge(enter); + container.select('.field-label > .remove-icon') // propagate bound data + .on('click', remove); + container.select('.field-label > .modified-icon') // propagate bound data + .on('click', revert); + container.each(function (d) { + var selection = select(this); + + if (!d.impl) { + createField(); + } + + var reference, help; // instantiate field help + + if (options.wrap && field.type === 'restrictions') { + help = uiFieldHelp(context, 'restrictions'); + } // instantiate tag reference + + + if (options.wrap && options.info) { + var referenceKey = d.key || ''; + + if (d.type === 'multiCombo') { + // lookup key without the trailing ':' + referenceKey = referenceKey.replace(/:$/, ''); + } + + reference = uiTagReference(d.reference || { + key: referenceKey + }); + + if (_state === 'hover') { + reference.showing(false); + } + } + + selection.call(d.impl); // add field help components + + if (help) { + selection.call(help.body).select('.field-label').call(help.button); + } // add tag reference components + + + if (reference) { + selection.call(reference.body).select('.field-label').call(reference.button); + } + + d.impl.tags(_tags); + }); + container.classed('locked', _locked).classed('modified', isModified()).classed('present', tagsContainFieldKey()); // show a tip and lock icon if the field is locked + + var annotation = container.selectAll('.field-label .label-textannotation'); + var icon = annotation.selectAll('.icon').data(_locked ? [0] : []); + icon.exit().remove(); + icon.enter().append('svg').attr('class', 'icon').append('use').attr('xlink:href', '#fas-lock'); + container.call(_locked ? _lockedTip : _lockedTip.destroy); + }; + + field.state = function (val) { + if (!arguments.length) return _state; + _state = val; + return field; + }; + + field.tags = function (val) { + if (!arguments.length) return _tags; + _tags = val; + + if (tagsContainFieldKey() && !_show) { + // always show a field if it has a value to display + _show = true; + + if (!field.impl) { + createField(); + } + } + + return field; + }; + + field.locked = function (val) { + if (!arguments.length) return _locked; + _locked = val; + return field; + }; + + field.show = function () { + _show = true; + + if (!field.impl) { + createField(); + } + + if (field["default"] && field.key && _tags[field.key] !== field["default"]) { + var t = {}; + t[field.key] = field["default"]; + dispatch.call('change', this, t); + } + }; // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown + + + field.isShown = function () { + return _show; + }; // An allowed field can appear in the UI or in the 'Add field' dropdown. + // A non-allowed field is hidden from the user altogether + + + field.isAllowed = function () { + if (entityIDs && entityIDs.length > 1 && uiFields[field.type].supportsMultiselection === false) return false; + if (field.geometry && !entityIDs.every(function (entityID) { + return field.matchGeometry(context.graph().geometry(entityID)); + })) return false; + + if (entityIDs && _entityExtent && field.locationSetID) { + // is field allowed in this location? + var validLocations = _mainLocations.locationsAt(_entityExtent.center()); + if (!validLocations[field.locationSetID]) return false; + } + + var prerequisiteTag = field.prerequisiteTag; + + if (entityIDs && !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present + prerequisiteTag) { + if (!entityIDs.every(function (entityID) { + var entity = context.graph().entity(entityID); + + if (prerequisiteTag.key) { + var value = entity.tags[prerequisiteTag.key]; + if (!value) return false; + + if (prerequisiteTag.valueNot) { + return prerequisiteTag.valueNot !== value; + } + + if (prerequisiteTag.value) { + return prerequisiteTag.value === value; + } + } else if (prerequisiteTag.keyNot) { + if (entity.tags[prerequisiteTag.keyNot]) return false; + } + + return true; + })) return false; + } + + return true; + }; + + field.focus = function () { + if (field.impl) { + field.impl.focus(); + } + }; + + return utilRebind(field, dispatch, 'on'); + } + + function uiFormFields(context) { + var moreCombo = uiCombobox(context, 'more-fields').minItems(1); + var _fieldsArr = []; + var _lastPlaceholder = ''; + var _state = ''; + var _klass = ''; + + function formFields(selection) { + var allowedFields = _fieldsArr.filter(function (field) { + return field.isAllowed(); + }); + + var shown = allowedFields.filter(function (field) { + return field.isShown(); + }); + var notShown = allowedFields.filter(function (field) { + return !field.isShown(); + }); + var container = selection.selectAll('.form-fields-container').data([0]); + container = container.enter().append('div').attr('class', 'form-fields-container ' + (_klass || '')).merge(container); + var fields = container.selectAll('.wrap-form-field').data(shown, function (d) { + return d.id + (d.entityIDs ? d.entityIDs.join() : ''); + }); + fields.exit().remove(); // Enter + + var enter = fields.enter().append('div').attr('class', function (d) { + return 'wrap-form-field wrap-form-field-' + d.safeid; + }); // Update + + fields = fields.merge(enter); + fields.order().each(function (d) { + select(this).call(d.render); + }); + var titles = []; + var moreFields = notShown.map(function (field) { + var title = field.title(); + titles.push(title); + var terms = field.terms(); + if (field.key) terms.push(field.key); + if (field.keys) terms = terms.concat(field.keys); + return { + display: field.label(), + value: title, + title: title, + field: field, + terms: terms + }; + }); + var placeholder = titles.slice(0, 3).join(', ') + (titles.length > 3 ? '…' : ''); + var more = selection.selectAll('.more-fields').data(_state === 'hover' || moreFields.length === 0 ? [] : [0]); + more.exit().remove(); + var moreEnter = more.enter().append('div').attr('class', 'more-fields').append('label'); + moreEnter.append('span').html(_t.html('inspector.add_fields')); + more = moreEnter.merge(more); + var input = more.selectAll('.value').data([0]); + input.exit().remove(); + input = input.enter().append('input').attr('class', 'value').attr('type', 'text').attr('placeholder', placeholder).call(utilNoAuto).merge(input); + input.call(utilGetSetValue, '').call(moreCombo.data(moreFields).on('accept', function (d) { + if (!d) return; // user entered something that was not matched + + var field = d.field; + field.show(); + selection.call(formFields); // rerender + + field.focus(); + })); // avoid updating placeholder excessively (triggers style recalc) + + if (_lastPlaceholder !== placeholder) { + input.attr('placeholder', placeholder); + _lastPlaceholder = placeholder; + } + } + + formFields.fieldsArr = function (val) { + if (!arguments.length) return _fieldsArr; + _fieldsArr = val || []; + return formFields; + }; + + formFields.state = function (val) { + if (!arguments.length) return _state; + _state = val; + return formFields; + }; + + formFields.klass = function (val) { + if (!arguments.length) return _klass; + _klass = val; + return formFields; + }; + + return formFields; + } + + function uiSectionPresetFields(context) { + var section = uiSection('preset-fields', context).label(_t.html('inspector.fields')).disclosureContent(renderDisclosureContent); + var dispatch = dispatch$8('change', 'revert'); + var formFields = uiFormFields(context); + + var _state; + + var _fieldsArr; + + var _presets = []; + + var _tags; + + var _entityIDs; + + function renderDisclosureContent(selection) { + if (!_fieldsArr) { + var graph = context.graph(); + var geometries = Object.keys(_entityIDs.reduce(function (geoms, entityID) { + geoms[graph.entity(entityID).geometry(graph)] = true; + return geoms; + }, {})); + var presetsManager = _mainPresetIndex; + var allFields = []; + var allMoreFields = []; + var sharedTotalFields; + + _presets.forEach(function (preset) { + var fields = preset.fields(); + var moreFields = preset.moreFields(); + allFields = utilArrayUnion(allFields, fields); + allMoreFields = utilArrayUnion(allMoreFields, moreFields); + + if (!sharedTotalFields) { + sharedTotalFields = utilArrayUnion(fields, moreFields); + } else { + sharedTotalFields = sharedTotalFields.filter(function (field) { + return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1; + }); + } + }); + + var sharedFields = allFields.filter(function (field) { + return sharedTotalFields.indexOf(field) !== -1; + }); + var sharedMoreFields = allMoreFields.filter(function (field) { + return sharedTotalFields.indexOf(field) !== -1; + }); + _fieldsArr = []; + sharedFields.forEach(function (field) { + if (field.matchAllGeometry(geometries)) { + _fieldsArr.push(uiField(context, field, _entityIDs)); + } + }); + var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]); + + if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) { + _fieldsArr.push(uiField(context, presetsManager.field('restrictions'), _entityIDs)); + } + + var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal()); + additionalFields.sort(function (field1, field2) { + return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode()); + }); + additionalFields.forEach(function (field) { + if (sharedFields.indexOf(field) === -1 && field.matchAllGeometry(geometries)) { + _fieldsArr.push(uiField(context, field, _entityIDs, { + show: false + })); + } + }); + + _fieldsArr.forEach(function (field) { + field.on('change', function (t, onInput) { + dispatch.call('change', field, _entityIDs, t, onInput); + }).on('revert', function (keys) { + dispatch.call('revert', field, keys); + }); + }); + } + + _fieldsArr.forEach(function (field) { + field.state(_state).tags(_tags); + }); + + selection.call(formFields.fieldsArr(_fieldsArr).state(_state).klass('grouped-items-area')); + selection.selectAll('.wrap-form-field input').on('keydown', function (d3_event) { + // if user presses enter, and combobox is not active, accept edits.. + if (d3_event.keyCode === 13 && // ↩ Return + context.container().select('.combobox').empty()) { + context.enter(modeBrowse(context)); + } + }); + } + + section.presets = function (val) { + if (!arguments.length) return _presets; + + if (!_presets || !val || !utilArrayIdentical(_presets, val)) { + _presets = val; + _fieldsArr = null; + } + + return section; + }; + + section.state = function (val) { + if (!arguments.length) return _state; + _state = val; + return section; + }; + + section.tags = function (val) { + if (!arguments.length) return _tags; + _tags = val; // Don't reset _fieldsArr here. + + return section; + }; + + section.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + + if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) { + _entityIDs = val; + _fieldsArr = null; + } + + return section; + }; + + return utilRebind(section, dispatch, 'on'); + } + + function uiSectionRawMemberEditor(context) { + var section = uiSection('raw-member-editor', context).shouldDisplay(function () { + if (!_entityIDs || _entityIDs.length !== 1) return false; + var entity = context.hasEntity(_entityIDs[0]); + return entity && entity.type === 'relation'; + }).label(function () { + var entity = context.hasEntity(_entityIDs[0]); + if (!entity) return ''; + var gt = entity.members.length > _maxMembers ? '>' : ''; + var count = gt + entity.members.slice(0, _maxMembers).length; + return _t('inspector.title_count', { + title: _t.html('inspector.members'), + count: count + }); + }).disclosureContent(renderDisclosureContent); + var taginfo = services.taginfo; + + var _entityIDs; + + var _maxMembers = 1000; + + function downloadMember(d3_event, d) { + d3_event.preventDefault(); // display the loading indicator + + select(this.parentNode).classed('tag-reference-loading', true); + context.loadEntity(d.id, function () { + section.reRender(); + }); + } + + function zoomToMember(d3_event, d) { + d3_event.preventDefault(); + var entity = context.entity(d.id); + context.map().zoomToEase(entity); // highlight the feature in case it wasn't previously on-screen + + utilHighlightEntities([d.id], true, context); + } + + function selectMember(d3_event, d) { + d3_event.preventDefault(); // remove the hover-highlight styling + + utilHighlightEntities([d.id], false, context); + var entity = context.entity(d.id); + var mapExtent = context.map().extent(); + + if (!entity.intersects(mapExtent, context.graph())) { + // zoom to the entity if its extent is not visible now + context.map().zoomToEase(entity); + } + + context.enter(modeSelect(context, [d.id])); + } + + function changeRole(d3_event, d) { + var oldRole = d.role; + var newRole = context.cleanRelationRole(select(this).property('value')); + + if (oldRole !== newRole) { + var member = { + id: d.id, + type: d.type, + role: newRole + }; + context.perform(actionChangeMember(d.relation.id, member, d.index), _t('operations.change_role.annotation', { + n: 1 + })); + context.validator().validate(); + } + } + + function deleteMember(d3_event, d) { + // remove the hover-highlight styling + utilHighlightEntities([d.id], false, context); + context.perform(actionDeleteMember(d.relation.id, d.index), _t('operations.delete_member.annotation', { + n: 1 + })); + + if (!context.hasEntity(d.relation.id)) { + // Removing the last member will also delete the relation. + // If this happens we need to exit the selection mode + context.enter(modeBrowse(context)); + } else { + // Changing the mode also runs `validate`, but otherwise we need to + // rerun it manually + context.validator().validate(); + } + } + + function renderDisclosureContent(selection) { + var entityID = _entityIDs[0]; + var memberships = []; + var entity = context.entity(entityID); + entity.members.slice(0, _maxMembers).forEach(function (member, index) { + memberships.push({ + index: index, + id: member.id, + type: member.type, + role: member.role, + relation: entity, + member: context.hasEntity(member.id), + domId: utilUniqueDomId(entityID + '-member-' + index) + }); + }); + var list = selection.selectAll('.member-list').data([0]); + list = list.enter().append('ul').attr('class', 'member-list').merge(list); + var items = list.selectAll('li').data(memberships, function (d) { + return osmEntity.key(d.relation) + ',' + d.index + ',' + (d.member ? osmEntity.key(d.member) : 'incomplete'); + }); + items.exit().each(unbind).remove(); + var itemsEnter = items.enter().append('li').attr('class', 'member-row form-field').classed('member-incomplete', function (d) { + return !d.member; + }); + itemsEnter.each(function (d) { + var item = select(this); + var label = item.append('label').attr('class', 'field-label').attr('for', d.domId); + + if (d.member) { + // highlight the member feature in the map while hovering on the list item + item.on('mouseover', function () { + utilHighlightEntities([d.id], true, context); + }).on('mouseout', function () { + utilHighlightEntities([d.id], false, context); + }); + var labelLink = label.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectMember); + labelLink.append('span').attr('class', 'member-entity-type').html(function (d) { + var matched = _mainPresetIndex.match(d.member, context.graph()); + return matched && matched.name() || utilDisplayType(d.member.id); + }); + labelLink.append('span').attr('class', 'member-entity-name').html(function (d) { + return utilDisplayName(d.member); + }); + label.append('button').attr('title', _t('icons.remove')).attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')); + label.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToMember); + } else { + var labelText = label.append('span').attr('class', 'label-text'); + labelText.append('span').attr('class', 'member-entity-type').html(_t.html('inspector.' + d.type, { + id: d.id + })); + labelText.append('span').attr('class', 'member-entity-name').html(_t.html('inspector.incomplete', { + id: d.id + })); + label.append('button').attr('class', 'member-download').attr('title', _t('icons.download')).call(svgIcon('#iD-icon-load')).on('click', downloadMember); + } + }); + var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member'); + wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) { + return d.domId; + }).property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto); + + if (taginfo) { + wrapEnter.each(bindTypeahead); + } // update + + + items = items.merge(itemsEnter).order(); + items.select('input.member-role').property('value', function (d) { + return d.role; + }).on('blur', changeRole).on('change', changeRole); + items.select('button.member-delete').on('click', deleteMember); + var dragOrigin, targetIndex; + items.call(d3_drag().on('start', function (d3_event) { + dragOrigin = { + x: d3_event.x, + y: d3_event.y + }; + targetIndex = null; + }).on('drag', function (d3_event) { + var x = d3_event.x - dragOrigin.x, + y = d3_event.y - dragOrigin.y; + if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold + Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return; + var index = items.nodes().indexOf(this); + select(this).classed('dragging', true); + targetIndex = null; + selection.selectAll('li.member-row').style('transform', function (d2, index2) { + var node = select(this).node(); + + if (index === index2) { + return 'translate(' + x + 'px, ' + y + 'px)'; + } else if (index2 > index && d3_event.y > node.offsetTop) { + if (targetIndex === null || index2 > targetIndex) { + targetIndex = index2; + } + + return 'translateY(-100%)'; + } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) { + if (targetIndex === null || index2 < targetIndex) { + targetIndex = index2; + } + + return 'translateY(100%)'; + } + + return null; + }); + }).on('end', function (d3_event, d) { + if (!select(this).classed('dragging')) return; + var index = items.nodes().indexOf(this); + select(this).classed('dragging', false); + selection.selectAll('li.member-row').style('transform', null); + + if (targetIndex !== null) { + // dragged to a new position, reorder + context.perform(actionMoveMember(d.relation.id, index, targetIndex), _t('operations.reorder_members.annotation')); + context.validator().validate(); + } + })); + + function bindTypeahead(d) { + var row = select(this); + var role = row.selectAll('input.member-role'); + var origValue = role.property('value'); + + function sort(value, data) { + var sameletter = []; + var other = []; + + for (var i = 0; i < data.length; i++) { + if (data[i].value.substring(0, value.length) === value) { + sameletter.push(data[i]); + } else { + other.push(data[i]); + } + } + + return sameletter.concat(other); + } + + role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) { + // The `geometry` param is used in the `taginfo.js` interface for + // filtering results, as a key into the `tag_members_fractions` + // object. If we don't know the geometry because the member is + // not yet downloaded, it's ok to guess based on type. + var geometry; + + if (d.member) { + geometry = context.graph().geometry(d.member.id); + } else if (d.type === 'relation') { + geometry = 'relation'; + } else if (d.type === 'way') { + geometry = 'line'; + } else { + geometry = 'point'; + } + + var rtype = entity.tags.type; + taginfo.roles({ + debounce: true, + rtype: rtype || '', + geometry: geometry, + query: role + }, function (err, data) { + if (!err) callback(sort(role, data)); + }); + }).on('cancel', function () { + role.property('value', origValue); + })); + } + + function unbind() { + var row = select(this); + row.selectAll('input.member-role').call(uiCombobox.off, context); + } + } + + section.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + _entityIDs = val; + return section; + }; + + return section; + } + + function actionDeleteMembers(relationId, memberIndexes) { + return function (graph) { + // Remove the members in descending order so removals won't shift what members + // are at the remaining indexes + memberIndexes.sort(function (a, b) { + return b - a; + }); + + for (var i in memberIndexes) { + graph = actionDeleteMember(relationId, memberIndexes[i])(graph); + } + + return graph; + }; + } + + function uiSectionRawMembershipEditor(context) { + var section = uiSection('raw-membership-editor', context).shouldDisplay(function () { + return _entityIDs && _entityIDs.length; + }).label(function () { + var parents = getSharedParentRelations(); + var gt = parents.length > _maxMemberships ? '>' : ''; + var count = gt + parents.slice(0, _maxMemberships).length; + return _t('inspector.title_count', { + title: _t.html('inspector.relations'), + count: count + }); + }).disclosureContent(renderDisclosureContent); + var taginfo = services.taginfo; + var nearbyCombo = uiCombobox(context, 'parent-relation').minItems(1).fetcher(fetchNearbyRelations).itemsMouseEnter(function (d3_event, d) { + if (d.relation) utilHighlightEntities([d.relation.id], true, context); + }).itemsMouseLeave(function (d3_event, d) { + if (d.relation) utilHighlightEntities([d.relation.id], false, context); + }); + var _inChange = false; + var _entityIDs = []; + + var _showBlank; + + var _maxMemberships = 1000; + + function getSharedParentRelations() { + var parents = []; + + for (var i = 0; i < _entityIDs.length; i++) { + var entity = context.graph().hasEntity(_entityIDs[i]); + if (!entity) continue; + + if (i === 0) { + parents = context.graph().parentRelations(entity); + } else { + parents = utilArrayIntersection(parents, context.graph().parentRelations(entity)); + } + + if (!parents.length) break; + } + + return parents; + } + + function getMemberships() { + var memberships = []; + var relations = getSharedParentRelations().slice(0, _maxMemberships); + var isMultiselect = _entityIDs.length > 1; + var i, relation, membership, index, member, indexedMember; + + for (i = 0; i < relations.length; i++) { + relation = relations[i]; + membership = { + relation: relation, + members: [], + hash: osmEntity.key(relation) + }; + + for (index = 0; index < relation.members.length; index++) { + member = relation.members[index]; + + if (_entityIDs.indexOf(member.id) !== -1) { + indexedMember = Object.assign({}, member, { + index: index + }); + membership.members.push(indexedMember); + membership.hash += ',' + index.toString(); + + if (!isMultiselect) { + // For single selections, list one entry per membership per relation. + // For multiselections, list one entry per relation. + memberships.push(membership); + membership = { + relation: relation, + members: [], + hash: osmEntity.key(relation) + }; + } + } + } + + if (membership.members.length) memberships.push(membership); + } + + memberships.forEach(function (membership) { + membership.domId = utilUniqueDomId('membership-' + membership.relation.id); + var roles = []; + membership.members.forEach(function (member) { + if (roles.indexOf(member.role) === -1) roles.push(member.role); + }); + membership.role = roles.length === 1 ? roles[0] : roles; + }); + return memberships; + } + + function selectRelation(d3_event, d) { + d3_event.preventDefault(); // remove the hover-highlight styling + + utilHighlightEntities([d.relation.id], false, context); + context.enter(modeSelect(context, [d.relation.id])); + } + + function zoomToRelation(d3_event, d) { + d3_event.preventDefault(); + var entity = context.entity(d.relation.id); + context.map().zoomToEase(entity); // highlight the relation in case it wasn't previously on-screen + + utilHighlightEntities([d.relation.id], true, context); + } + + function changeRole(d3_event, d) { + if (d === 0) return; // called on newrow (shouldn't happen) + + if (_inChange) return; // avoid accidental recursive call #5731 + + var newRole = context.cleanRelationRole(select(this).property('value')); + if (!newRole.trim() && typeof d.role !== 'string') return; + var membersToUpdate = d.members.filter(function (member) { + return member.role !== newRole; + }); + + if (membersToUpdate.length) { + _inChange = true; + context.perform(function actionChangeMemberRoles(graph) { + membersToUpdate.forEach(function (member) { + var newMember = Object.assign({}, member, { + role: newRole + }); + delete newMember.index; + graph = actionChangeMember(d.relation.id, newMember, member.index)(graph); + }); + return graph; + }, _t('operations.change_role.annotation', { + n: membersToUpdate.length + })); + context.validator().validate(); + } + + _inChange = false; + } + + function addMembership(d, role) { + this.blur(); // avoid keeping focus on the button + + _showBlank = false; + + function actionAddMembers(relationId, ids, role) { + return function (graph) { + for (var i in ids) { + var member = { + id: ids[i], + type: graph.entity(ids[i]).type, + role: role + }; + graph = actionAddMember(relationId, member)(graph); + } + + return graph; + }; + } + + if (d.relation) { + context.perform(actionAddMembers(d.relation.id, _entityIDs, role), _t('operations.add_member.annotation', { + n: _entityIDs.length + })); + context.validator().validate(); + } else { + var relation = osmRelation(); + context.perform(actionAddEntity(relation), actionAddMembers(relation.id, _entityIDs, role), _t('operations.add.annotation.relation')); // changing the mode also runs `validate` + + context.enter(modeSelect(context, [relation.id]).newFeature(true)); + } + } + + function deleteMembership(d3_event, d) { + this.blur(); // avoid keeping focus on the button + + if (d === 0) return; // called on newrow (shouldn't happen) + // remove the hover-highlight styling + + utilHighlightEntities([d.relation.id], false, context); + var indexes = d.members.map(function (member) { + return member.index; + }); + context.perform(actionDeleteMembers(d.relation.id, indexes), _t('operations.delete_member.annotation', { + n: _entityIDs.length + })); + context.validator().validate(); + } + + function fetchNearbyRelations(q, callback) { + var newRelation = { + relation: null, + value: _t('inspector.new_relation'), + display: _t.html('inspector.new_relation') + }; + var entityID = _entityIDs[0]; + var result = []; + var graph = context.graph(); + + function baseDisplayLabel(entity) { + var matched = _mainPresetIndex.match(entity, graph); + var presetName = matched && matched.name() || _t('inspector.relation'); + var entityName = utilDisplayName(entity) || ''; + return presetName + ' ' + entityName; + } + + var explicitRelation = q && context.hasEntity(q.toLowerCase()); + + if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) { + // loaded relation is specified explicitly, only show that + result.push({ + relation: explicitRelation, + value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id + }); + } else { + context.history().intersects(context.map().extent()).forEach(function (entity) { + if (entity.type !== 'relation' || entity.id === entityID) return; + var value = baseDisplayLabel(entity); + if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) return; + result.push({ + relation: entity, + value: value + }); + }); + result.sort(function (a, b) { + return osmRelation.creationOrder(a.relation, b.relation); + }); // Dedupe identical names by appending relation id - see #2891 + + var dupeGroups = Object.values(utilArrayGroupBy(result, 'value')).filter(function (v) { + return v.length > 1; + }); + dupeGroups.forEach(function (group) { + group.forEach(function (obj) { + obj.value += ' ' + obj.relation.id; + }); + }); + } + + result.forEach(function (obj) { + obj.title = obj.value; + }); + result.unshift(newRelation); + callback(result); + } + + function renderDisclosureContent(selection) { + var memberships = getMemberships(); + var list = selection.selectAll('.member-list').data([0]); + list = list.enter().append('ul').attr('class', 'member-list').merge(list); + var items = list.selectAll('li.member-row-normal').data(memberships, function (d) { + return d.hash; + }); + items.exit().each(unbind).remove(); // Enter + + var itemsEnter = items.enter().append('li').attr('class', 'member-row member-row-normal form-field'); // highlight the relation in the map while hovering on the list item + + itemsEnter.on('mouseover', function (d3_event, d) { + utilHighlightEntities([d.relation.id], true, context); + }).on('mouseout', function (d3_event, d) { + utilHighlightEntities([d.relation.id], false, context); + }); + var labelEnter = itemsEnter.append('label').attr('class', 'field-label').attr('for', function (d) { + return d.domId; + }); + var labelLink = labelEnter.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectRelation); + labelLink.append('span').attr('class', 'member-entity-type').html(function (d) { + var matched = _mainPresetIndex.match(d.relation, context.graph()); + return matched && matched.name() || _t('inspector.relation'); + }); + labelLink.append('span').attr('class', 'member-entity-name').html(function (d) { + return utilDisplayName(d.relation); + }); + labelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', deleteMembership); + labelEnter.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToRelation); + var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member'); + wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) { + return d.domId; + }).property('type', 'text').property('value', function (d) { + return typeof d.role === 'string' ? d.role : ''; + }).attr('title', function (d) { + return Array.isArray(d.role) ? d.role.filter(Boolean).join('\n') : d.role; + }).attr('placeholder', function (d) { + return Array.isArray(d.role) ? _t('inspector.multiple_roles') : _t('inspector.role'); + }).classed('mixed', function (d) { + return Array.isArray(d.role); + }).call(utilNoAuto).on('blur', changeRole).on('change', changeRole); + + if (taginfo) { + wrapEnter.each(bindTypeahead); + } + + var newMembership = list.selectAll('.member-row-new').data(_showBlank ? [0] : []); // Exit + + newMembership.exit().remove(); // Enter + + var newMembershipEnter = newMembership.enter().append('li').attr('class', 'member-row member-row-new form-field'); + var newLabelEnter = newMembershipEnter.append('label').attr('class', 'field-label'); + newLabelEnter.append('input').attr('placeholder', _t('inspector.choose_relation')).attr('type', 'text').attr('class', 'member-entity-input').call(utilNoAuto); + newLabelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', function () { + list.selectAll('.member-row-new').remove(); + }); + var newWrapEnter = newMembershipEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member'); + newWrapEnter.append('input').attr('class', 'member-role').property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto); // Update + + newMembership = newMembership.merge(newMembershipEnter); + newMembership.selectAll('.member-entity-input').on('blur', cancelEntity) // if it wasn't accepted normally, cancel it + .call(nearbyCombo.on('accept', acceptEntity).on('cancel', cancelEntity)); // Container for the Add button + + var addRow = selection.selectAll('.add-row').data([0]); // enter + + var addRowEnter = addRow.enter().append('div').attr('class', 'add-row'); + var addRelationButton = addRowEnter.append('button').attr('class', 'add-relation'); + addRelationButton.call(svgIcon('#iD-icon-plus', 'light')); + addRelationButton.call(uiTooltip().title(_t.html('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left')); + addRowEnter.append('div').attr('class', 'space-value'); // preserve space + + addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space + // update + + addRow = addRow.merge(addRowEnter); + addRow.select('.add-relation').on('click', function () { + _showBlank = true; + section.reRender(); + list.selectAll('.member-entity-input').node().focus(); + }); + + function acceptEntity(d) { + if (!d) { + cancelEntity(); + return; + } // remove hover-higlighting + + + if (d.relation) utilHighlightEntities([d.relation.id], false, context); + var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value')); + addMembership(d, role); + } + + function cancelEntity() { + var input = newMembership.selectAll('.member-entity-input'); + input.property('value', ''); // remove hover-higlighting + + context.surface().selectAll('.highlighted').classed('highlighted', false); + } + + function bindTypeahead(d) { + var row = select(this); + var role = row.selectAll('input.member-role'); + var origValue = role.property('value'); + + function sort(value, data) { + var sameletter = []; + var other = []; + + for (var i = 0; i < data.length; i++) { + if (data[i].value.substring(0, value.length) === value) { + sameletter.push(data[i]); + } else { + other.push(data[i]); + } + } + + return sameletter.concat(other); + } + + role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) { + var rtype = d.relation.tags.type; + taginfo.roles({ + debounce: true, + rtype: rtype || '', + geometry: context.graph().geometry(_entityIDs[0]), + query: role + }, function (err, data) { + if (!err) callback(sort(role, data)); + }); + }).on('cancel', function () { + role.property('value', origValue); + })); + } + + function unbind() { + var row = select(this); + row.selectAll('input.member-role').call(uiCombobox.off, context); + } + } + + section.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + _entityIDs = val; + _showBlank = false; + return section; + }; + + return section; + } + + function uiSectionSelectionList(context) { + var _selectedIDs = []; + var section = uiSection('selected-features', context).shouldDisplay(function () { + return _selectedIDs.length > 1; + }).label(function () { + return _t('inspector.title_count', { + title: _t.html('inspector.features'), + count: _selectedIDs.length + }); + }).disclosureContent(renderDisclosureContent); + context.history().on('change.selectionList', function (difference) { + if (difference) { + section.reRender(); + } + }); + + section.entityIDs = function (val) { + if (!arguments.length) return _selectedIDs; + _selectedIDs = val; + return section; + }; + + function selectEntity(d3_event, entity) { + context.enter(modeSelect(context, [entity.id])); + } + + function deselectEntity(d3_event, entity) { + var selectedIDs = _selectedIDs.slice(); + + var index = selectedIDs.indexOf(entity.id); + + if (index > -1) { + selectedIDs.splice(index, 1); + context.enter(modeSelect(context, selectedIDs)); + } + } + + function renderDisclosureContent(selection) { + var list = selection.selectAll('.feature-list').data([0]); + list = list.enter().append('ul').attr('class', 'feature-list').merge(list); + + var entities = _selectedIDs.map(function (id) { + return context.hasEntity(id); + }).filter(Boolean); + + var items = list.selectAll('.feature-list-item').data(entities, osmEntity.key); + items.exit().remove(); // Enter + + var enter = items.enter().append('li').attr('class', 'feature-list-item').each(function (d) { + select(this).on('mouseover', function () { + utilHighlightEntities([d.id], true, context); + }).on('mouseout', function () { + utilHighlightEntities([d.id], false, context); + }); + }); + var label = enter.append('button').attr('class', 'label').on('click', selectEntity); + label.append('span').attr('class', 'entity-geom-icon').call(svgIcon('', 'pre-text')); + label.append('span').attr('class', 'entity-type'); + label.append('span').attr('class', 'entity-name'); + enter.append('button').attr('class', 'close').attr('title', _t('icons.deselect')).on('click', deselectEntity).call(svgIcon('#iD-icon-close')); // Update + + items = items.merge(enter); + items.selectAll('.entity-geom-icon use').attr('href', function () { + var entity = this.parentNode.parentNode.__data__; + return '#iD-icon-' + entity.geometry(context.graph()); + }); + items.selectAll('.entity-type').html(function (entity) { + return _mainPresetIndex.match(entity, context.graph()).name(); + }); + items.selectAll('.entity-name').html(function (d) { + // fetch latest entity + var entity = context.entity(d.id); + return utilDisplayName(entity); + }); + } + + return section; + } + + function uiEntityEditor(context) { + var dispatch = dispatch$8('choose'); + var _state = 'select'; + var _coalesceChanges = false; + var _modified = false; + + var _base; + + var _entityIDs; + + var _activePresets = []; + + var _newFeature; + + var _sections; + + function entityEditor(selection) { + var combinedTags = utilCombinedTags(_entityIDs, context.graph()); // Header + + var header = selection.selectAll('.header').data([0]); // Enter + + var headerEnter = header.enter().append('div').attr('class', 'header fillL'); + headerEnter.append('button').attr('class', 'preset-reset preset-choose').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-forward' : '#iD-icon-backward')); + headerEnter.append('button').attr('class', 'close').on('click', function () { + context.enter(modeBrowse(context)); + }).call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close')); + headerEnter.append('h3'); // Update + + header = header.merge(headerEnter); + header.selectAll('h3').html(_entityIDs.length === 1 ? _t.html('inspector.edit') : _t.html('inspector.edit_features')); + header.selectAll('.preset-reset').on('click', function () { + dispatch.call('choose', this, _activePresets); + }); // Body + + var body = selection.selectAll('.inspector-body').data([0]); // Enter + + var bodyEnter = body.enter().append('div').attr('class', 'entity-editor inspector-body sep-top'); // Update + + body = body.merge(bodyEnter); + + if (!_sections) { + _sections = [uiSectionSelectionList(context), uiSectionFeatureType(context).on('choose', function (presets) { + dispatch.call('choose', this, presets); + }), uiSectionEntityIssues(context), uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags), uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags), uiSectionRawMemberEditor(context), uiSectionRawMembershipEditor(context)]; + } + + _sections.forEach(function (section) { + if (section.entityIDs) { + section.entityIDs(_entityIDs); + } + + if (section.presets) { + section.presets(_activePresets); + } + + if (section.tags) { + section.tags(combinedTags); + } + + if (section.state) { + section.state(_state); + } + + body.call(section.render); + }); + + context.history().on('change.entity-editor', historyChanged); + + function historyChanged(difference) { + if (selection.selectAll('.entity-editor').empty()) return; + if (_state === 'hide') return; + var significant = !difference || difference.didChange.properties || difference.didChange.addition || difference.didChange.deletion; + if (!significant) return; + _entityIDs = _entityIDs.filter(context.hasEntity); + if (!_entityIDs.length) return; + var priorActivePreset = _activePresets.length === 1 && _activePresets[0]; + loadActivePresets(); + var graph = context.graph(); + entityEditor.modified(_base !== graph); + entityEditor(selection); + + if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) { + // flash the button to indicate the preset changed + context.container().selectAll('.entity-editor button.preset-reset .label').style('background-color', '#fff').transition().duration(750).style('background-color', null); + } + } + } // Tag changes that fire on input can all get coalesced into a single + // history operation when the user leaves the field. #2342 + // Use explicit entityIDs in case the selection changes before the event is fired. + + + function changeTags(entityIDs, changed, onInput) { + var actions = []; + + for (var i in entityIDs) { + var entityID = entityIDs[i]; + var entity = context.entity(entityID); + var tags = Object.assign({}, entity.tags); // shallow copy + + for (var k in changed) { + if (!k) continue; + var v = changed[k]; + + if (v !== undefined || tags.hasOwnProperty(k)) { + tags[k] = v; + } + } + + if (!onInput) { + tags = utilCleanTags(tags); + } + + if (!fastDeepEqual(entity.tags, tags)) { + actions.push(actionChangeTags(entityID, tags)); + } + } + + if (actions.length) { + var combinedAction = function combinedAction(graph) { + actions.forEach(function (action) { + graph = action(graph); + }); + return graph; + }; + + var annotation = _t('operations.change_tags.annotation'); + + if (_coalesceChanges) { + context.overwrite(combinedAction, annotation); + } else { + context.perform(combinedAction, annotation); + _coalesceChanges = !!onInput; + } + } // if leaving field (blur event), rerun validation + + + if (!onInput) { + context.validator().validate(); + } + } + + function revertTags(keys) { + var actions = []; + + for (var i in _entityIDs) { + var entityID = _entityIDs[i]; + var original = context.graph().base().entities[entityID]; + var changed = {}; + + for (var j in keys) { + var key = keys[j]; + changed[key] = original ? original.tags[key] : undefined; + } + + var entity = context.entity(entityID); + var tags = Object.assign({}, entity.tags); // shallow copy + + for (var k in changed) { + if (!k) continue; + var v = changed[k]; + + if (v !== undefined || tags.hasOwnProperty(k)) { + tags[k] = v; + } + } + + tags = utilCleanTags(tags); + + if (!fastDeepEqual(entity.tags, tags)) { + actions.push(actionChangeTags(entityID, tags)); + } + } + + if (actions.length) { + var combinedAction = function combinedAction(graph) { + actions.forEach(function (action) { + graph = action(graph); + }); + return graph; + }; + + var annotation = _t('operations.change_tags.annotation'); + + if (_coalesceChanges) { + context.overwrite(combinedAction, annotation); + } else { + context.perform(combinedAction, annotation); + _coalesceChanges = false; + } + } + + context.validator().validate(); + } + + entityEditor.modified = function (val) { + if (!arguments.length) return _modified; + _modified = val; + return entityEditor; + }; + + entityEditor.state = function (val) { + if (!arguments.length) return _state; + _state = val; + return entityEditor; + }; + + entityEditor.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; // always reload these even if the entityIDs are unchanged, since we + // could be reselecting after something like dragging a node + + _base = context.graph(); + _coalesceChanges = false; + if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) return entityEditor; // exit early if no change + + _entityIDs = val; + loadActivePresets(true); + return entityEditor.modified(false); + }; + + entityEditor.newFeature = function (val) { + if (!arguments.length) return _newFeature; + _newFeature = val; + return entityEditor; + }; + + function loadActivePresets(isForNewSelection) { + var graph = context.graph(); + var counts = {}; + + for (var i in _entityIDs) { + var entity = graph.hasEntity(_entityIDs[i]); + if (!entity) return; + var match = _mainPresetIndex.match(entity, graph); + if (!counts[match.id]) counts[match.id] = 0; + counts[match.id] += 1; + } + + var matches = Object.keys(counts).sort(function (p1, p2) { + return counts[p2] - counts[p1]; + }).map(function (pID) { + return _mainPresetIndex.item(pID); + }); + + if (!isForNewSelection) { + // A "weak" preset doesn't set any tags. (e.g. "Address") + 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") + + if (weakPreset && matches.length === 1 && matches[0].isFallback()) return; + } + + entityEditor.presets(matches); + } + + entityEditor.presets = function (val) { + if (!arguments.length) return _activePresets; // don't reload the same preset + + if (!utilArrayIdentical(val, _activePresets)) { + _activePresets = val; + } + + return entityEditor; + }; + + return utilRebind(entityEditor, dispatch, 'on'); + } + + function uiPresetList(context) { + var dispatch = dispatch$8('cancel', 'choose'); + + var _entityIDs; + + var _currLoc; + + var _currentPresets; + + var _autofocus = false; + + function presetList(selection) { + if (!_entityIDs) return; + var presets = _mainPresetIndex.matchAllGeometry(entityGeometries()); + selection.html(''); + var messagewrap = selection.append('div').attr('class', 'header fillL'); + var message = messagewrap.append('h3').html(_t.html('inspector.choose')); + messagewrap.append('button').attr('class', 'preset-choose').on('click', function () { + dispatch.call('cancel', this); + }).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward')); + + function initialKeydown(d3_event) { + // hack to let delete shortcut work when search is autofocused + if (search.property('value').length === 0 && (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + operationDelete(context, _entityIDs)(); // hack to let undo work when search is autofocused + } else if (search.property('value').length === 0 && (d3_event.ctrlKey || d3_event.metaKey) && d3_event.keyCode === utilKeybinding.keyCodes.z) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + context.undo(); + } else if (!d3_event.ctrlKey && !d3_event.metaKey) { + // don't check for delete/undo hack on future keydown events + select(this).on('keydown', keydown); + keydown.call(this, d3_event); + } + } + + function keydown(d3_event) { + // down arrow + if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] && // if insertion point is at the end of the string + search.node().selectionStart === search.property('value').length) { + d3_event.preventDefault(); + d3_event.stopPropagation(); // move focus to the first item in the preset list + + var buttons = list.selectAll('.preset-list-button'); + if (!buttons.empty()) buttons.nodes()[0].focus(); + } + } + + function keypress(d3_event) { + // enter + var value = search.property('value'); + + if (d3_event.keyCode === 13 && // ↩ Return + value.length) { + list.selectAll('.preset-list-item:first-child').each(function (d) { + d.choose.call(this); + }); + } + } + + function inputevent() { + var value = search.property('value'); + list.classed('filtered', value.length); + var results, messageText; + + if (value.length) { + results = presets.search(value, entityGeometries()[0], _currLoc); + messageText = _t('inspector.results', { + n: results.collection.length, + search: value + }); + } else { + results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro(), _currLoc); + messageText = _t('inspector.choose'); + } + + list.call(drawList, results); + message.html(messageText); + } + + var searchWrap = selection.append('div').attr('class', 'search-header'); + searchWrap.call(svgIcon('#iD-icon-search', 'pre-text')); + 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', debounce(inputevent)); + + if (_autofocus) { + search.node().focus(); // Safari 14 doesn't always like to focus immediately, + // so try again on the next pass + + setTimeout(function () { + search.node().focus(); + }, 0); + } + + var listWrap = selection.append('div').attr('class', 'inspector-body'); + var list = listWrap.append('div').attr('class', 'preset-list').call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro(), _currLoc)); + context.features().on('change.preset-list', updateForFeatureHiddenState); + } + + function drawList(list, presets) { + presets = presets.matchAllGeometry(entityGeometries()); + var collection = presets.collection.reduce(function (collection, preset) { + if (!preset) return collection; + + if (preset.members) { + if (preset.members.collection.filter(function (preset) { + return preset.addable(); + }).length > 1) { + collection.push(CategoryItem(preset)); + } + } else if (preset.addable()) { + collection.push(PresetItem(preset)); + } + + return collection; + }, []); + var items = list.selectAll('.preset-list-item').data(collection, function (d) { + return d.preset.id; + }); + items.order(); + items.exit().remove(); + items.enter().append('div').attr('class', function (item) { + return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); + }).classed('current', function (item) { + return _currentPresets.indexOf(item.preset) !== -1; + }).each(function (item) { + select(this).call(item); + }).style('opacity', 0).transition().style('opacity', 1); + updateForFeatureHiddenState(); + } + + function itemKeydown(d3_event) { + // the actively focused item + var item = select(this.closest('.preset-list-item')); + var parentItem = select(item.node().parentNode.closest('.preset-list-item')); // arrow down, move focus to the next, lower item + + if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) { + d3_event.preventDefault(); + d3_event.stopPropagation(); // the next item in the list at the same level + + var nextItem = select(item.node().nextElementSibling); // if there is no next item in this list + + if (nextItem.empty()) { + // if there is a parent item + if (!parentItem.empty()) { + // the item is the last item of a sublist, + // select the next item at the parent level + nextItem = select(parentItem.node().nextElementSibling); + } // if the focused item is expanded + + } else if (select(this).classed('expanded')) { + // select the first subitem instead + nextItem = item.select('.subgrid .preset-list-item:first-child'); + } + + if (!nextItem.empty()) { + // focus on the next item + nextItem.select('.preset-list-button').node().focus(); + } // arrow up, move focus to the previous, higher item + + } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑']) { + d3_event.preventDefault(); + d3_event.stopPropagation(); // the previous item in the list at the same level + + var previousItem = select(item.node().previousElementSibling); // if there is no previous item in this list + + if (previousItem.empty()) { + // if there is a parent item + if (!parentItem.empty()) { + // the item is the first subitem of a sublist select the parent item + previousItem = parentItem; + } // if the previous item is expanded + + } else if (previousItem.select('.preset-list-button').classed('expanded')) { + // select the last subitem of the sublist of the previous item + previousItem = previousItem.select('.subgrid .preset-list-item:last-child'); + } + + if (!previousItem.empty()) { + // focus on the previous item + previousItem.select('.preset-list-button').node().focus(); + } else { + // the focus is at the top of the list, move focus back to the search field + var search = select(this.closest('.preset-list-pane')).select('.preset-search-input'); + search.node().focus(); + } // arrow left, move focus to the parent item if there is one + + } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) { + d3_event.preventDefault(); + d3_event.stopPropagation(); // if there is a parent item, focus on the parent item + + if (!parentItem.empty()) { + parentItem.select('.preset-list-button').node().focus(); + } // arrow right, choose this item + + } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + item.datum().choose.call(select(this).node()); + } + } + + function CategoryItem(preset) { + var box, + sublist, + shown = false; + + function item(selection) { + var wrap = selection.append('div').attr('class', 'preset-list-button-wrap category'); + + function click() { + var isExpanded = select(this).classed('expanded'); + var iconName = isExpanded ? _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward' : '#iD-icon-down'; + select(this).classed('expanded', !isExpanded); + select(this).selectAll('div.label-inner svg.icon use').attr('href', iconName); + item.choose(); + } + + var geometries = entityGeometries(); + var button = wrap.append('button').attr('class', 'preset-list-button').classed('expanded', false).call(uiPresetIcon().geometry(geometries.length === 1 && geometries[0]).preset(preset)).on('click', click).on('keydown', function (d3_event) { + // right arrow, expand the focused item + if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) { + d3_event.preventDefault(); + d3_event.stopPropagation(); // if the item isn't expanded + + if (!select(this).classed('expanded')) { + // toggle expansion (expand the item) + click.call(this, d3_event); + } // left arrow, collapse the focused item + + } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) { + d3_event.preventDefault(); + d3_event.stopPropagation(); // if the item is expanded + + if (select(this).classed('expanded')) { + // toggle expansion (collapse the item) + click.call(this, d3_event); + } + } else { + itemKeydown.call(this, d3_event); + } + }); + var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner'); + label.append('div').attr('class', 'namepart').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline')).append('span').html(function () { + return preset.nameLabel() + '…'; + }); + box = selection.append('div').attr('class', 'subgrid').style('max-height', '0px').style('opacity', 0); + box.append('div').attr('class', 'arrow'); + sublist = box.append('div').attr('class', 'preset-list fillL3'); + } + + item.choose = function () { + if (!box || !sublist) return; + + if (shown) { + shown = false; + box.transition().duration(200).style('opacity', '0').style('max-height', '0px').style('padding-bottom', '0px'); + } else { + shown = true; + var members = preset.members.matchAllGeometry(entityGeometries()); + sublist.call(drawList, members); + box.transition().duration(200).style('opacity', '1').style('max-height', 200 + members.collection.length * 190 + 'px').style('padding-bottom', '10px'); + } + }; + + item.preset = preset; + return item; + } + + function PresetItem(preset) { + function item(selection) { + var wrap = selection.append('div').attr('class', 'preset-list-button-wrap'); + var geometries = entityGeometries(); + var button = wrap.append('button').attr('class', 'preset-list-button').call(uiPresetIcon().geometry(geometries.length === 1 && geometries[0]).preset(preset)).on('click', item.choose).on('keydown', itemKeydown); + var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner'); + var nameparts = [preset.nameLabel(), preset.subtitleLabel()].filter(Boolean); + label.selectAll('.namepart').data(nameparts).enter().append('div').attr('class', 'namepart').html(function (d) { + return d; + }); + wrap.call(item.reference.button); + selection.call(item.reference.body); + } + + item.choose = function () { + if (select(this).classed('disabled')) return; + + if (!context.inIntro()) { + _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]); + } + + context.perform(function (graph) { + for (var i in _entityIDs) { + var entityID = _entityIDs[i]; + var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph); + graph = actionChangePreset(entityID, oldPreset, preset)(graph); + } + + return graph; + }, _t('operations.change_tags.annotation')); + context.validator().validate(); // rerun validation + + dispatch.call('choose', this, preset); + }; + + item.help = function (d3_event) { + d3_event.stopPropagation(); + item.reference.toggle(); + }; + + item.preset = preset; + item.reference = uiTagReference(preset.reference()); + return item; + } + + function updateForFeatureHiddenState() { + if (!_entityIDs.every(context.hasEntity)) return; + var geometries = entityGeometries(); + var button = context.container().selectAll('.preset-list .preset-list-button'); // remove existing tooltips + + button.call(uiTooltip().destroyAny); + button.each(function (item, index) { + var hiddenPresetFeaturesId; + + for (var i in geometries) { + hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]); + if (hiddenPresetFeaturesId) break; + } + + var isHiddenPreset = !context.inIntro() && !!hiddenPresetFeaturesId && (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]); + select(this).classed('disabled', isHiddenPreset); + + if (isHiddenPreset) { + var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId); + select(this).call(uiTooltip().title(_t.html('inspector.hidden_preset.' + (isAutoHidden ? 'zoom' : 'manual'), { + features: _t.html('feature.' + hiddenPresetFeaturesId + '.description') + })).placement(index < 2 ? 'bottom' : 'top')); + } + }); + } + + presetList.autofocus = function (val) { + if (!arguments.length) return _autofocus; + _autofocus = val; + return presetList; + }; + + presetList.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + _entityIDs = val; + _currLoc = null; + + if (_entityIDs && _entityIDs.length) { + // calculate current location + var extent = _entityIDs.reduce(function (extent, entityID) { + var entity = context.graph().entity(entityID); + return extent.extend(entity.extent(context.graph())); + }, geoExtent()); + + _currLoc = extent.center(); // match presets + + var presets = _entityIDs.map(function (entityID) { + return _mainPresetIndex.match(context.entity(entityID), context.graph()); + }); + + presetList.presets(presets); + } + + return presetList; + }; + + presetList.presets = function (val) { + if (!arguments.length) return _currentPresets; + _currentPresets = val; + return presetList; + }; + + function entityGeometries() { + var counts = {}; + + for (var i in _entityIDs) { + var entityID = _entityIDs[i]; + var entity = context.entity(entityID); + var geometry = entity.geometry(context.graph()); // Treat entities on addr:interpolation lines as points, not vertices (#3241) + + if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) { + geometry = 'point'; + } + + if (!counts[geometry]) counts[geometry] = 0; + counts[geometry] += 1; + } + + return Object.keys(counts).sort(function (geom1, geom2) { + return counts[geom2] - counts[geom1]; + }); + } + + return utilRebind(presetList, dispatch, 'on'); + } + + function uiViewOnOSM(context) { + var _what; // an osmEntity or osmNote + + + function viewOnOSM(selection) { + var url; + + if (_what instanceof osmEntity) { + url = context.connection().entityURL(_what); + } else if (_what instanceof osmNote) { + url = context.connection().noteURL(_what); + } + + var data = !_what || _what.isNew() ? [] : [_what]; + var link = selection.selectAll('.view-on-osm').data(data, function (d) { + return d.id; + }); // exit + + link.exit().remove(); // enter + + var linkEnter = link.enter().append('a').attr('class', 'view-on-osm').attr('target', '_blank').attr('href', url).call(svgIcon('#iD-icon-out-link', 'inline')); + linkEnter.append('span').html(_t.html('inspector.view_on_osm')); + } + + viewOnOSM.what = function (_) { + if (!arguments.length) return _what; + _what = _; + return viewOnOSM; + }; + + return viewOnOSM; + } + + function uiInspector(context) { + var presetList = uiPresetList(context); + var entityEditor = uiEntityEditor(context); + var wrap = select(null), + presetPane = select(null), + editorPane = select(null); + var _state = 'select'; + + var _entityIDs; + + var _newFeature = false; + + function inspector(selection) { + presetList.entityIDs(_entityIDs).autofocus(_newFeature).on('choose', inspector.setPreset).on('cancel', function () { + inspector.setPreset(); + }); + entityEditor.state(_state).entityIDs(_entityIDs).on('choose', inspector.showList); + wrap = selection.selectAll('.panewrap').data([0]); + var enter = wrap.enter().append('div').attr('class', 'panewrap'); + enter.append('div').attr('class', 'preset-list-pane pane'); + enter.append('div').attr('class', 'entity-editor-pane pane'); + wrap = wrap.merge(enter); + presetPane = wrap.selectAll('.preset-list-pane'); + editorPane = wrap.selectAll('.entity-editor-pane'); + + function shouldDefaultToPresetList() { + // always show the inspector on hover + if (_state !== 'select') return false; // can only change preset on single selection + + if (_entityIDs.length !== 1) return false; + var entityID = _entityIDs[0]; + var entity = context.hasEntity(entityID); + if (!entity) return false; // default to inspector if there are already tags + + if (entity.hasNonGeometryTags()) return false; // prompt to select preset if feature is new and untagged + + if (_newFeature) return true; // all existing features except vertices should default to inspector + + if (entity.geometry(context.graph()) !== 'vertex') return false; // show vertex relations if any + + if (context.graph().parentRelations(entity).length) return false; // show vertex issues if there are any + + if (context.validator().getEntityIssues(entityID).length) return false; // show turn retriction editor for junction vertices + + if (entity.isHighwayIntersection(context.graph())) return false; // otherwise show preset list for uninteresting vertices + + return true; + } + + if (shouldDefaultToPresetList()) { + wrap.style('right', '-100%'); + editorPane.classed('hide', true); + presetPane.classed('hide', false).call(presetList); + } else { + wrap.style('right', '0%'); + presetPane.classed('hide', true); + editorPane.classed('hide', false).call(entityEditor); + } + + var footer = selection.selectAll('.footer').data([0]); + footer = footer.enter().append('div').attr('class', 'footer').merge(footer); + footer.call(uiViewOnOSM(context).what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0]))); + } + + inspector.showList = function (presets) { + presetPane.classed('hide', false); + wrap.transition().styleTween('right', function () { + return interpolate$1('0%', '-100%'); + }).on('end', function () { + editorPane.classed('hide', true); + }); + + if (presets) { + presetList.presets(presets); + } + + presetPane.call(presetList.autofocus(true)); + }; + + inspector.setPreset = function (preset) { + // upon setting multipolygon, go to the area preset list instead of the editor + if (preset && preset.id === 'type/multipolygon') { + presetPane.call(presetList.autofocus(true)); + } else { + editorPane.classed('hide', false); + wrap.transition().styleTween('right', function () { + return interpolate$1('-100%', '0%'); + }).on('end', function () { + presetPane.classed('hide', true); + }); + + if (preset) { + entityEditor.presets([preset]); + } + + editorPane.call(entityEditor); + } + }; + + inspector.state = function (val) { + if (!arguments.length) return _state; + _state = val; + entityEditor.state(_state); // remove any old field help overlay that might have gotten attached to the inspector + + context.container().selectAll('.field-help-body').remove(); + return inspector; + }; + + inspector.entityIDs = function (val) { + if (!arguments.length) return _entityIDs; + _entityIDs = val; + return inspector; + }; + + inspector.newFeature = function (val) { + if (!arguments.length) return _newFeature; + _newFeature = val; + return inspector; + }; + + return inspector; + } + + function uiImproveOsmComments() { + var _qaItem; + + function issueComments(selection) { + // make the div immediately so it appears above the buttons + var comments = selection.selectAll('.comments-container').data([0]); + comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments); // must retrieve comments from API before they can be displayed + + services.improveOSM.getComments(_qaItem).then(function (d) { + if (!d.comments) return; // nothing to do here + + var commentEnter = comments.selectAll('.comment').data(d.comments).enter().append('div').attr('class', 'comment'); + commentEnter.append('div').attr('class', 'comment-avatar').call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon')); + var mainEnter = commentEnter.append('div').attr('class', 'comment-main'); + var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata'); + metadataEnter.append('div').attr('class', 'comment-author').each(function (d) { + var osm = services.osm; + var selection = select(this); + + if (osm && d.username) { + selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.username)).attr('target', '_blank'); + } + + selection.html(function (d) { + return d.username; + }); + }); + metadataEnter.append('div').attr('class', 'comment-date').html(function (d) { + return _t.html('note.status.commented', { + when: localeDateString(d.timestamp) + }); + }); + mainEnter.append('div').attr('class', 'comment-text').append('p').html(function (d) { + return d.text; + }); + })["catch"](function (err) { + console.log(err); // eslint-disable-line no-console + }); + } + + function localeDateString(s) { + if (!s) return null; + var options = { + day: 'numeric', + month: 'short', + year: 'numeric' + }; + var d = new Date(s * 1000); // timestamp is served in seconds, date takes ms + + if (isNaN(d.getTime())) return null; + return d.toLocaleDateString(_mainLocalizer.localeCode(), options); + } + + issueComments.issue = function (val) { + if (!arguments.length) return _qaItem; + _qaItem = val; + return issueComments; + }; + + return issueComments; + } + + function uiImproveOsmDetails(context) { + var _qaItem; + + function issueDetail(d) { + if (d.desc) return d.desc; + var issueKey = d.issueKey; + d.replacements = d.replacements || {}; + d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string + + return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".description"), d.replacements); + } + + function improveOsmDetails(selection) { + var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) { + return "".concat(d.id, "-").concat(d.status || 0); + }); + details.exit().remove(); + var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description + + var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection'); + descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description')); + descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message.. + + var relatedEntities = []; + descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () { + var link = select(this); + var isObjectLink = link.classed('error_object_link'); + var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent; + var entity = context.hasEntity(entityID); + relatedEntities.push(entityID); // Add click handler + + link.on('mouseenter', function () { + utilHighlightEntities([entityID], true, context); + }).on('mouseleave', function () { + utilHighlightEntities([entityID], false, context); + }).on('click', function (d3_event) { + d3_event.preventDefault(); + utilHighlightEntities([entityID], false, context); + var osmlayer = context.layers().layer('osm'); + + if (!osmlayer.enabled()) { + osmlayer.enabled(true); + } + + context.map().centerZoom(_qaItem.loc, 20); + + if (entity) { + context.enter(modeSelect(context, [entityID])); + } else { + context.loadEntity(entityID, function (err, result) { + if (err) return; + var entity = result.data.find(function (e) { + return e.id === entityID; + }); + if (entity) context.enter(modeSelect(context, [entityID])); + }); + } + }); // Replace with friendly name if possible + // (The entity may not yet be loaded into the graph) + + if (entity) { + var name = utilDisplayName(entity); // try to use common name + + if (!name && !isObjectLink) { + var preset = _mainPresetIndex.match(entity, context.graph()); + name = preset && !preset.isFallback() && preset.name(); // fallback to preset name + } + + if (name) { + this.innerText = name; + } + } + }); // Don't hide entities related to this error - #5880 + + context.features().forceVisible(relatedEntities); + context.map().pan([0, 0]); // trigger a redraw + } + + improveOsmDetails.issue = function (val) { + if (!arguments.length) return _qaItem; + _qaItem = val; + return improveOsmDetails; + }; + + return improveOsmDetails; + } + + function uiImproveOsmHeader() { + var _qaItem; + + function issueTitle(d) { + var issueKey = d.issueKey; + d.replacements = d.replacements || {}; + d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string + + return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".title"), d.replacements); + } + + function improveOsmHeader(selection) { + var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) { + return "".concat(d.id, "-").concat(d.status || 0); + }); + header.exit().remove(); + var headerEnter = header.enter().append('div').attr('class', 'qa-header'); + var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) { + return d.id < 0; + }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) { + return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType); + }); + svgEnter.append('polygon').attr('fill', 'currentColor').attr('class', 'qaItem-fill').attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6'); + svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) { + var picon = d.icon; + + if (!picon) { + return ''; + } else { + var isMaki = /^maki-/.test(picon); + return "#".concat(picon).concat(isMaki ? '-11' : ''); + } + }); + headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle); + } + + improveOsmHeader.issue = function (val) { + if (!arguments.length) return _qaItem; + _qaItem = val; + return improveOsmHeader; + }; + + return improveOsmHeader; + } + + function uiImproveOsmEditor(context) { + var dispatch = dispatch$8('change'); + var qaDetails = uiImproveOsmDetails(context); + var qaComments = uiImproveOsmComments(); + var qaHeader = uiImproveOsmHeader(); + + var _qaItem; + + function improveOsmEditor(selection) { + var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL'); + headerEnter.append('button').attr('class', 'close').on('click', function () { + return context.enter(modeBrowse(context)); + }).call(svgIcon('#iD-icon-close')); + headerEnter.append('h3').html(_t.html('QA.improveOSM.title')); + var body = selection.selectAll('.body').data([0]); + body = body.enter().append('div').attr('class', 'body').merge(body); + var editor = body.selectAll('.qa-editor').data([0]); + editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(qaComments.issue(_qaItem)).call(improveOsmSaveSection); + } + + function improveOsmSaveSection(selection) { + var isSelected = _qaItem && _qaItem.id === context.selectedErrorID(); + + var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment); + var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) { + return "".concat(d.id, "-").concat(d.status || 0); + }); // exit + + saveSection.exit().remove(); // enter + + var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf'); + saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('note.newComment')); + saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) { + return d.newComment; + }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update + + saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons); + + function changeInput() { + var input = select(this); + var val = input.property('value').trim(); + + if (val === '') { + val = undefined; + } // store the unsaved comment with the issue itself + + + _qaItem = _qaItem.update({ + newComment: val + }); + var qaService = services.improveOSM; + + if (qaService) { + qaService.replaceItem(_qaItem); + } + + saveSection.call(qaSaveButtons); + } + } + + function qaSaveButtons(selection) { + var isSelected = _qaItem && _qaItem.id === context.selectedErrorID(); + + var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) { + return d.status + d.id; + }); // exit + + buttonSection.exit().remove(); // enter + + var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons'); + buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment')); + buttonEnter.append('button').attr('class', 'button close-button action'); + buttonEnter.append('button').attr('class', 'button ignore-button action'); // update + + buttonSection = buttonSection.merge(buttonEnter); + buttonSection.select('.comment-button').attr('disabled', function (d) { + return d.newComment ? null : true; + }).on('click.comment', function (d3_event, d) { + this.blur(); // avoid keeping focus on the button - #4641 + + var qaService = services.improveOSM; + + if (qaService) { + qaService.postUpdate(d, function (err, item) { + return dispatch.call('change', item); + }); + } + }); + buttonSection.select('.close-button').html(function (d) { + var andComment = d.newComment ? '_comment' : ''; + return _t.html("QA.keepRight.close".concat(andComment)); + }).on('click.close', function (d3_event, d) { + this.blur(); // avoid keeping focus on the button - #4641 + + var qaService = services.improveOSM; + + if (qaService) { + d.newStatus = 'SOLVED'; + qaService.postUpdate(d, function (err, item) { + return dispatch.call('change', item); + }); + } + }); + buttonSection.select('.ignore-button').html(function (d) { + var andComment = d.newComment ? '_comment' : ''; + return _t.html("QA.keepRight.ignore".concat(andComment)); + }).on('click.ignore', function (d3_event, d) { + this.blur(); // avoid keeping focus on the button - #4641 + + var qaService = services.improveOSM; + + if (qaService) { + d.newStatus = 'INVALID'; + qaService.postUpdate(d, function (err, item) { + return dispatch.call('change', item); + }); + } + }); + } // NOTE: Don't change method name until UI v3 is merged + + + improveOsmEditor.error = function (val) { + if (!arguments.length) return _qaItem; + _qaItem = val; + return improveOsmEditor; + }; + + return utilRebind(improveOsmEditor, dispatch, 'on'); + } + + function uiKeepRightDetails(context) { + var _qaItem; + + function issueDetail(d) { + var itemType = d.itemType, + parentIssueType = d.parentIssueType; + var unknown = _t.html('inspector.unknown'); + var replacements = d.replacements || {}; + replacements["default"] = unknown; // special key `default` works as a fallback string + + var detail = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".description"), replacements); + + if (detail === unknown) { + detail = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".description"), replacements); + } + + return detail; + } + + function keepRightDetails(selection) { + var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) { + return "".concat(d.id, "-").concat(d.status || 0); + }); + details.exit().remove(); + var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description + + var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection'); + descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description')); + descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message.. + + var relatedEntities = []; + descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () { + var link = select(this); + var isObjectLink = link.classed('error_object_link'); + var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent; + var entity = context.hasEntity(entityID); + relatedEntities.push(entityID); // Add click handler + + link.on('mouseenter', function () { + utilHighlightEntities([entityID], true, context); + }).on('mouseleave', function () { + utilHighlightEntities([entityID], false, context); + }).on('click', function (d3_event) { + d3_event.preventDefault(); + utilHighlightEntities([entityID], false, context); + var osmlayer = context.layers().layer('osm'); + + if (!osmlayer.enabled()) { + osmlayer.enabled(true); + } + + context.map().centerZoomEase(_qaItem.loc, 20); + + if (entity) { + context.enter(modeSelect(context, [entityID])); + } else { + context.loadEntity(entityID, function (err, result) { + if (err) return; + var entity = result.data.find(function (e) { + return e.id === entityID; + }); + if (entity) context.enter(modeSelect(context, [entityID])); + }); + } + }); // Replace with friendly name if possible + // (The entity may not yet be loaded into the graph) + + if (entity) { + var name = utilDisplayName(entity); // try to use common name + + if (!name && !isObjectLink) { + var preset = _mainPresetIndex.match(entity, context.graph()); + name = preset && !preset.isFallback() && preset.name(); // fallback to preset name + } + + if (name) { + this.innerText = name; + } + } + }); // Don't hide entities related to this issue - #5880 + + context.features().forceVisible(relatedEntities); + context.map().pan([0, 0]); // trigger a redraw + } + + keepRightDetails.issue = function (val) { + if (!arguments.length) return _qaItem; + _qaItem = val; + return keepRightDetails; + }; + + return keepRightDetails; + } + + function uiKeepRightHeader() { + var _qaItem; + + function issueTitle(d) { + var itemType = d.itemType, + parentIssueType = d.parentIssueType; + var unknown = _t.html('inspector.unknown'); + var replacements = d.replacements || {}; + replacements["default"] = unknown; // special key `default` works as a fallback string + + var title = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".title"), replacements); + + if (title === unknown) { + title = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".title"), replacements); + } + + return title; + } + + function keepRightHeader(selection) { + var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) { + return "".concat(d.id, "-").concat(d.status || 0); + }); + header.exit().remove(); + var headerEnter = header.enter().append('div').attr('class', 'qa-header'); + var iconEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) { + return d.id < 0; + }); + iconEnter.append('div').attr('class', function (d) { + return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType); + }).call(svgIcon('#iD-icon-bolt', 'qaItem-fill')); + headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle); + } + + keepRightHeader.issue = function (val) { + if (!arguments.length) return _qaItem; + _qaItem = val; + return keepRightHeader; + }; + + return keepRightHeader; + } + + function uiViewOnKeepRight() { + var _qaItem; + + function viewOnKeepRight(selection) { + var url; + + if (services.keepRight && _qaItem instanceof QAItem) { + url = services.keepRight.issueURL(_qaItem); + } + + var link = selection.selectAll('.view-on-keepRight').data(url ? [url] : []); // exit + + link.exit().remove(); // enter + + var linkEnter = link.enter().append('a').attr('class', 'view-on-keepRight').attr('target', '_blank').attr('rel', 'noopener') // security measure + .attr('href', function (d) { + return d; + }).call(svgIcon('#iD-icon-out-link', 'inline')); + linkEnter.append('span').html(_t.html('inspector.view_on_keepRight')); + } + + viewOnKeepRight.what = function (val) { + if (!arguments.length) return _qaItem; + _qaItem = val; + return viewOnKeepRight; + }; + + return viewOnKeepRight; + } + + function uiKeepRightEditor(context) { + var dispatch = dispatch$8('change'); + var qaDetails = uiKeepRightDetails(context); + var qaHeader = uiKeepRightHeader(); + + var _qaItem; + + function keepRightEditor(selection) { + var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL'); + headerEnter.append('button').attr('class', 'close').on('click', function () { + return context.enter(modeBrowse(context)); + }).call(svgIcon('#iD-icon-close')); + headerEnter.append('h3').html(_t.html('QA.keepRight.title')); + var body = selection.selectAll('.body').data([0]); + body = body.enter().append('div').attr('class', 'body').merge(body); + var editor = body.selectAll('.qa-editor').data([0]); + editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(keepRightSaveSection); + var footer = selection.selectAll('.footer').data([0]); + footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnKeepRight().what(_qaItem)); + } + + function keepRightSaveSection(selection) { + var isSelected = _qaItem && _qaItem.id === context.selectedErrorID(); + + var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment); + var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) { + return "".concat(d.id, "-").concat(d.status || 0); + }); // exit + + saveSection.exit().remove(); // enter + + var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf'); + saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('QA.keepRight.comment')); + saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) { + return d.newComment || d.comment; + }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update + + saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons); + + function changeInput() { + var input = select(this); + var val = input.property('value').trim(); + + if (val === _qaItem.comment) { + val = undefined; + } // store the unsaved comment with the issue itself + + + _qaItem = _qaItem.update({ + newComment: val + }); + var qaService = services.keepRight; + + if (qaService) { + qaService.replaceItem(_qaItem); // update keepright cache + } + + saveSection.call(qaSaveButtons); + } + } + + function qaSaveButtons(selection) { + var isSelected = _qaItem && _qaItem.id === context.selectedErrorID(); + + var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) { + return d.status + d.id; + }); // exit + + buttonSection.exit().remove(); // enter + + var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons'); + buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment')); + buttonEnter.append('button').attr('class', 'button close-button action'); + buttonEnter.append('button').attr('class', 'button ignore-button action'); // update + + buttonSection = buttonSection.merge(buttonEnter); + buttonSection.select('.comment-button') // select and propagate data + .attr('disabled', function (d) { + return d.newComment ? null : true; + }).on('click.comment', function (d3_event, d) { + this.blur(); // avoid keeping focus on the button - #4641 + + var qaService = services.keepRight; + + if (qaService) { + qaService.postUpdate(d, function (err, item) { + return dispatch.call('change', item); + }); + } + }); + buttonSection.select('.close-button') // select and propagate data + .html(function (d) { + var andComment = d.newComment ? '_comment' : ''; + return _t.html("QA.keepRight.close".concat(andComment)); + }).on('click.close', function (d3_event, d) { + this.blur(); // avoid keeping focus on the button - #4641 + + var qaService = services.keepRight; + + if (qaService) { + d.newStatus = 'ignore_t'; // ignore temporarily (item fixed) + + qaService.postUpdate(d, function (err, item) { + return dispatch.call('change', item); + }); + } + }); + buttonSection.select('.ignore-button') // select and propagate data + .html(function (d) { + var andComment = d.newComment ? '_comment' : ''; + return _t.html("QA.keepRight.ignore".concat(andComment)); + }).on('click.ignore', function (d3_event, d) { + this.blur(); // avoid keeping focus on the button - #4641 + + var qaService = services.keepRight; + + if (qaService) { + d.newStatus = 'ignore'; // ignore permanently (false positive) + + qaService.postUpdate(d, function (err, item) { + return dispatch.call('change', item); + }); + } + }); + } // NOTE: Don't change method name until UI v3 is merged + + + keepRightEditor.error = function (val) { + if (!arguments.length) return _qaItem; + _qaItem = val; + return keepRightEditor; + }; + + return utilRebind(keepRightEditor, dispatch, 'on'); + } + + function uiOsmoseDetails(context) { + var _qaItem; + + function issueString(d, type) { + if (!d) return ''; // Issue strings are cached from Osmose API + + var s = services.osmose.getStrings(d.itemType); + return type in s ? s[type] : ''; + } + + function osmoseDetails(selection) { + var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) { + return "".concat(d.id, "-").concat(d.status || 0); + }); + details.exit().remove(); + var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // Description + + if (issueString(_qaItem, 'detail')) { + var div = detailsEnter.append('div').attr('class', 'qa-details-subsection'); + div.append('h4').html(_t.html('QA.keepRight.detail_description')); + div.append('p').attr('class', 'qa-details-description-text').html(function (d) { + return issueString(d, 'detail'); + }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank'); + } // Elements (populated later as data is requested) + + + var detailsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection'); + var elemsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection'); // Suggested Fix (mustn't exist for every issue type) + + if (issueString(_qaItem, 'fix')) { + var _div = detailsEnter.append('div').attr('class', 'qa-details-subsection'); + + _div.append('h4').html(_t.html('QA.osmose.fix_title')); + + _div.append('p').html(function (d) { + return issueString(d, 'fix'); + }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank'); + } // Common Pitfalls (mustn't exist for every issue type) + + + if (issueString(_qaItem, 'trap')) { + var _div2 = detailsEnter.append('div').attr('class', 'qa-details-subsection'); + + _div2.append('h4').html(_t.html('QA.osmose.trap_title')); + + _div2.append('p').html(function (d) { + return issueString(d, 'trap'); + }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank'); + } // Save current item to check if UI changed by time request resolves + + + var thisItem = _qaItem; + services.osmose.loadIssueDetail(_qaItem).then(function (d) { + // No details to add if there are no associated issue elements + if (!d.elems || d.elems.length === 0) return; // Do nothing if UI has moved on by the time this resolves + + if (context.selectedErrorID() !== thisItem.id && context.container().selectAll(".qaItem.osmose.hover.itemId-".concat(thisItem.id)).empty()) return; // Things like keys and values are dynamically added to a subtitle string + + if (d.detail) { + detailsDiv.append('h4').html(_t.html('QA.osmose.detail_title')); + detailsDiv.append('p').html(function (d) { + return d.detail; + }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank'); + } // Create list of linked issue elements + + + elemsDiv.append('h4').html(_t.html('QA.osmose.elems_title')); + elemsDiv.append('ul').selectAll('li').data(d.elems).enter().append('li').append('a').attr('href', '#').attr('class', 'error_entity_link').html(function (d) { + return d; + }).each(function () { + var link = select(this); + var entityID = this.textContent; + var entity = context.hasEntity(entityID); // Add click handler + + link.on('mouseenter', function () { + utilHighlightEntities([entityID], true, context); + }).on('mouseleave', function () { + utilHighlightEntities([entityID], false, context); + }).on('click', function (d3_event) { + d3_event.preventDefault(); + utilHighlightEntities([entityID], false, context); + var osmlayer = context.layers().layer('osm'); + + if (!osmlayer.enabled()) { + osmlayer.enabled(true); + } + + context.map().centerZoom(d.loc, 20); + + if (entity) { + context.enter(modeSelect(context, [entityID])); + } else { + context.loadEntity(entityID, function (err, result) { + if (err) return; + var entity = result.data.find(function (e) { + return e.id === entityID; + }); + if (entity) context.enter(modeSelect(context, [entityID])); + }); + } + }); // Replace with friendly name if possible + // (The entity may not yet be loaded into the graph) + + if (entity) { + var name = utilDisplayName(entity); // try to use common name + + if (!name) { + var preset = _mainPresetIndex.match(entity, context.graph()); + name = preset && !preset.isFallback() && preset.name(); // fallback to preset name + } + + if (name) { + this.innerText = name; + } + } + }); // Don't hide entities related to this issue - #5880 + + context.features().forceVisible(d.elems); + context.map().pan([0, 0]); // trigger a redraw + })["catch"](function (err) { + console.log(err); // eslint-disable-line no-console + }); + } + + osmoseDetails.issue = function (val) { + if (!arguments.length) return _qaItem; + _qaItem = val; + return osmoseDetails; + }; + + return osmoseDetails; + } + + function uiOsmoseHeader() { + var _qaItem; + + function issueTitle(d) { + var unknown = _t('inspector.unknown'); + if (!d) return unknown; // Issue titles supplied by Osmose + + var s = services.osmose.getStrings(d.itemType); + return 'title' in s ? s.title : unknown; + } + + function osmoseHeader(selection) { + var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) { + return "".concat(d.id, "-").concat(d.status || 0); + }); + header.exit().remove(); + var headerEnter = header.enter().append('div').attr('class', 'qa-header'); + var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) { + return d.id < 0; + }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) { + return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType); + }); + svgEnter.append('polygon').attr('fill', function (d) { + return services.osmose.getColor(d.item); + }).attr('class', 'qaItem-fill').attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6'); + svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) { + var picon = d.icon; + + if (!picon) { + return ''; + } else { + var isMaki = /^maki-/.test(picon); + return "#".concat(picon).concat(isMaki ? '-11' : ''); + } + }); + headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle); + } + + osmoseHeader.issue = function (val) { + if (!arguments.length) return _qaItem; + _qaItem = val; + return osmoseHeader; + }; + + return osmoseHeader; + } + + function uiViewOnOsmose() { + var _qaItem; + + function viewOnOsmose(selection) { + var url; + + if (services.osmose && _qaItem instanceof QAItem) { + url = services.osmose.itemURL(_qaItem); + } + + var link = selection.selectAll('.view-on-osmose').data(url ? [url] : []); // exit + + link.exit().remove(); // enter + + var linkEnter = link.enter().append('a').attr('class', 'view-on-osmose').attr('target', '_blank').attr('rel', 'noopener') // security measure + .attr('href', function (d) { + return d; + }).call(svgIcon('#iD-icon-out-link', 'inline')); + linkEnter.append('span').html(_t.html('inspector.view_on_osmose')); + } + + viewOnOsmose.what = function (val) { + if (!arguments.length) return _qaItem; + _qaItem = val; + return viewOnOsmose; + }; + + return viewOnOsmose; + } + + function uiOsmoseEditor(context) { + var dispatch = dispatch$8('change'); + var qaDetails = uiOsmoseDetails(context); + var qaHeader = uiOsmoseHeader(); + + var _qaItem; + + function osmoseEditor(selection) { + var header = selection.selectAll('.header').data([0]); + var headerEnter = header.enter().append('div').attr('class', 'header fillL'); + headerEnter.append('button').attr('class', 'close').on('click', function () { + return context.enter(modeBrowse(context)); + }).call(svgIcon('#iD-icon-close')); + headerEnter.append('h3').html(_t.html('QA.osmose.title')); + var body = selection.selectAll('.body').data([0]); + body = body.enter().append('div').attr('class', 'body').merge(body); + var editor = body.selectAll('.qa-editor').data([0]); + editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(osmoseSaveSection); + var footer = selection.selectAll('.footer').data([0]); + footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnOsmose().what(_qaItem)); + } + + function osmoseSaveSection(selection) { + var isSelected = _qaItem && _qaItem.id === context.selectedErrorID(); + + var isShown = _qaItem && isSelected; + var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) { + return "".concat(d.id, "-").concat(d.status || 0); + }); // exit + + saveSection.exit().remove(); // enter + + var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf'); // update + + saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons); + } + + function qaSaveButtons(selection) { + var isSelected = _qaItem && _qaItem.id === context.selectedErrorID(); + + var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) { + return d.status + d.id; + }); // exit + + buttonSection.exit().remove(); // enter + + var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons'); + buttonEnter.append('button').attr('class', 'button close-button action'); + buttonEnter.append('button').attr('class', 'button ignore-button action'); // update + + buttonSection = buttonSection.merge(buttonEnter); + buttonSection.select('.close-button').html(_t.html('QA.keepRight.close')).on('click.close', function (d3_event, d) { + this.blur(); // avoid keeping focus on the button - #4641 + + var qaService = services.osmose; + + if (qaService) { + d.newStatus = 'done'; + qaService.postUpdate(d, function (err, item) { + return dispatch.call('change', item); + }); + } + }); + buttonSection.select('.ignore-button').html(_t.html('QA.keepRight.ignore')).on('click.ignore', function (d3_event, d) { + this.blur(); // avoid keeping focus on the button - #4641 + + var qaService = services.osmose; + + if (qaService) { + d.newStatus = 'false'; + qaService.postUpdate(d, function (err, item) { + return dispatch.call('change', item); + }); + } + }); + } // NOTE: Don't change method name until UI v3 is merged + + + osmoseEditor.error = function (val) { + if (!arguments.length) return _qaItem; + _qaItem = val; + return osmoseEditor; + }; + + return utilRebind(osmoseEditor, dispatch, 'on'); + } + + function uiNoteComments() { + var _note; + + function noteComments(selection) { + if (_note.isNew()) return; // don't draw .comments-container + + var comments = selection.selectAll('.comments-container').data([0]); + comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments); + var commentEnter = comments.selectAll('.comment').data(_note.comments).enter().append('div').attr('class', 'comment'); + commentEnter.append('div').attr('class', function (d) { + return 'comment-avatar user-' + d.uid; + }).call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon')); + var mainEnter = commentEnter.append('div').attr('class', 'comment-main'); + var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata'); + metadataEnter.append('div').attr('class', 'comment-author').each(function (d) { + var selection = select(this); + var osm = services.osm; + + if (osm && d.user) { + selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.user)).attr('target', '_blank'); + } + + selection.html(function (d) { + return d.user || _t.html('note.anonymous'); + }); + }); + metadataEnter.append('div').attr('class', 'comment-date').html(function (d) { + return _t('note.status.' + d.action, { + when: localeDateString(d.date) + }); + }); + mainEnter.append('div').attr('class', 'comment-text').html(function (d) { + return d.html; + }).selectAll('a').attr('rel', 'noopener nofollow').attr('target', '_blank'); + comments.call(replaceAvatars); + } + + function replaceAvatars(selection) { + var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true'; + var osm = services.osm; + if (showThirdPartyIcons !== 'true' || !osm) return; + var uids = {}; // gather uids in the comment thread + + _note.comments.forEach(function (d) { + if (d.uid) uids[d.uid] = true; + }); + + Object.keys(uids).forEach(function (uid) { + osm.loadUser(uid, function (err, user) { + if (!user || !user.image_url) return; + selection.selectAll('.comment-avatar.user-' + uid).html('').append('img').attr('class', 'icon comment-avatar-icon').attr('src', user.image_url).attr('alt', user.display_name); + }); + }); + } + + function localeDateString(s) { + if (!s) return null; + var options = { + day: 'numeric', + month: 'short', + year: 'numeric' + }; + s = s.replace(/-/g, '/'); // fix browser-specific Date() issues + + var d = new Date(s); + if (isNaN(d.getTime())) return null; + return d.toLocaleDateString(_mainLocalizer.localeCode(), options); + } + + noteComments.note = function (val) { + if (!arguments.length) return _note; + _note = val; + return noteComments; + }; + + return noteComments; + } + + function uiNoteHeader() { + var _note; + + function noteHeader(selection) { + var header = selection.selectAll('.note-header').data(_note ? [_note] : [], function (d) { + return d.status + d.id; + }); + header.exit().remove(); + var headerEnter = header.enter().append('div').attr('class', 'note-header'); + var iconEnter = headerEnter.append('div').attr('class', function (d) { + return 'note-header-icon ' + d.status; + }).classed('new', function (d) { + return d.id < 0; + }); + iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-note', 'note-fill')); + iconEnter.each(function (d) { + var statusIcon; + + if (d.id < 0) { + statusIcon = '#iD-icon-plus'; + } else if (d.status === 'open') { + statusIcon = '#iD-icon-close'; + } else { + statusIcon = '#iD-icon-apply'; + } + + iconEnter.append('div').attr('class', 'note-icon-annotation').call(svgIcon(statusIcon, 'icon-annotation')); + }); + headerEnter.append('div').attr('class', 'note-header-label').html(function (d) { + if (_note.isNew()) { + return _t('note.new'); + } + + return _t('note.note') + ' ' + d.id + ' ' + (d.status === 'closed' ? _t('note.closed') : ''); + }); + } + + noteHeader.note = function (val) { + if (!arguments.length) return _note; + _note = val; + return noteHeader; + }; + + return noteHeader; + } + + function uiNoteReport() { + var _note; + + function noteReport(selection) { + var url; + + if (services.osm && _note instanceof osmNote && !_note.isNew()) { + url = services.osm.noteReportURL(_note); + } + + var link = selection.selectAll('.note-report').data(url ? [url] : []); // exit + + link.exit().remove(); // enter + + var linkEnter = link.enter().append('a').attr('class', 'note-report').attr('target', '_blank').attr('href', function (d) { + return d; + }).call(svgIcon('#iD-icon-out-link', 'inline')); + linkEnter.append('span').html(_t.html('note.report')); + } + + noteReport.note = function (val) { + if (!arguments.length) return _note; + _note = val; + return noteReport; + }; + + return noteReport; + } + + function uiNoteEditor(context) { + var dispatch = dispatch$8('change'); + var noteComments = uiNoteComments(); + var noteHeader = uiNoteHeader(); // var formFields = uiFormFields(context); + + var _note; + + var _newNote; // var _fieldsArr; + + + function noteEditor(selection) { + var header = selection.selectAll('.header').data([0]); + var headerEnter = header.enter().append('div').attr('class', 'header fillL'); + headerEnter.append('button').attr('class', 'close').on('click', function () { + context.enter(modeBrowse(context)); + }).call(svgIcon('#iD-icon-close')); + headerEnter.append('h3').html(_t.html('note.title')); + var body = selection.selectAll('.body').data([0]); + body = body.enter().append('div').attr('class', 'body').merge(body); + var editor = body.selectAll('.note-editor').data([0]); + editor.enter().append('div').attr('class', 'modal-section note-editor').merge(editor).call(noteHeader.note(_note)).call(noteComments.note(_note)).call(noteSaveSection); + var footer = selection.selectAll('.footer').data([0]); + footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnOSM(context).what(_note)).call(uiNoteReport().note(_note)); // rerender the note editor on any auth change + + var osm = services.osm; + + if (osm) { + osm.on('change.note-save', function () { + selection.call(noteEditor); + }); + } + } + + function noteSaveSection(selection) { + var isSelected = _note && _note.id === context.selectedNoteID(); + + var noteSave = selection.selectAll('.note-save').data(isSelected ? [_note] : [], function (d) { + return d.status + d.id; + }); // exit + + noteSave.exit().remove(); // enter + + var noteSaveEnter = noteSave.enter().append('div').attr('class', 'note-save save-section cf'); // // if new note, show categories to pick from + // if (_note.isNew()) { + // var presets = presetManager; + // // NOTE: this key isn't a age and therefore there is no documentation (yet) + // _fieldsArr = [ + // uiField(context, presets.field('category'), null, { show: true, revert: false }), + // ]; + // _fieldsArr.forEach(function(field) { + // field + // .on('change', changeCategory); + // }); + // noteSaveEnter + // .append('div') + // .attr('class', 'note-category') + // .call(formFields.fieldsArr(_fieldsArr)); + // } + // function changeCategory() { + // // NOTE: perhaps there is a better way to get value + // var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined; + // // store the unsaved category with the note itself + // _note = _note.update({ newCategory: val }); + // var osm = services.osm; + // if (osm) { + // osm.replaceNote(_note); // update note cache + // } + // noteSave + // .call(noteSaveButtons); + // } + + noteSaveEnter.append('h4').attr('class', '.note-save-header').html(function () { + return _note.isNew() ? _t('note.newDescription') : _t('note.newComment'); + }); + var commentTextarea = noteSaveEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('note.inputPlaceholder')).attr('maxlength', 1000).property('value', function (d) { + return d.newComment; + }).call(utilNoAuto).on('keydown.note-input', keydown).on('input.note-input', changeInput).on('blur.note-input', changeInput); + + if (!commentTextarea.empty() && _newNote) { + // autofocus the comment field for new notes + commentTextarea.node().focus(); + } // update + + + noteSave = noteSaveEnter.merge(noteSave).call(userDetails).call(noteSaveButtons); // fast submit if user presses cmd+enter + + function keydown(d3_event) { + if (!(d3_event.keyCode === 13 && // ↩ Return + d3_event.metaKey)) return; + var osm = services.osm; + if (!osm) return; + var hasAuth = osm.authenticated(); + if (!hasAuth) return; + if (!_note.newComment) return; + d3_event.preventDefault(); + select(this).on('keydown.note-input', null); // focus on button and submit + + window.setTimeout(function () { + if (_note.isNew()) { + noteSave.selectAll('.save-button').node().focus(); + clickSave(); + } else { + noteSave.selectAll('.comment-button').node().focus(); + clickComment(); + } + }, 10); + } + + function changeInput() { + var input = select(this); + var val = input.property('value').trim() || undefined; // store the unsaved comment with the note itself + + _note = _note.update({ + newComment: val + }); + var osm = services.osm; + + if (osm) { + osm.replaceNote(_note); // update note cache + } + + noteSave.call(noteSaveButtons); + } + } + + function userDetails(selection) { + var detailSection = selection.selectAll('.detail-section').data([0]); + detailSection = detailSection.enter().append('div').attr('class', 'detail-section').merge(detailSection); + var osm = services.osm; + if (!osm) return; // Add warning if user is not logged in + + var hasAuth = osm.authenticated(); + var authWarning = detailSection.selectAll('.auth-warning').data(hasAuth ? [] : [0]); + authWarning.exit().transition().duration(200).style('opacity', 0).remove(); + var authEnter = authWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning auth-warning').style('opacity', 0); + authEnter.call(svgIcon('#iD-icon-alert', 'inline')); + authEnter.append('span').html(_t.html('note.login')); + 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) { + d3_event.preventDefault(); + osm.authenticate(); + }); + authEnter.transition().duration(200).style('opacity', 1); + var prose = detailSection.selectAll('.note-save-prose').data(hasAuth ? [0] : []); + prose.exit().remove(); + prose = prose.enter().append('p').attr('class', 'note-save-prose').html(_t.html('note.upload_explanation')).merge(prose); + osm.userDetails(function (err, user) { + if (err) return; + var userLink = select(document.createElement('div')); + + if (user.image_url) { + userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon'); + } + + userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank'); + prose.html(_t.html('note.upload_explanation_with_user', { + user: userLink.html() + })); + }); + } + + function noteSaveButtons(selection) { + var osm = services.osm; + var hasAuth = osm && osm.authenticated(); + + var isSelected = _note && _note.id === context.selectedNoteID(); + + var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_note] : [], function (d) { + return d.status + d.id; + }); // exit + + buttonSection.exit().remove(); // enter + + var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons'); + + if (_note.isNew()) { + buttonEnter.append('button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel')); + buttonEnter.append('button').attr('class', 'button save-button action').html(_t.html('note.save')); + } else { + buttonEnter.append('button').attr('class', 'button status-button action'); + buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('note.comment')); + } // update + + + buttonSection = buttonSection.merge(buttonEnter); + buttonSection.select('.cancel-button') // select and propagate data + .on('click.cancel', clickCancel); + buttonSection.select('.save-button') // select and propagate data + .attr('disabled', isSaveDisabled).on('click.save', clickSave); + buttonSection.select('.status-button') // select and propagate data + .attr('disabled', hasAuth ? null : true).html(function (d) { + var action = d.status === 'open' ? 'close' : 'open'; + var andComment = d.newComment ? '_comment' : ''; + return _t('note.' + action + andComment); + }).on('click.status', clickStatus); + buttonSection.select('.comment-button') // select and propagate data + .attr('disabled', isSaveDisabled).on('click.comment', clickComment); + + function isSaveDisabled(d) { + return hasAuth && d.status === 'open' && d.newComment ? null : true; + } + } + + function clickCancel(d3_event, d) { + this.blur(); // avoid keeping focus on the button - #4641 + + var osm = services.osm; + + if (osm) { + osm.removeNote(d); + } + + context.enter(modeBrowse(context)); + dispatch.call('change'); + } + + function clickSave(d3_event, d) { + this.blur(); // avoid keeping focus on the button - #4641 + + var osm = services.osm; + + if (osm) { + osm.postNoteCreate(d, function (err, note) { + dispatch.call('change', note); + }); + } + } + + function clickStatus(d3_event, d) { + this.blur(); // avoid keeping focus on the button - #4641 + + var osm = services.osm; + + if (osm) { + var setStatus = d.status === 'open' ? 'closed' : 'open'; + osm.postNoteUpdate(d, setStatus, function (err, note) { + dispatch.call('change', note); + }); + } + } + + function clickComment(d3_event, d) { + this.blur(); // avoid keeping focus on the button - #4641 + + var osm = services.osm; + + if (osm) { + osm.postNoteUpdate(d, d.status, function (err, note) { + dispatch.call('change', note); + }); + } + } + + noteEditor.note = function (val) { + if (!arguments.length) return _note; + _note = val; + return noteEditor; + }; + + noteEditor.newNote = function (val) { + if (!arguments.length) return _newNote; + _newNote = val; + return noteEditor; + }; + + return utilRebind(noteEditor, dispatch, 'on'); + } + + function uiSidebar(context) { + var inspector = uiInspector(context); + var dataEditor = uiDataEditor(context); + var noteEditor = uiNoteEditor(context); + var improveOsmEditor = uiImproveOsmEditor(context); + var keepRightEditor = uiKeepRightEditor(context); + var osmoseEditor = uiOsmoseEditor(context); + + var _current; + + var _wasData = false; + var _wasNote = false; + var _wasQaItem = false; // use pointer events on supported platforms; fallback to mouse events + + var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; + + function sidebar(selection) { + var container = context.container(); + var minWidth = 240; + var sidebarWidth; + var containerWidth; + var dragOffset; // Set the initial width constraints + + selection.style('min-width', minWidth + 'px').style('max-width', '400px').style('width', '33.3333%'); + var resizer = selection.append('div').attr('class', 'sidebar-resizer').on(_pointerPrefix + 'down.sidebar-resizer', pointerdown); + var downPointerId, lastClientX, containerLocGetter; + + function pointerdown(d3_event) { + if (downPointerId) return; + if ('button' in d3_event && d3_event.button !== 0) return; + downPointerId = d3_event.pointerId || 'mouse'; + lastClientX = d3_event.clientX; + containerLocGetter = utilFastMouse(container.node()); // offset from edge of sidebar-resizer + + dragOffset = utilFastMouse(resizer.node())(d3_event)[0] - 1; + sidebarWidth = selection.node().getBoundingClientRect().width; + containerWidth = container.node().getBoundingClientRect().width; + var widthPct = sidebarWidth / containerWidth * 100; + selection.style('width', widthPct + '%') // lock in current width + .style('max-width', '85%'); // but allow larger widths + + resizer.classed('dragging', true); + select(window).on('touchmove.sidebar-resizer', function (d3_event) { + // disable page scrolling while resizing on touch input + d3_event.preventDefault(); + }, { + passive: false + }).on(_pointerPrefix + 'move.sidebar-resizer', pointermove).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup); + } + + function pointermove(d3_event) { + if (downPointerId !== (d3_event.pointerId || 'mouse')) return; + d3_event.preventDefault(); + var dx = d3_event.clientX - lastClientX; + lastClientX = d3_event.clientX; + var isRTL = _mainLocalizer.textDirection() === 'rtl'; + var scaleX = isRTL ? 0 : 1; + var xMarginProperty = isRTL ? 'margin-right' : 'margin-left'; + var x = containerLocGetter(d3_event)[0] - dragOffset; + sidebarWidth = isRTL ? containerWidth - x : x; + var isCollapsed = selection.classed('collapsed'); + var shouldCollapse = sidebarWidth < minWidth; + selection.classed('collapsed', shouldCollapse); + + if (shouldCollapse) { + if (!isCollapsed) { + selection.style(xMarginProperty, '-400px').style('width', '400px'); + context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]); + } + } else { + var widthPct = sidebarWidth / containerWidth * 100; + selection.style(xMarginProperty, null).style('width', widthPct + '%'); + + if (isCollapsed) { + context.ui().onResize([-sidebarWidth * scaleX, 0]); + } else { + context.ui().onResize([-dx * scaleX, 0]); + } + } + } + + function pointerup(d3_event) { + if (downPointerId !== (d3_event.pointerId || 'mouse')) return; + downPointerId = null; + resizer.classed('dragging', false); + select(window).on('touchmove.sidebar-resizer', null).on(_pointerPrefix + 'move.sidebar-resizer', null).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null); + } + + var featureListWrap = selection.append('div').attr('class', 'feature-list-pane').call(uiFeatureList(context)); + var inspectorWrap = selection.append('div').attr('class', 'inspector-hidden inspector-wrap'); + + var hoverModeSelect = function hoverModeSelect(targets) { + context.container().selectAll('.feature-list-item button').classed('hover', false); + + if (context.selectedIDs().length > 1 && targets && targets.length) { + var elements = context.container().selectAll('.feature-list-item button').filter(function (node) { + return targets.indexOf(node) !== -1; + }); + + if (!elements.empty()) { + elements.classed('hover', true); + } + } + }; + + sidebar.hoverModeSelect = throttle(hoverModeSelect, 200); + + function hover(targets) { + var datum = targets && targets.length && targets[0]; + + if (datum && datum.__featurehash__) { + // hovering on data + _wasData = true; + sidebar.show(dataEditor.datum(datum)); + selection.selectAll('.sidebar-component').classed('inspector-hover', true); + } else if (datum instanceof osmNote) { + if (context.mode().id === 'drag-note') return; + _wasNote = true; + var osm = services.osm; + + if (osm) { + datum = osm.getNote(datum.id); // marker may contain stale data - get latest + } + + sidebar.show(noteEditor.note(datum)); + selection.selectAll('.sidebar-component').classed('inspector-hover', true); + } else if (datum instanceof QAItem) { + _wasQaItem = true; + var errService = services[datum.service]; + + if (errService) { + // marker may contain stale data - get latest + datum = errService.getError(datum.id); + } // Currently only three possible services + + + var errEditor; + + if (datum.service === 'keepRight') { + errEditor = keepRightEditor; + } else if (datum.service === 'osmose') { + errEditor = osmoseEditor; + } else { + errEditor = improveOsmEditor; + } + + context.container().selectAll('.qaItem.' + datum.service).classed('hover', function (d) { + return d.id === datum.id; + }); + sidebar.show(errEditor.error(datum)); + selection.selectAll('.sidebar-component').classed('inspector-hover', true); + } else if (!_current && datum instanceof osmEntity) { + featureListWrap.classed('inspector-hidden', true); + inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', true); + + if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') { + inspector.state('hover').entityIDs([datum.id]).newFeature(false); + inspectorWrap.call(inspector); + } + } else if (!_current) { + featureListWrap.classed('inspector-hidden', false); + inspectorWrap.classed('inspector-hidden', true); + inspector.state('hide'); + } else if (_wasData || _wasNote || _wasQaItem) { + _wasNote = false; + _wasData = false; + _wasQaItem = false; + context.container().selectAll('.note').classed('hover', false); + context.container().selectAll('.qaItem').classed('hover', false); + sidebar.hide(); + } + } + + sidebar.hover = throttle(hover, 200); + + sidebar.intersects = function (extent) { + var rect = selection.node().getBoundingClientRect(); + return extent.intersects([context.projection.invert([0, rect.height]), context.projection.invert([rect.width, 0])]); + }; + + sidebar.select = function (ids, newFeature) { + sidebar.hide(); + + if (ids && ids.length) { + var entity = ids.length === 1 && context.entity(ids[0]); + + if (entity && newFeature && selection.classed('collapsed')) { + // uncollapse the sidebar + var extent = entity.extent(context.graph()); + sidebar.expand(sidebar.intersects(extent)); + } + + featureListWrap.classed('inspector-hidden', true); + inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', false); // reload the UI even if the ids are the same since the entities + // themselves may have changed + + inspector.state('select').entityIDs(ids).newFeature(newFeature); + inspectorWrap.call(inspector); + } else { + inspector.state('hide'); + } + }; + + sidebar.showPresetList = function () { + inspector.showList(); + }; + + sidebar.show = function (component, element) { + featureListWrap.classed('inspector-hidden', true); + inspectorWrap.classed('inspector-hidden', true); + if (_current) _current.remove(); + _current = selection.append('div').attr('class', 'sidebar-component').call(component, element); + }; + + sidebar.hide = function () { + featureListWrap.classed('inspector-hidden', false); + inspectorWrap.classed('inspector-hidden', true); + if (_current) _current.remove(); + _current = null; + }; + + sidebar.expand = function (moveMap) { + if (selection.classed('collapsed')) { + sidebar.toggle(moveMap); + } + }; + + sidebar.collapse = function (moveMap) { + if (!selection.classed('collapsed')) { + sidebar.toggle(moveMap); + } + }; + + sidebar.toggle = function (moveMap) { + // Don't allow sidebar to toggle when the user is in the walkthrough. + if (context.inIntro()) return; + var isCollapsed = selection.classed('collapsed'); + var isCollapsing = !isCollapsed; + var isRTL = _mainLocalizer.textDirection() === 'rtl'; + var scaleX = isRTL ? 0 : 1; + var xMarginProperty = isRTL ? 'margin-right' : 'margin-left'; + sidebarWidth = selection.node().getBoundingClientRect().width; // switch from % to px + + selection.style('width', sidebarWidth + 'px'); + var startMargin, endMargin, lastMargin; + + if (isCollapsing) { + startMargin = lastMargin = 0; + endMargin = -sidebarWidth; + } else { + startMargin = lastMargin = -sidebarWidth; + endMargin = 0; + } + + if (!isCollapsing) { + // unhide the sidebar's content before it transitions onscreen + selection.classed('collapsed', isCollapsing); + } + + selection.transition().style(xMarginProperty, endMargin + 'px').tween('panner', function () { + var i = d3_interpolateNumber(startMargin, endMargin); + return function (t) { + var dx = lastMargin - Math.round(i(t)); + lastMargin = lastMargin - dx; + context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]); + }; + }).on('end', function () { + if (isCollapsing) { + // hide the sidebar's content after it transitions offscreen + selection.classed('collapsed', isCollapsing); + } // switch back from px to % + + + if (!isCollapsing) { + var containerWidth = container.node().getBoundingClientRect().width; + var widthPct = sidebarWidth / containerWidth * 100; + selection.style(xMarginProperty, null).style('width', widthPct + '%'); + } + }); + }; // toggle the sidebar collapse when double-clicking the resizer + + + resizer.on('dblclick', function (d3_event) { + d3_event.preventDefault(); + + if (d3_event.sourceEvent) { + d3_event.sourceEvent.preventDefault(); + } + + sidebar.toggle(); + }); // ensure hover sidebar is closed when zooming out beyond editable zoom + + context.map().on('crossEditableZoom.sidebar', function (within) { + if (!within && !selection.select('.inspector-hover').empty()) { + hover([]); + } + }); + } + + sidebar.showPresetList = function () {}; + + sidebar.hover = function () {}; + + sidebar.hover.cancel = function () {}; + + sidebar.intersects = function () {}; + + sidebar.select = function () {}; + + sidebar.show = function () {}; + + sidebar.hide = function () {}; + + sidebar.expand = function () {}; + + sidebar.collapse = function () {}; + + sidebar.toggle = function () {}; + + return sidebar; + } + + function uiSourceSwitch(context) { + var keys; + + function click(d3_event) { + d3_event.preventDefault(); + var osm = context.connection(); + if (!osm) return; + if (context.inIntro()) return; + if (context.history().hasChanges() && !window.confirm(_t('source_switch.lose_changes'))) return; + var isLive = select(this).classed('live'); + isLive = !isLive; + context.enter(modeBrowse(context)); + context.history().clearSaved(); // remove saved history + + context.flush(); // remove stored data + + select(this).html(isLive ? _t.html('source_switch.live') : _t.html('source_switch.dev')).classed('live', isLive).classed('chip', isLive); + osm["switch"](isLive ? keys[0] : keys[1]); // switch connection (warning: dispatches 'change' event) + } + + var sourceSwitch = function sourceSwitch(selection) { + selection.append('a').attr('href', '#').html(_t.html('source_switch.live')).attr('class', 'live chip').on('click', click); + }; + + sourceSwitch.keys = function (_) { + if (!arguments.length) return keys; + keys = _; + return sourceSwitch; + }; + + return sourceSwitch; + } + + function uiSpinner(context) { + var osm = context.connection(); + return function (selection) { + var img = selection.append('img').attr('src', context.imagePath('loader-black.gif')).style('opacity', 0); + + if (osm) { + osm.on('loading.spinner', function () { + img.transition().style('opacity', 1); + }).on('loaded.spinner', function () { + img.transition().style('opacity', 0); + }); + } + }; + } + + function uiSplash(context) { + return function (selection) { + // Exception - if there are restorable changes, skip this splash screen. + // This is because we currently only support one `uiModal` at a time + // and we need to show them `uiRestore`` instead of this one. + if (context.history().hasRestorableChanges()) return; // If user has not seen this version of the privacy policy, show the splash again. + + var updateMessage = ''; + var sawPrivacyVersion = corePreferences('sawPrivacyVersion'); + var showSplash = !corePreferences('sawSplash'); + + if (sawPrivacyVersion !== context.privacyVersion) { + updateMessage = _t('splash.privacy_update'); + showSplash = true; + } + + if (!showSplash) return; + corePreferences('sawSplash', true); + corePreferences('sawPrivacyVersion', context.privacyVersion); // fetch intro graph data now, while user is looking at the splash screen + + _mainFileFetcher.get('intro_graph'); + var modalSelection = uiModal(selection); + modalSelection.select('.modal').attr('class', 'modal-splash modal'); + var introModal = modalSelection.select('.content').append('div').attr('class', 'fillL'); + introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('splash.welcome')); + var modalSection = introModal.append('div').attr('class', 'modal-section'); + modalSection.append('p').html(_t.html('splash.text', { + version: context.version, + website: 'changelog', + github: 'github.com' + })); + modalSection.append('p').html(_t.html('splash.privacy', { + updateMessage: updateMessage, + privacyLink: '' + _t('splash.privacy_policy') + '' + })); + var buttonWrap = introModal.append('div').attr('class', 'modal-actions'); + var walkthrough = buttonWrap.append('button').attr('class', 'walkthrough').on('click', function () { + context.container().call(uiIntro(context)); + modalSelection.close(); + }); + walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough'); + walkthrough.append('div').html(_t.html('splash.walkthrough')); + var startEditing = buttonWrap.append('button').attr('class', 'start-editing').on('click', modalSelection.close); + startEditing.append('svg').attr('class', 'logo logo-features').append('use').attr('xlink:href', '#iD-logo-features'); + startEditing.append('div').html(_t.html('splash.start')); + modalSelection.select('button.close').attr('class', 'hide'); + }; + } + + function uiStatus(context) { + var osm = context.connection(); + return function (selection) { + if (!osm) return; + + function update(err, apiStatus) { + selection.html(''); + + if (err) { + if (apiStatus === 'connectionSwitched') { + // if the connection was just switched, we can't rely on + // the status (we're getting the status of the previous api) + return; + } else if (apiStatus === 'rateLimited') { + selection.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) { + d3_event.preventDefault(); + osm.authenticate(); + }); + } else { + // don't allow retrying too rapidly + var throttledRetry = throttle(function () { + // try loading the visible tiles + context.loadTiles(context.projection); // manually reload the status too in case all visible tiles were already loaded + + osm.reloadApiStatus(); + }, 2000); // eslint-disable-next-line no-warning-comments + // TODO: nice messages for different error types + + + selection.html(_t.html('osm_api_status.message.error') + ' ').append('a').attr('href', '#') // let the user manually retry their connection directly + .html(_t.html('osm_api_status.retry')).on('click.retry', function (d3_event) { + d3_event.preventDefault(); + throttledRetry(); + }); + } + } else if (apiStatus === 'readonly') { + selection.html(_t.html('osm_api_status.message.readonly')); + } else if (apiStatus === 'offline') { + selection.html(_t.html('osm_api_status.message.offline')); + } + + selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus)); + } + + osm.on('apiStatusChange.uiStatus', update); + context.history().on('storage_error', function () { + selection.html(_t.html('osm_api_status.message.local_storage_full')); + selection.attr('class', 'api-status error'); + }); // reload the status periodically regardless of other factors + + window.setInterval(function () { + osm.reloadApiStatus(); + }, 90000); // load the initial status in case no OSM data was loaded yet + + osm.reloadApiStatus(); + }; + } + + function modeDrawArea(context, wayID, startGraph, button) { + var mode = { + button: button, + id: 'draw-area' + }; + var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawArea', function () { + context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.areas'))(); + }); + mode.wayID = wayID; + + mode.enter = function () { + context.install(behavior); + }; + + mode.exit = function () { + context.uninstall(behavior); + }; + + mode.selectedIDs = function () { + return [wayID]; + }; + + mode.activeID = function () { + return behavior && behavior.activeID() || []; + }; + + return mode; + } + + function modeAddArea(context, mode) { + mode.id = 'add-area'; + var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode); + var defaultTags = { + area: 'yes' + }; + if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'area'); + + function actionClose(wayId) { + return function (graph) { + return graph.replace(graph.entity(wayId).close()); + }; + } + + function start(loc) { + var startGraph = context.graph(); + var node = osmNode({ + loc: loc + }); + var way = osmWay({ + tags: defaultTags + }); + context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id)); + context.enter(modeDrawArea(context, way.id, startGraph, mode.button)); + } + + function startFromWay(loc, edge) { + var startGraph = context.graph(); + var node = osmNode({ + loc: loc + }); + var way = osmWay({ + tags: defaultTags + }); + context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id), actionAddMidpoint({ + loc: loc, + edge: edge + }, node)); + context.enter(modeDrawArea(context, way.id, startGraph, mode.button)); + } + + function startFromNode(node) { + var startGraph = context.graph(); + var way = osmWay({ + tags: defaultTags + }); + context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id)); + context.enter(modeDrawArea(context, way.id, startGraph, mode.button)); + } + + mode.enter = function () { + context.install(behavior); + }; + + mode.exit = function () { + context.uninstall(behavior); + }; + + return mode; + } + + function modeAddLine(context, mode) { + mode.id = 'add-line'; + var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode); + var defaultTags = {}; + if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'line'); + + function start(loc) { + var startGraph = context.graph(); + var node = osmNode({ + loc: loc + }); + var way = osmWay({ + tags: defaultTags + }); + context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id)); + context.enter(modeDrawLine(context, way.id, startGraph, mode.button)); + } + + function startFromWay(loc, edge) { + var startGraph = context.graph(); + var node = osmNode({ + loc: loc + }); + var way = osmWay({ + tags: defaultTags + }); + context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionAddMidpoint({ + loc: loc, + edge: edge + }, node)); + context.enter(modeDrawLine(context, way.id, startGraph, mode.button)); + } + + function startFromNode(node) { + var startGraph = context.graph(); + var way = osmWay({ + tags: defaultTags + }); + context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id)); + context.enter(modeDrawLine(context, way.id, startGraph, mode.button)); + } + + mode.enter = function () { + context.install(behavior); + }; + + mode.exit = function () { + context.uninstall(behavior); + }; + + return mode; + } + + function modeAddPoint(context, mode) { + mode.id = 'add-point'; + var behavior = behaviorDraw(context).on('click', add).on('clickWay', addWay).on('clickNode', addNode).on('cancel', cancel).on('finish', cancel); + var defaultTags = {}; + if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point'); + + function add(loc) { + var node = osmNode({ + loc: loc, + tags: defaultTags + }); + context.perform(actionAddEntity(node), _t('operations.add.annotation.point')); + enterSelectMode(node); + } + + function addWay(loc, edge) { + var node = osmNode({ + tags: defaultTags + }); + context.perform(actionAddMidpoint({ + loc: loc, + edge: edge + }, node), _t('operations.add.annotation.vertex')); + enterSelectMode(node); + } + + function enterSelectMode(node) { + context.enter(modeSelect(context, [node.id]).newFeature(true)); + } + + function addNode(node) { + if (Object.keys(defaultTags).length === 0) { + enterSelectMode(node); + return; + } + + var tags = Object.assign({}, node.tags); // shallow copy + + for (var key in defaultTags) { + tags[key] = defaultTags[key]; + } + + context.perform(actionChangeTags(node.id, tags), _t('operations.add.annotation.point')); + enterSelectMode(node); + } + + function cancel() { + context.enter(modeBrowse(context)); + } + + mode.enter = function () { + context.install(behavior); + }; + + mode.exit = function () { + context.uninstall(behavior); + }; + + return mode; + } + + function modeSelectNote(context, selectedNoteID) { + var mode = { + id: 'select-note', + button: 'browse' + }; + + var _keybinding = utilKeybinding('select-note'); + + var _noteEditor = uiNoteEditor(context).on('change', function () { + context.map().pan([0, 0]); // trigger a redraw + + var note = checkSelectedID(); + if (!note) return; + context.ui().sidebar.show(_noteEditor.note(note)); + }); + + var _behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior]; + var _newFeature = false; + + function checkSelectedID() { + if (!services.osm) return; + var note = services.osm.getNote(selectedNoteID); + + if (!note) { + context.enter(modeBrowse(context)); + } + + return note; + } // class the note as selected, or return to browse mode if the note is gone + + + function selectNote(d3_event, drawn) { + if (!checkSelectedID()) return; + var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID); + + if (selection.empty()) { + // Return to browse mode if selected DOM elements have + // disappeared because the user moved them out of view.. + var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent; + + if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) { + context.enter(modeBrowse(context)); + } + } else { + selection.classed('selected', true); + context.selectedNoteID(selectedNoteID); + } + } + + function esc() { + if (context.container().select('.combobox').size()) return; + context.enter(modeBrowse(context)); + } + + mode.zoomToSelected = function () { + if (!services.osm) return; + var note = services.osm.getNote(selectedNoteID); + + if (note) { + context.map().centerZoomEase(note.loc, 20); + } + }; + + mode.newFeature = function (val) { + if (!arguments.length) return _newFeature; + _newFeature = val; + return mode; + }; + + mode.enter = function () { + var note = checkSelectedID(); + if (!note) return; + + _behaviors.forEach(context.install); + + _keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true); + + select(document).call(_keybinding); + selectNote(); + var sidebar = context.ui().sidebar; + sidebar.show(_noteEditor.note(note).newNote(_newFeature)); // expand the sidebar, avoid obscuring the note if needed + + sidebar.expand(sidebar.intersects(note.extent())); + context.map().on('drawn.select', selectNote); + }; + + mode.exit = function () { + _behaviors.forEach(context.uninstall); + + select(document).call(_keybinding.unbind); + context.surface().selectAll('.layer-notes .selected').classed('selected hover', false); + context.map().on('drawn.select', null); + context.ui().sidebar.hide(); + context.selectedNoteID(null); + }; + + return mode; + } + + function modeAddNote(context) { + var mode = { + id: 'add-note', + button: 'note', + description: _t.html('modes.add_note.description'), + key: _t('modes.add_note.key') + }; + var behavior = behaviorDraw(context).on('click', add).on('cancel', cancel).on('finish', cancel); + + function add(loc) { + var osm = services.osm; + if (!osm) return; + var note = osmNote({ + loc: loc, + status: 'open', + comments: [] + }); + osm.replaceNote(note); // force a reraw (there is no history change that would otherwise do this) + + context.map().pan([0, 0]); + context.selectedNoteID(note.id).enter(modeSelectNote(context, note.id).newFeature(true)); + } + + function cancel() { + context.enter(modeBrowse(context)); + } + + mode.enter = function () { + context.install(behavior); + }; + + mode.exit = function () { + context.uninstall(behavior); + }; + + return mode; + } + + var JXON = new function () { + var sValueProp = 'keyValue', + sAttributesProp = 'keyAttributes', + sAttrPref = '@', + + /* you can customize these values */ + aCache = [], + rIsNull = /^\s*$/, + rIsBool = /^(?:true|false)$/i; + + function parseText(sValue) { + if (rIsNull.test(sValue)) { + return null; + } + + if (rIsBool.test(sValue)) { + return sValue.toLowerCase() === 'true'; + } + + if (isFinite(sValue)) { + return parseFloat(sValue); + } + + if (isFinite(Date.parse(sValue))) { + return new Date(sValue); + } + + return sValue; + } + + function EmptyTree() {} + + EmptyTree.prototype.toString = function () { + return 'null'; + }; + + EmptyTree.prototype.valueOf = function () { + return null; + }; + + function objectify(vValue) { + return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue); + } + + function createObjTree(oParentNode, nVerb, bFreeze, bNesteAttr) { + var nLevelStart = aCache.length, + bChildren = oParentNode.hasChildNodes(), + bAttributes = oParentNode.hasAttributes(), + bHighVerb = Boolean(nVerb & 2); + var sProp, + vContent, + nLength = 0, + sCollectedTxt = '', + vResult = bHighVerb ? {} : + /* put here the default value for empty nodes: */ + true; + + if (bChildren) { + for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) { + oNode = oParentNode.childNodes.item(nItem); + + if (oNode.nodeType === 4) { + /* nodeType is 'CDATASection' (4) */ + sCollectedTxt += oNode.nodeValue; + } else if (oNode.nodeType === 3) { + /* nodeType is 'Text' (3) */ + sCollectedTxt += oNode.nodeValue.trim(); + } else if (oNode.nodeType === 1 && !oNode.prefix) { + /* nodeType is 'Element' (1) */ + aCache.push(oNode); + } + } + } + + var nLevelEnd = aCache.length, + vBuiltVal = parseText(sCollectedTxt); + + if (!bHighVerb && (bChildren || bAttributes)) { + vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; + } + + for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) { + sProp = aCache[nElId].nodeName.toLowerCase(); + vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr); + + if (vResult.hasOwnProperty(sProp)) { + if (vResult[sProp].constructor !== Array) { + vResult[sProp] = [vResult[sProp]]; + } + + vResult[sProp].push(vContent); + } else { + vResult[sProp] = vContent; + nLength++; + } + } + + if (bAttributes) { + var nAttrLen = oParentNode.attributes.length, + sAPrefix = bNesteAttr ? '' : sAttrPref, + oAttrParent = bNesteAttr ? {} : vResult; + + for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) { + oAttrib = oParentNode.attributes.item(nAttrib); + oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim()); + } + + if (bNesteAttr) { + if (bFreeze) { + Object.freeze(oAttrParent); + } + + vResult[sAttributesProp] = oAttrParent; + nLength -= nAttrLen - 1; + } + } + + if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) { + vResult[sValueProp] = vBuiltVal; + } else if (!bHighVerb && nLength === 0 && sCollectedTxt) { + vResult = vBuiltVal; + } + + if (bFreeze && (bHighVerb || nLength > 0)) { + Object.freeze(vResult); + } + + aCache.length = nLevelStart; + return vResult; + } + + function loadObjTree(oXMLDoc, oParentEl, oParentObj) { + var vValue, oChild; + + if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) { + oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); + /* verbosity level is 0 */ + } else if (oParentObj.constructor === Date) { + oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString())); + } + + for (var sName in oParentObj) { + vValue = oParentObj[sName]; + + if (isFinite(sName) || vValue instanceof Function) { + continue; + } + /* verbosity level is 0 */ + + + if (sName === sValueProp) { + if (vValue !== null && vValue !== true) { + oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); + } + } else if (sName === sAttributesProp) { + /* verbosity level is 3 */ + for (var sAttrib in vValue) { + oParentEl.setAttribute(sAttrib, vValue[sAttrib]); + } + } else if (sName.charAt(0) === sAttrPref) { + oParentEl.setAttribute(sName.slice(1), vValue); + } else if (vValue.constructor === Array) { + for (var nItem = 0; nItem < vValue.length; nItem++) { + oChild = oXMLDoc.createElement(sName); + loadObjTree(oXMLDoc, oChild, vValue[nItem]); + oParentEl.appendChild(oChild); + } + } else { + oChild = oXMLDoc.createElement(sName); + + if (vValue instanceof Object) { + loadObjTree(oXMLDoc, oChild, vValue); + } else if (vValue !== null && vValue !== true) { + oChild.appendChild(oXMLDoc.createTextNode(vValue.toString())); + } + + oParentEl.appendChild(oChild); + } + } + } + + this.build = function (oXMLParent, nVerbosity + /* optional */ + , bFreeze + /* optional */ + , bNesteAttributes + /* optional */ + ) { + var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 : + /* put here the default verbosity level: */ + 1; + + return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3); + }; + + this.unbuild = function (oObjTree) { + var oNewDoc = document.implementation.createDocument('', '', null); + loadObjTree(oNewDoc, oNewDoc, oObjTree); + return oNewDoc; + }; + + this.stringify = function (oObjTree) { + return new XMLSerializer().serializeToString(JXON.unbuild(oObjTree)); + }; + }(); // var myObject = JXON.build(doc); + // we got our javascript object! try: alert(JSON.stringify(myObject)); + // var newDoc = JXON.unbuild(myObject); + // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc)); + + function uiConflicts(context) { + var dispatch = dispatch$8('cancel', 'save'); + var keybinding = utilKeybinding('conflicts'); + + var _origChanges; + + var _conflictList; + + var _shownConflictIndex; + + function keybindingOn() { + select(document).call(keybinding.on('⎋', cancel, true)); + } + + function keybindingOff() { + select(document).call(keybinding.unbind); + } + + function tryAgain() { + keybindingOff(); + dispatch.call('save'); + } + + function cancel() { + keybindingOff(); + dispatch.call('cancel'); + } + + function conflicts(selection) { + keybindingOn(); + var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL'); + headerEnter.append('button').attr('class', 'fr').on('click', cancel).call(svgIcon('#iD-icon-close')); + headerEnter.append('h3').html(_t.html('save.conflict.header')); + var bodyEnter = selection.selectAll('.body').data([0]).enter().append('div').attr('class', 'body fillL'); + var conflictsHelpEnter = bodyEnter.append('div').attr('class', 'conflicts-help').html(_t.html('save.conflict.help')); // Download changes link + + var detected = utilDetect(); + var changeset = new osmChangeset(); + delete changeset.id; // Export without changeset_id + + var data = JXON.stringify(changeset.osmChangeJXON(_origChanges)); + var blob = new Blob([data], { + type: 'text/xml;charset=utf-8;' + }); + var fileName = 'changes.osc'; + var linkEnter = conflictsHelpEnter.selectAll('.download-changes').append('a').attr('class', 'download-changes'); + + if (detected.download) { + // All except IE11 and Edge + linkEnter // download the data as a file + .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName); + } else { + // IE11 and Edge + linkEnter // open data uri in a new tab + .attr('target', '_blank').on('click.download', function () { + navigator.msSaveBlob(blob, fileName); + }); + } + + linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('save.conflict.download_changes')); + bodyEnter.append('div').attr('class', 'conflict-container fillL3').call(showConflict, 0); + bodyEnter.append('div').attr('class', 'conflicts-done').attr('opacity', 0).style('display', 'none').html(_t.html('save.conflict.done')); + var buttonsEnter = bodyEnter.append('div').attr('class', 'buttons col12 joined conflicts-buttons'); + buttonsEnter.append('button').attr('disabled', _conflictList.length > 1).attr('class', 'action conflicts-button col6').html(_t.html('save.title')).on('click.try_again', tryAgain); + buttonsEnter.append('button').attr('class', 'secondary-action conflicts-button col6').html(_t.html('confirm.cancel')).on('click.cancel', cancel); + } + + function showConflict(selection, index) { + index = utilWrap(index, _conflictList.length); + _shownConflictIndex = index; + var parent = select(selection.node().parentNode); // enable save button if this is the last conflict being reviewed.. + + if (index === _conflictList.length - 1) { + window.setTimeout(function () { + parent.select('.conflicts-button').attr('disabled', null); + parent.select('.conflicts-done').transition().attr('opacity', 1).style('display', 'block'); + }, 250); + } + + var conflict = selection.selectAll('.conflict').data([_conflictList[index]]); + conflict.exit().remove(); + var conflictEnter = conflict.enter().append('div').attr('class', 'conflict'); + conflictEnter.append('h4').attr('class', 'conflict-count').html(_t.html('save.conflict.count', { + num: index + 1, + total: _conflictList.length + })); + conflictEnter.append('a').attr('class', 'conflict-description').attr('href', '#').html(function (d) { + return d.name; + }).on('click', function (d3_event, d) { + d3_event.preventDefault(); + zoomToEntity(d.id); + }); + var details = conflictEnter.append('div').attr('class', 'conflict-detail-container'); + details.append('ul').attr('class', 'conflict-detail-list').selectAll('li').data(function (d) { + return d.details || []; + }).enter().append('li').attr('class', 'conflict-detail-item').html(function (d) { + return d; + }); + details.append('div').attr('class', 'conflict-choices').call(addChoices); + details.append('div').attr('class', 'conflict-nav-buttons joined cf').selectAll('button').data(['previous', 'next']).enter().append('button').html(function (d) { + return _t.html('save.conflict.' + d); + }).attr('class', 'conflict-nav-button action col6').attr('disabled', function (d, i) { + return i === 0 && index === 0 || i === 1 && index === _conflictList.length - 1 || null; + }).on('click', function (d3_event, d) { + d3_event.preventDefault(); + var container = parent.selectAll('.conflict-container'); + var sign = d === 'previous' ? -1 : 1; + container.selectAll('.conflict').remove(); + container.call(showConflict, index + sign); + }); + } + + function addChoices(selection) { + var choices = selection.append('ul').attr('class', 'layer-list').selectAll('li').data(function (d) { + return d.choices || []; + }); // enter + + var choicesEnter = choices.enter().append('li').attr('class', 'layer'); + var labelEnter = choicesEnter.append('label'); + labelEnter.append('input').attr('type', 'radio').attr('name', function (d) { + return d.id; + }).on('change', function (d3_event, d) { + var ul = this.parentNode.parentNode.parentNode; + ul.__data__.chosen = d.id; + choose(d3_event, ul, d); + }); + labelEnter.append('span').html(function (d) { + return d.text; + }); // update + + choicesEnter.merge(choices).each(function (d) { + var ul = this.parentNode; + + if (ul.__data__.chosen === d.id) { + choose(null, ul, d); + } + }); + } + + function choose(d3_event, ul, datum) { + if (d3_event) d3_event.preventDefault(); + select(ul).selectAll('li').classed('active', function (d) { + return d === datum; + }).selectAll('input').property('checked', function (d) { + return d === datum; + }); + var extent = geoExtent(); + var entity; + entity = context.graph().hasEntity(datum.id); + if (entity) extent._extend(entity.extent(context.graph())); + datum.action(); + entity = context.graph().hasEntity(datum.id); + if (entity) extent._extend(entity.extent(context.graph())); + zoomToEntity(datum.id, extent); + } + + function zoomToEntity(id, extent) { + context.surface().selectAll('.hover').classed('hover', false); + var entity = context.graph().hasEntity(id); + + if (entity) { + if (extent) { + context.map().trimmedExtent(extent); + } else { + context.map().zoomToEase(entity); + } + + context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true); + } + } // The conflict list should be an array of objects like: + // { + // id: id, + // name: entityName(local), + // details: merge.conflicts(), + // chosen: 1, + // choices: [ + // choice(id, keepMine, forceLocal), + // choice(id, keepTheirs, forceRemote) + // ] + // } + + + conflicts.conflictList = function (_) { + if (!arguments.length) return _conflictList; + _conflictList = _; + return conflicts; + }; + + conflicts.origChanges = function (_) { + if (!arguments.length) return _origChanges; + _origChanges = _; + return conflicts; + }; + + conflicts.shownEntityIds = function () { + if (_conflictList && typeof _shownConflictIndex === 'number') { + return [_conflictList[_shownConflictIndex].id]; + } + + return []; + }; + + return utilRebind(conflicts, dispatch, 'on'); + } + + function uiConfirm(selection) { + var modalSelection = uiModal(selection); + modalSelection.select('.modal').classed('modal-alert', true); + var section = modalSelection.select('.content'); + section.append('div').attr('class', 'modal-section header'); + section.append('div').attr('class', 'modal-section message-text'); + var buttons = section.append('div').attr('class', 'modal-section buttons cf'); + + modalSelection.okButton = function () { + buttons.append('button').attr('class', 'button ok-button action').on('click.confirm', function () { + modalSelection.remove(); + }).html(_t.html('confirm.okay')).node().focus(); + return modalSelection; + }; + + return modalSelection; + } + + function uiChangesetEditor(context) { + var dispatch = dispatch$8('change'); + var formFields = uiFormFields(context); + var commentCombo = uiCombobox(context, 'comment').caseSensitive(true); + + var _fieldsArr; + + var _tags; + + var _changesetID; + + function changesetEditor(selection) { + render(selection); + } + + function render(selection) { + var initial = false; + + if (!_fieldsArr) { + initial = true; + var presets = _mainPresetIndex; + _fieldsArr = [uiField(context, presets.field('comment'), null, { + show: true, + revert: false + }), uiField(context, presets.field('source'), null, { + show: false, + revert: false + }), uiField(context, presets.field('hashtags'), null, { + show: false, + revert: false + })]; + + _fieldsArr.forEach(function (field) { + field.on('change', function (t, onInput) { + dispatch.call('change', field, undefined, t, onInput); + }); + }); + } + + _fieldsArr.forEach(function (field) { + field.tags(_tags); + }); + + selection.call(formFields.fieldsArr(_fieldsArr)); + + if (initial) { + var commentField = selection.select('.form-field-comment textarea'); + var commentNode = commentField.node(); + + if (commentNode) { + commentNode.focus(); + commentNode.select(); + } // trigger a 'blur' event so that comment field can be cleaned + // and checked for hashtags, even if retrieved from localstorage + + + utilTriggerEvent(commentField, 'blur'); + var osm = context.connection(); + + if (osm) { + osm.userChangesets(function (err, changesets) { + if (err) return; + var comments = changesets.map(function (changeset) { + var comment = changeset.tags.comment; + return comment ? { + title: comment, + value: comment + } : null; + }).filter(Boolean); + commentField.call(commentCombo.data(utilArrayUniqBy(comments, 'title'))); + }); + } + } // Add warning if comment mentions Google + + + var hasGoogle = _tags.comment.match(/google/i); + + var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning').data(hasGoogle ? [0] : []); + commentWarning.exit().transition().duration(200).style('opacity', 0).remove(); + var commentEnter = commentWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning comment-warning').style('opacity', 0); + commentEnter.append('a').attr('target', '_blank').call(svgIcon('#iD-icon-alert', 'inline')).attr('href', _t('commit.google_warning_link')).append('span').html(_t.html('commit.google_warning')); + commentEnter.transition().duration(200).style('opacity', 1); + } + + changesetEditor.tags = function (_) { + if (!arguments.length) return _tags; + _tags = _; // Don't reset _fieldsArr here. + + return changesetEditor; + }; + + changesetEditor.changesetID = function (_) { + if (!arguments.length) return _changesetID; + if (_changesetID === _) return changesetEditor; + _changesetID = _; + _fieldsArr = null; + return changesetEditor; + }; + + return utilRebind(changesetEditor, dispatch, 'on'); + } + + function uiSectionChanges(context) { + var detected = utilDetect(); + var _discardTags = {}; + _mainFileFetcher.get('discarded').then(function (d) { + _discardTags = d; + })["catch"](function () { + /* ignore */ + }); + var section = uiSection('changes-list', context).label(function () { + var history = context.history(); + var summary = history.difference().summary(); + return _t('inspector.title_count', { + title: _t.html('commit.changes'), + count: summary.length + }); + }).disclosureContent(renderDisclosureContent); + + function renderDisclosureContent(selection) { + var history = context.history(); + var summary = history.difference().summary(); + var container = selection.selectAll('.commit-section').data([0]); + var containerEnter = container.enter().append('div').attr('class', 'commit-section'); + containerEnter.append('ul').attr('class', 'changeset-list'); + container = containerEnter.merge(container); + var items = container.select('ul').selectAll('li').data(summary); + var itemsEnter = items.enter().append('li').attr('class', 'change-item'); + var buttons = itemsEnter.append('button').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click); + buttons.each(function (d) { + select(this).call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType)); + }); + buttons.append('span').attr('class', 'change-type').html(function (d) { + return _t.html('commit.' + d.changeType) + ' '; + }); + buttons.append('strong').attr('class', 'entity-type').html(function (d) { + var matched = _mainPresetIndex.match(d.entity, d.graph); + return matched && matched.name() || utilDisplayType(d.entity.id); + }); + buttons.append('span').attr('class', 'entity-name').html(function (d) { + var name = utilDisplayName(d.entity) || '', + string = ''; + + if (name !== '') { + string += ':'; + } + + return string += ' ' + name; + }); + items = itemsEnter.merge(items); // Download changeset link + + var changeset = new osmChangeset().update({ + id: undefined + }); + var changes = history.changes(actionDiscardTags(history.difference(), _discardTags)); + delete changeset.id; // Export without chnageset_id + + var data = JXON.stringify(changeset.osmChangeJXON(changes)); + var blob = new Blob([data], { + type: 'text/xml;charset=utf-8;' + }); + var fileName = 'changes.osc'; + var linkEnter = container.selectAll('.download-changes').data([0]).enter().append('a').attr('class', 'download-changes'); + + if (detected.download) { + // All except IE11 and Edge + linkEnter // download the data as a file + .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName); + } else { + // IE11 and Edge + linkEnter // open data uri in a new tab + .attr('target', '_blank').on('click.download', function () { + navigator.msSaveBlob(blob, fileName); + }); + } + + linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('commit.download_changes')); + + function mouseover(d) { + if (d.entity) { + context.surface().selectAll(utilEntityOrMemberSelector([d.entity.id], context.graph())).classed('hover', true); + } + } + + function mouseout() { + context.surface().selectAll('.hover').classed('hover', false); + } + + function click(d3_event, change) { + if (change.changeType !== 'deleted') { + var entity = change.entity; + context.map().zoomToEase(entity); + context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true); + } + } + } + + return section; + } + + function uiCommitWarnings(context) { + function commitWarnings(selection) { + var issuesBySeverity = context.validator().getIssuesBySeverity({ + what: 'edited', + where: 'all', + includeDisabledRules: true + }); + + for (var severity in issuesBySeverity) { + var issues = issuesBySeverity[severity]; + + if (severity !== 'error') { + // exclude 'fixme' and similar - #8603 + issues = issues.filter(function (issue) { + return issue.type !== 'help_request'; + }); + } + + var section = severity + '-section'; + var issueItem = severity + '-item'; + var container = selection.selectAll('.' + section).data(issues.length ? [0] : []); + container.exit().remove(); + var containerEnter = container.enter().append('div').attr('class', 'modal-section ' + section + ' fillL2'); + containerEnter.append('h3').html(severity === 'warning' ? _t.html('commit.warnings') : _t.html('commit.errors')); + containerEnter.append('ul').attr('class', 'changeset-list'); + container = containerEnter.merge(container); + var items = container.select('ul').selectAll('li').data(issues, function (d) { + return d.key; + }); + items.exit().remove(); + var itemsEnter = items.enter().append('li').attr('class', issueItem); + var buttons = itemsEnter.append('button').on('mouseover', function (d3_event, d) { + if (d.entityIds) { + context.surface().selectAll(utilEntityOrMemberSelector(d.entityIds, context.graph())).classed('hover', true); + } + }).on('mouseout', function () { + context.surface().selectAll('.hover').classed('hover', false); + }).on('click', function (d3_event, d) { + context.validator().focusIssue(d); + }); + buttons.call(svgIcon('#iD-icon-alert', 'pre-text')); + buttons.append('strong').attr('class', 'issue-message'); + buttons.filter(function (d) { + return d.tooltip; + }).call(uiTooltip().title(function (d) { + return d.tooltip; + }).placement('top')); + items = itemsEnter.merge(items); + items.selectAll('.issue-message').html(function (d) { + return d.message(context); + }); + } + } + + return commitWarnings; + } + + var readOnlyTags = [/^changesets_count$/, /^created_by$/, /^ideditor:/, /^imagery_used$/, /^host$/, /^locale$/, /^warnings:/, /^resolved:/, /^closed:note$/, /^closed:keepright$/, /^closed:improveosm:/, /^closed:osmose:/]; // treat most punctuation (except -, _, +, &) as hashtag delimiters - #4398 + // from https://stackoverflow.com/a/25575009 + + var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g; + function uiCommit(context) { + var dispatch = dispatch$8('cancel'); + + var _userDetails; + + var _selection; + + var changesetEditor = uiChangesetEditor(context).on('change', changeTags); + var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context).on('change', changeTags).readOnlyTags(readOnlyTags); + var commitChanges = uiSectionChanges(context); + var commitWarnings = uiCommitWarnings(context); + + function commit(selection) { + _selection = selection; // Initialize changeset if one does not exist yet. + + if (!context.changeset) initChangeset(); + loadDerivedChangesetTags(); + selection.call(render); + } + + function initChangeset() { + // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899 + var commentDate = +corePreferences('commentDate') || 0; + var currDate = Date.now(); + var cutoff = 2 * 86400 * 1000; // 2 days + + if (commentDate > currDate || currDate - commentDate > cutoff) { + corePreferences('comment', null); + corePreferences('hashtags', null); + corePreferences('source', null); + } // load in explicitly-set values, if any + + + if (context.defaultChangesetComment()) { + corePreferences('comment', context.defaultChangesetComment()); + corePreferences('commentDate', Date.now()); + } + + if (context.defaultChangesetSource()) { + corePreferences('source', context.defaultChangesetSource()); + corePreferences('commentDate', Date.now()); + } + + if (context.defaultChangesetHashtags()) { + corePreferences('hashtags', context.defaultChangesetHashtags()); + corePreferences('commentDate', Date.now()); + } + + var detected = utilDetect(); + var tags = { + comment: corePreferences('comment') || '', + created_by: context.cleanTagValue('iD ' + context.version), + host: context.cleanTagValue(detected.host), + locale: context.cleanTagValue(_mainLocalizer.localeCode()) + }; // call findHashtags initially - this will remove stored + // hashtags if any hashtags are found in the comment - #4304 + + findHashtags(tags, true); + var hashtags = corePreferences('hashtags'); + + if (hashtags) { + tags.hashtags = hashtags; + } + + var source = corePreferences('source'); + + if (source) { + tags.source = source; + } + + var photoOverlaysUsed = context.history().photoOverlaysUsed(); + + if (photoOverlaysUsed.length) { + var sources = (tags.source || '').split(';'); // include this tag for any photo layer + + if (sources.indexOf('streetlevel imagery') === -1) { + sources.push('streetlevel imagery'); + } // add the photo overlays used during editing as sources + + + photoOverlaysUsed.forEach(function (photoOverlay) { + if (sources.indexOf(photoOverlay) === -1) { + sources.push(photoOverlay); + } + }); + tags.source = context.cleanTagValue(sources.join(';')); + } + + context.changeset = new osmChangeset({ + tags: tags + }); + } // Calculates read-only metadata tags based on the user's editing session and applies + // them to the changeset. + + + function loadDerivedChangesetTags() { + var osm = context.connection(); + if (!osm) return; + var tags = Object.assign({}, context.changeset.tags); // shallow copy + // assign tags for imagery used + + var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';')); + tags.imagery_used = imageryUsed || 'None'; // assign tags for closed issues and notes + + var osmClosed = osm.getClosedIDs(); + var itemType; + + if (osmClosed.length) { + tags['closed:note'] = context.cleanTagValue(osmClosed.join(';')); + } + + if (services.keepRight) { + var krClosed = services.keepRight.getClosedIDs(); + + if (krClosed.length) { + tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';')); + } + } + + if (services.improveOSM) { + var iOsmClosed = services.improveOSM.getClosedCounts(); + + for (itemType in iOsmClosed) { + tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString()); + } + } + + if (services.osmose) { + var osmoseClosed = services.osmose.getClosedCounts(); + + for (itemType in osmoseClosed) { + tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString()); + } + } // remove existing issue counts + + + for (var key in tags) { + if (key.match(/(^warnings:)|(^resolved:)/)) { + delete tags[key]; + } + } + + function addIssueCounts(issues, prefix) { + var issuesByType = utilArrayGroupBy(issues, 'type'); + + for (var issueType in issuesByType) { + var issuesOfType = issuesByType[issueType]; + + if (issuesOfType[0].subtype) { + var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype'); + + for (var issueSubtype in issuesBySubtype) { + var issuesOfSubtype = issuesBySubtype[issueSubtype]; + tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString()); + } + } else { + tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString()); + } + } + } // add counts of warnings generated by the user's edits + + + var warnings = context.validator().getIssuesBySeverity({ + what: 'edited', + where: 'all', + includeIgnored: true, + includeDisabledRules: true + }).warning.filter(function (issue) { + return issue.type !== 'help_request'; + }); // exclude 'fixme' and similar - #8603 + + addIssueCounts(warnings, 'warnings'); // add counts of issues resolved by the user's edits + + var resolvedIssues = context.validator().getResolvedIssues(); + addIssueCounts(resolvedIssues, 'resolved'); + context.changeset = context.changeset.update({ + tags: tags + }); + } + + function render(selection) { + var osm = context.connection(); + if (!osm) return; + var header = selection.selectAll('.header').data([0]); + var headerTitle = header.enter().append('div').attr('class', 'header fillL'); + headerTitle.append('div').append('h3').html(_t.html('commit.title')); + headerTitle.append('button').attr('class', 'close').on('click', function () { + dispatch.call('cancel', this); + }).call(svgIcon('#iD-icon-close')); + var body = selection.selectAll('.body').data([0]); + body = body.enter().append('div').attr('class', 'body').merge(body); // Changeset Section + + var changesetSection = body.selectAll('.changeset-editor').data([0]); + changesetSection = changesetSection.enter().append('div').attr('class', 'modal-section changeset-editor').merge(changesetSection); + changesetSection.call(changesetEditor.changesetID(context.changeset.id).tags(context.changeset.tags)); // Warnings + + body.call(commitWarnings); // Upload Explanation + + var saveSection = body.selectAll('.save-section').data([0]); + saveSection = saveSection.enter().append('div').attr('class', 'modal-section save-section fillL').merge(saveSection); + var prose = saveSection.selectAll('.commit-info').data([0]); + + if (prose.enter().size()) { + // first time, make sure to update user details in prose + _userDetails = null; + } + + prose = prose.enter().append('p').attr('class', 'commit-info').html(_t.html('commit.upload_explanation')).merge(prose); // always check if this has changed, but only update prose.html() + // if needed, because it can trigger a style recalculation + + osm.userDetails(function (err, user) { + if (err) return; + if (_userDetails === user) return; // no change + + _userDetails = user; + var userLink = select(document.createElement('div')); + + if (user.image_url) { + userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon'); + } + + userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank'); + prose.html(_t.html('commit.upload_explanation_with_user', { + user: userLink.html() + })); + }); // Request Review + + var requestReview = saveSection.selectAll('.request-review').data([0]); // Enter + + var requestReviewEnter = requestReview.enter().append('div').attr('class', 'request-review'); + var requestReviewDomId = utilUniqueDomId('commit-input-request-review'); + var labelEnter = requestReviewEnter.append('label').attr('for', requestReviewDomId); + + if (!labelEnter.empty()) { + labelEnter.call(uiTooltip().title(_t.html('commit.request_review_info')).placement('top')); + } + + labelEnter.append('input').attr('type', 'checkbox').attr('id', requestReviewDomId); + labelEnter.append('span').html(_t.html('commit.request_review')); // Update + + requestReview = requestReview.merge(requestReviewEnter); + var requestReviewInput = requestReview.selectAll('input').property('checked', isReviewRequested(context.changeset.tags)).on('change', toggleRequestReview); // Buttons + + var buttonSection = saveSection.selectAll('.buttons').data([0]); // enter + + var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons fillL'); + buttonEnter.append('button').attr('class', 'secondary-action button cancel-button').append('span').attr('class', 'label').html(_t.html('commit.cancel')); + var uploadButton = buttonEnter.append('button').attr('class', 'action button save-button'); + uploadButton.append('span').attr('class', 'label').html(_t.html('commit.save')); + var uploadBlockerTooltipText = getUploadBlockerMessage(); // update + + buttonSection = buttonSection.merge(buttonEnter); + buttonSection.selectAll('.cancel-button').on('click.cancel', function () { + dispatch.call('cancel', this); + }); + buttonSection.selectAll('.save-button').classed('disabled', uploadBlockerTooltipText !== null).on('click.save', function () { + if (!select(this).classed('disabled')) { + this.blur(); // avoid keeping focus on the button - #4641 + + for (var key in context.changeset.tags) { + // remove any empty keys before upload + if (!key) delete context.changeset.tags[key]; + } + + context.uploader().save(context.changeset); + } + }); // remove any existing tooltip + + uiTooltip().destroyAny(buttonSection.selectAll('.save-button')); + + if (uploadBlockerTooltipText) { + buttonSection.selectAll('.save-button').call(uiTooltip().title(uploadBlockerTooltipText).placement('top')); + } // Raw Tag Editor + + + var tagSection = body.selectAll('.tag-section.raw-tag-editor').data([0]); + tagSection = tagSection.enter().append('div').attr('class', 'modal-section tag-section raw-tag-editor').merge(tagSection); + tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy + .render); + var changesSection = body.selectAll('.commit-changes-section').data([0]); + changesSection = changesSection.enter().append('div').attr('class', 'modal-section commit-changes-section').merge(changesSection); // Change summary + + changesSection.call(commitChanges.render); + + function toggleRequestReview() { + var rr = requestReviewInput.property('checked'); + updateChangeset({ + review_requested: rr ? 'yes' : undefined + }); + tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy + .render); + } + } + + function getUploadBlockerMessage() { + var errors = context.validator().getIssuesBySeverity({ + what: 'edited', + where: 'all' + }).error; + + if (errors.length) { + return _t('commit.outstanding_errors_message', { + count: errors.length + }); + } else { + var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length; + + if (!hasChangesetComment) { + return _t('commit.comment_needed_message'); + } + } + + return null; + } + + function changeTags(_, changed, onInput) { + if (changed.hasOwnProperty('comment')) { + if (changed.comment === undefined) { + changed.comment = ''; + } + + if (!onInput) { + corePreferences('comment', changed.comment); + corePreferences('commentDate', Date.now()); + } + } + + if (changed.hasOwnProperty('source')) { + if (changed.source === undefined) { + corePreferences('source', null); + } else if (!onInput) { + corePreferences('source', changed.source); + corePreferences('commentDate', Date.now()); + } + } // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset` + + + updateChangeset(changed, onInput); + + if (_selection) { + _selection.call(render); + } + } + + function findHashtags(tags, commentOnly) { + var detectedHashtags = commentHashtags(); + + if (detectedHashtags.length) { + // always remove stored hashtags if there are hashtags in the comment - #4304 + corePreferences('hashtags', null); + } + + if (!detectedHashtags.length || !commentOnly) { + detectedHashtags = detectedHashtags.concat(hashtagHashtags()); + } + + var allLowerCase = new Set(); + return detectedHashtags.filter(function (hashtag) { + // Compare tags as lowercase strings, but keep original case tags + var lowerCase = hashtag.toLowerCase(); + + if (!allLowerCase.has(lowerCase)) { + allLowerCase.add(lowerCase); + return true; + } + + return false; + }); // Extract hashtags from `comment` + + function commentHashtags() { + var matches = (tags.comment || '').replace(/http\S*/g, '') // drop anything that looks like a URL - #4289 + .match(hashtagRegex); + return matches || []; + } // Extract and clean hashtags from `hashtags` + + + function hashtagHashtags() { + var matches = (tags.hashtags || '').split(/[,;\s]+/).map(function (s) { + if (s[0] !== '#') { + s = '#' + s; + } // prepend '#' + + + var matched = s.match(hashtagRegex); + return matched && matched[0]; + }).filter(Boolean); // exclude falsy + + return matches || []; + } + } + + function isReviewRequested(tags) { + var rr = tags.review_requested; + if (rr === undefined) return false; + rr = rr.trim().toLowerCase(); + return !(rr === '' || rr === 'no'); + } + + function updateChangeset(changed, onInput) { + var tags = Object.assign({}, context.changeset.tags); // shallow copy + + Object.keys(changed).forEach(function (k) { + var v = changed[k]; + k = context.cleanTagKey(k); + if (readOnlyTags.indexOf(k) !== -1) return; + + if (v === undefined) { + delete tags[k]; + } else if (onInput) { + tags[k] = v; + } else { + tags[k] = context.cleanTagValue(v); + } + }); + + if (!onInput) { + // when changing the comment, override hashtags with any found in comment. + var commentOnly = changed.hasOwnProperty('comment') && changed.comment !== ''; + var arr = findHashtags(tags, commentOnly); + + if (arr.length) { + tags.hashtags = context.cleanTagValue(arr.join(';')); + corePreferences('hashtags', tags.hashtags); + } else { + delete tags.hashtags; + corePreferences('hashtags', null); + } + } // always update userdetails, just in case user reauthenticates as someone else + + + if (_userDetails && _userDetails.changesets_count !== undefined) { + var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1; // #4283 + + tags.changesets_count = String(changesetsCount); // first 100 edits - new user + + if (changesetsCount <= 100) { + var s; + s = corePreferences('walkthrough_completed'); + + if (s) { + tags['ideditor:walkthrough_completed'] = s; + } + + s = corePreferences('walkthrough_progress'); + + if (s) { + tags['ideditor:walkthrough_progress'] = s; + } + + s = corePreferences('walkthrough_started'); + + if (s) { + tags['ideditor:walkthrough_started'] = s; + } + } + } else { + delete tags.changesets_count; + } + + if (!fastDeepEqual(context.changeset.tags, tags)) { + context.changeset = context.changeset.update({ + tags: tags + }); + } + } + + commit.reset = function () { + context.changeset = null; + }; + + return utilRebind(commit, dispatch, 'on'); + } + + // for punction see https://stackoverflow.com/a/21224179 + + function simplify(str) { + if (typeof str !== 'string') return ''; + 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()); + } + + // `resolveStrings` + // Resolves the text strings for a given community index item + // + // Arguments + // `item`: Object containing the community index item + // `defaults`: Object containing the community index default strings + // `localizerFn?`: optional function we will call to do the localization. + // This function should be like the iD `t()` function that + // accepts a `stringID` and returns a localized string + // + // Returns + // An Object containing all the resolved strings: + // { + // name: 'talk-ru Mailing List', + // url: 'https://lists.openstreetmap.org/listinfo/talk-ru', + // signupUrl: 'https://example.url/signup', + // description: 'A one line description', + // extendedDescription: 'Extended description', + // nameHTML: 'the name', + // urlHTML: 'the url', + // signupUrlHTML: 'the signupUrl', + // descriptionHTML: the description, with urls and signupUrls linkified, + // extendedDescriptionHTML: the extendedDescription with urls and signupUrls linkified + // } + // + + function resolveStrings(item, defaults, localizerFn) { + var itemStrings = Object.assign({}, item.strings); // shallow clone + + var defaultStrings = Object.assign({}, defaults[item.type]); // shallow clone + + var anyToken = new RegExp(/(\{\w+\})/, 'gi'); // Pre-localize the item and default strings + + if (localizerFn) { + if (itemStrings.community) { + var communityID = simplify(itemStrings.community); + itemStrings.community = localizerFn("_communities.".concat(communityID)); + } + + ['name', 'description', 'extendedDescription'].forEach(function (prop) { + if (defaultStrings[prop]) defaultStrings[prop] = localizerFn("_defaults.".concat(item.type, ".").concat(prop)); + if (itemStrings[prop]) itemStrings[prop] = localizerFn("".concat(item.id, ".").concat(prop)); + }); + } + + var replacements = { + account: item.account, + community: itemStrings.community, + signupUrl: itemStrings.signupUrl, + url: itemStrings.url + }; // Resolve URLs first (which may refer to {account}) + + if (!replacements.signupUrl) { + replacements.signupUrl = resolve(itemStrings.signupUrl || defaultStrings.signupUrl); + } + + if (!replacements.url) { + replacements.url = resolve(itemStrings.url || defaultStrings.url); + } + + var resolved = { + name: resolve(itemStrings.name || defaultStrings.name), + url: resolve(itemStrings.url || defaultStrings.url), + signupUrl: resolve(itemStrings.signupUrl || defaultStrings.signupUrl), + description: resolve(itemStrings.description || defaultStrings.description), + extendedDescription: resolve(itemStrings.extendedDescription || defaultStrings.extendedDescription) + }; // Generate linkified strings + + resolved.nameHTML = linkify(resolved.url, resolved.name); + resolved.urlHTML = linkify(resolved.url); + resolved.signupUrlHTML = linkify(resolved.signupUrl); + resolved.descriptionHTML = resolve(itemStrings.description || defaultStrings.description, true); + resolved.extendedDescriptionHTML = resolve(itemStrings.extendedDescription || defaultStrings.extendedDescription, true); + return resolved; + + function resolve(s, addLinks) { + if (!s) return undefined; + var result = s; + + for (var key in replacements) { + var token = "{".concat(key, "}"); + var regex = new RegExp(token, 'g'); + + if (regex.test(result)) { + var replacement = replacements[key]; + + if (!replacement) { + throw new Error("Cannot resolve token: ".concat(token)); + } else { + if (addLinks && (key === 'signupUrl' || key === 'url')) { + replacement = linkify(replacement); + } + + result = result.replace(regex, replacement); + } + } + } // There shouldn't be any leftover tokens in a resolved string + + + var leftovers = result.match(anyToken); + + if (leftovers) { + throw new Error("Cannot resolve tokens: ".concat(leftovers)); + } // Linkify subreddits like `/r/openstreetmap` + // https://github.com/osmlab/osm-community-index/issues/82 + // https://github.com/openstreetmap/iD/issues/4997 + + + if (addLinks && item.type === 'reddit') { + result = result.replace(/(\/r\/\w+\/*)/i, function (match) { + return linkify(resolved.url, match); + }); + } + + return result; + } + + function linkify(url, text) { + if (!url) return undefined; + text = text || url; + return "").concat(text, ""); + } + } + + var _oci = null; + function uiSuccess(context) { + var MAXEVENTS = 2; + var dispatch = dispatch$8('cancel'); + + var _changeset; + + var _location; + + ensureOSMCommunityIndex(); // start fetching the data + + function ensureOSMCommunityIndex() { + var data = _mainFileFetcher; + return Promise.all([data.get('oci_features'), data.get('oci_resources'), data.get('oci_defaults')]).then(function (vals) { + if (_oci) return _oci; // Merge Custom Features + + if (vals[0] && Array.isArray(vals[0].features)) { + _mainLocations.mergeCustomGeoJSON(vals[0]); + } + + var ociResources = Object.values(vals[1].resources); + + if (ociResources.length) { + // Resolve all locationSet features. + return _mainLocations.mergeLocationSets(ociResources).then(function () { + _oci = { + resources: ociResources, + defaults: vals[2].defaults + }; + return _oci; + }); + } else { + _oci = { + resources: [], + // no resources? + defaults: vals[2].defaults + }; + return _oci; + } + }); + } // string-to-date parsing in JavaScript is weird + + + function parseEventDate(when) { + if (!when) return; + var raw = when.trim(); + if (!raw) return; + + if (!/Z$/.test(raw)) { + // if no trailing 'Z', add one + raw += 'Z'; // this forces date to be parsed as a UTC date + } + + var parsed = new Date(raw); + return new Date(parsed.toUTCString().substr(0, 25)); // convert to local timezone + } + + function success(selection) { + var header = selection.append('div').attr('class', 'header fillL'); + header.append('h3').html(_t.html('success.just_edited')); + header.append('button').attr('class', 'close').on('click', function () { + return dispatch.call('cancel'); + }).call(svgIcon('#iD-icon-close')); + var body = selection.append('div').attr('class', 'body save-success fillL'); + var summary = body.append('div').attr('class', 'save-summary'); + summary.append('h3').html(_t.html('success.thank_you' + (_location ? '_location' : ''), { + where: _location + })); + 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')); + var osm = context.connection(); + if (!osm) return; + var changesetURL = osm.changesetURL(_changeset.id); + var table = summary.append('table').attr('class', 'summary-table'); + var row = table.append('tr').attr('class', 'summary-row'); + row.append('td').attr('class', 'cell-icon summary-icon').append('a').attr('target', '_blank').attr('href', changesetURL).append('svg').attr('class', 'logo-small').append('use').attr('xlink:href', '#iD-logo-osm'); + var summaryDetail = row.append('td').attr('class', 'cell-detail summary-detail'); + summaryDetail.append('a').attr('class', 'cell-detail summary-view-on-osm').attr('target', '_blank').attr('href', changesetURL).html(_t.html('success.view_on_osm')); + summaryDetail.append('div').html(_t.html('success.changeset_id', { + changeset_id: "").concat(_changeset.id, "") + })); // Get OSM community index features intersecting the map.. + + ensureOSMCommunityIndex().then(function (oci) { + var loc = context.map().center(); + var validLocations = _mainLocations.locationsAt(loc); // Gather the communities + + var communities = []; + oci.resources.forEach(function (resource) { + var area = validLocations[resource.locationSetID]; + if (!area) return; // Resolve strings + + var localizer = function localizer(stringID) { + return _t.html("community.".concat(stringID)); + }; + + resource.resolved = resolveStrings(resource, oci.defaults, localizer); + communities.push({ + area: area, + order: resource.order || 0, + resource: resource + }); + }); // sort communities by feature area ascending, community order descending + + communities.sort(function (a, b) { + return a.area - b.area || b.order - a.order; + }); + body.call(showCommunityLinks, communities.map(function (c) { + return c.resource; + })); + }); + } + + function showCommunityLinks(selection, resources) { + var communityLinks = selection.append('div').attr('class', 'save-communityLinks'); + communityLinks.append('h3').html(_t.html('success.like_osm')); + var table = communityLinks.append('table').attr('class', 'community-table'); + var row = table.selectAll('.community-row').data(resources); + var rowEnter = row.enter().append('tr').attr('class', 'community-row'); + rowEnter.append('td').attr('class', 'cell-icon community-icon').append('a').attr('target', '_blank').attr('href', function (d) { + return d.resolved.url; + }).append('svg').attr('class', 'logo-small').append('use').attr('xlink:href', function (d) { + return "#community-".concat(d.type); + }); + var communityDetail = rowEnter.append('td').attr('class', 'cell-detail community-detail'); + communityDetail.each(showCommunityDetails); + 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')); + } + + function showCommunityDetails(d) { + var selection = select(this); + var communityID = d.id; + selection.append('div').attr('class', 'community-name').html(d.resolved.nameHTML); + selection.append('div').attr('class', 'community-description').html(d.resolved.descriptionHTML); // Create an expanding section if any of these are present.. + + if (d.resolved.extendedDescriptionHTML || d.languageCodes && d.languageCodes.length) { + selection.append('div').call(uiDisclosure(context, "community-more-".concat(d.id), false).expanded(false).updatePreference(false).label(_t.html('success.more')).content(showMore)); + } + + var nextEvents = (d.events || []).map(function (event) { + event.date = parseEventDate(event.when); + return event; + }).filter(function (event) { + // date is valid and future (or today) + var t = event.date.getTime(); + var now = new Date().setHours(0, 0, 0, 0); + return !isNaN(t) && t >= now; + }).sort(function (a, b) { + // sort by date ascending + return a.date < b.date ? -1 : a.date > b.date ? 1 : 0; + }).slice(0, MAXEVENTS); // limit number of events shown + + if (nextEvents.length) { + selection.append('div').call(uiDisclosure(context, "community-events-".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); + } + + function showMore(selection) { + var more = selection.selectAll('.community-more').data([0]); + var moreEnter = more.enter().append('div').attr('class', 'community-more'); + + if (d.resolved.extendedDescriptionHTML) { + moreEnter.append('div').attr('class', 'community-extended-description').html(d.resolved.extendedDescriptionHTML); + } + + if (d.languageCodes && d.languageCodes.length) { + var languageList = d.languageCodes.map(function (code) { + return _mainLocalizer.languageName(code); + }).join(', '); + moreEnter.append('div').attr('class', 'community-languages').html(_t.html('success.languages', { + languages: languageList + })); + } + } + + function showNextEvents(selection) { + var events = selection.append('div').attr('class', 'community-events'); + var item = events.selectAll('.community-event').data(nextEvents); + var itemEnter = item.enter().append('div').attr('class', 'community-event'); + itemEnter.append('div').attr('class', 'community-event-name').append('a').attr('target', '_blank').attr('href', function (d) { + return d.url; + }).html(function (d) { + var name = d.name; + + if (d.i18n && d.id) { + name = _t("community.".concat(communityID, ".events.").concat(d.id, ".name"), { + "default": name + }); + } + + return name; + }); + itemEnter.append('div').attr('class', 'community-event-when').html(function (d) { + var options = { + weekday: 'short', + day: 'numeric', + month: 'short', + year: 'numeric' + }; + + if (d.date.getHours() || d.date.getMinutes()) { + // include time if it has one + options.hour = 'numeric'; + options.minute = 'numeric'; + } + + return d.date.toLocaleString(_mainLocalizer.localeCode(), options); + }); + itemEnter.append('div').attr('class', 'community-event-where').html(function (d) { + var where = d.where; + + if (d.i18n && d.id) { + where = _t("community.".concat(communityID, ".events.").concat(d.id, ".where"), { + "default": where + }); + } + + return where; + }); + itemEnter.append('div').attr('class', 'community-event-description').html(function (d) { + var description = d.description; + + if (d.i18n && d.id) { + description = _t("community.".concat(communityID, ".events.").concat(d.id, ".description"), { + "default": description + }); + } + + return description; + }); + } + } + + success.changeset = function (val) { + if (!arguments.length) return _changeset; + _changeset = val; + return success; + }; + + success.location = function (val) { + if (!arguments.length) return _location; + _location = val; + return success; + }; + + return utilRebind(success, dispatch, 'on'); + } + + function modeSave(context) { + var mode = { + id: 'save' + }; + var keybinding = utilKeybinding('modeSave'); + var commit = uiCommit(context).on('cancel', cancel); + + var _conflictsUi; // uiConflicts + + + var _location; + + var _success; + + var uploader = context.uploader().on('saveStarted.modeSave', function () { + keybindingOff(); + }) // fire off some async work that we want to be ready later + .on('willAttemptUpload.modeSave', prepareForSuccess).on('progressChanged.modeSave', showProgress).on('resultNoChanges.modeSave', function () { + cancel(); + }).on('resultErrors.modeSave', showErrors).on('resultConflicts.modeSave', showConflicts).on('resultSuccess.modeSave', showSuccess); + + function cancel() { + context.enter(modeBrowse(context)); + } + + function showProgress(num, total) { + var modal = context.container().select('.loading-modal .modal-section'); + var progress = modal.selectAll('.progress').data([0]); // enter/update + + progress.enter().append('div').attr('class', 'progress').merge(progress).text(_t('save.conflict_progress', { + num: num, + total: total + })); + } + + function showConflicts(changeset, conflicts, origChanges) { + var selection = context.container().select('.sidebar').append('div').attr('class', 'sidebar-component'); + context.container().selectAll('.main-content').classed('active', true).classed('inactive', false); + _conflictsUi = uiConflicts(context).conflictList(conflicts).origChanges(origChanges).on('cancel', function () { + context.container().selectAll('.main-content').classed('active', false).classed('inactive', true); + selection.remove(); + keybindingOn(); + uploader.cancelConflictResolution(); + }).on('save', function () { + context.container().selectAll('.main-content').classed('active', false).classed('inactive', true); + selection.remove(); + uploader.processResolvedConflicts(changeset); + }); + selection.call(_conflictsUi); + } + + function showErrors(errors) { + keybindingOn(); + var selection = uiConfirm(context.container()); + selection.select('.modal-section.header').append('h3').text(_t('save.error')); + addErrors(selection, errors); + selection.okButton(); + } + + function addErrors(selection, data) { + var message = selection.select('.modal-section.message-text'); + var items = message.selectAll('.error-container').data(data); + var enter = items.enter().append('div').attr('class', 'error-container'); + enter.append('a').attr('class', 'error-description').attr('href', '#').classed('hide-toggle', true).text(function (d) { + return d.msg || _t('save.unknown_error_details'); + }).on('click', function (d3_event) { + d3_event.preventDefault(); + var error = select(this); + var detail = select(this.nextElementSibling); + var exp = error.classed('expanded'); + detail.style('display', exp ? 'none' : 'block'); + error.classed('expanded', !exp); + }); + var details = enter.append('div').attr('class', 'error-detail-container').style('display', 'none'); + details.append('ul').attr('class', 'error-detail-list').selectAll('li').data(function (d) { + return d.details || []; + }).enter().append('li').attr('class', 'error-detail-item').text(function (d) { + return d; + }); + items.exit().remove(); + } + + function showSuccess(changeset) { + commit.reset(); + + var ui = _success.changeset(changeset).location(_location).on('cancel', function () { + context.ui().sidebar.hide(); + }); + + context.enter(modeBrowse(context).sidebar(ui)); + } + + function keybindingOn() { + select(document).call(keybinding.on('⎋', cancel, true)); + } + + function keybindingOff() { + select(document).call(keybinding.unbind); + } // Reverse geocode current map location so we can display a message on + // the success screen like "Thank you for editing around place, region." + + + function prepareForSuccess() { + _success = uiSuccess(context); + _location = null; + if (!services.geocoder) return; + services.geocoder.reverse(context.map().center(), function (err, result) { + if (err || !result || !result.address) return; + var addr = result.address; + var place = addr && (addr.town || addr.city || addr.county) || ''; + var region = addr && (addr.state || addr.country) || ''; + var separator = place && region ? _t('success.thank_you_where.separator') : ''; + _location = _t('success.thank_you_where.format', { + place: place, + separator: separator, + region: region + }); + }); + } + + mode.selectedIDs = function () { + return _conflictsUi ? _conflictsUi.shownEntityIds() : []; + }; + + mode.enter = function () { + // Show sidebar + context.ui().sidebar.expand(); + + function done() { + context.ui().sidebar.show(commit); + } + + keybindingOn(); + context.container().selectAll('.main-content').classed('active', false).classed('inactive', true); + var osm = context.connection(); + + if (!osm) { + cancel(); + return; + } + + if (osm.authenticated()) { + done(); + } else { + osm.authenticate(function (err) { + if (err) { + cancel(); + } else { + done(); + } + }); + } + }; + + mode.exit = function () { + keybindingOff(); + context.container().selectAll('.main-content').classed('active', true).classed('inactive', false); + context.ui().sidebar.hide(); + }; + + return mode; + } + + function modeSelectError(context, selectedErrorID, selectedErrorService) { + var mode = { + id: 'select-error', + button: 'browse' + }; + var keybinding = utilKeybinding('select-error'); + var errorService = services[selectedErrorService]; + var errorEditor; + + switch (selectedErrorService) { + case 'improveOSM': + errorEditor = uiImproveOsmEditor(context).on('change', function () { + context.map().pan([0, 0]); // trigger a redraw + + var error = checkSelectedID(); + if (!error) return; + context.ui().sidebar.show(errorEditor.error(error)); + }); + break; + + case 'keepRight': + errorEditor = uiKeepRightEditor(context).on('change', function () { + context.map().pan([0, 0]); // trigger a redraw + + var error = checkSelectedID(); + if (!error) return; + context.ui().sidebar.show(errorEditor.error(error)); + }); + break; + + case 'osmose': + errorEditor = uiOsmoseEditor(context).on('change', function () { + context.map().pan([0, 0]); // trigger a redraw + + var error = checkSelectedID(); + if (!error) return; + context.ui().sidebar.show(errorEditor.error(error)); + }); + break; + } + + var behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior]; + + function checkSelectedID() { + if (!errorService) return; + var error = errorService.getError(selectedErrorID); + + if (!error) { + context.enter(modeBrowse(context)); + } + + return error; + } + + mode.zoomToSelected = function () { + if (!errorService) return; + var error = errorService.getError(selectedErrorID); + + if (error) { + context.map().centerZoomEase(error.loc, 20); + } + }; + + mode.enter = function () { + var error = checkSelectedID(); + if (!error) return; + behaviors.forEach(context.install); + keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true); + select(document).call(keybinding); + selectError(); + var sidebar = context.ui().sidebar; + sidebar.show(errorEditor.error(error)); + context.map().on('drawn.select-error', selectError); // class the error as selected, or return to browse mode if the error is gone + + function selectError(d3_event, drawn) { + if (!checkSelectedID()) return; + var selection = context.surface().selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService); + + if (selection.empty()) { + // Return to browse mode if selected DOM elements have + // disappeared because the user moved them out of view.. + var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent; + + if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) { + context.enter(modeBrowse(context)); + } + } else { + selection.classed('selected', true); + context.selectedErrorID(selectedErrorID); + } + } + + function esc() { + if (context.container().select('.combobox').size()) return; + context.enter(modeBrowse(context)); + } + }; + + mode.exit = function () { + behaviors.forEach(context.uninstall); + select(document).call(keybinding.unbind); + context.surface().selectAll('.qaItem.selected').classed('selected hover', false); + context.map().on('drawn.select-error', null); + context.ui().sidebar.hide(); + context.selectedErrorID(null); + context.features().forceVisible([]); + }; + + return mode; + } + + function uiToolOldDrawModes(context) { + var tool = { + id: 'old_modes', + label: _t.html('toolbar.add_feature') + }; + var modes = [modeAddPoint(context, { + title: _t.html('modes.add_point.title'), + button: 'point', + description: _t.html('modes.add_point.description'), + preset: _mainPresetIndex.item('point'), + key: '1' + }), modeAddLine(context, { + title: _t.html('modes.add_line.title'), + button: 'line', + description: _t.html('modes.add_line.description'), + preset: _mainPresetIndex.item('line'), + key: '2' + }), modeAddArea(context, { + title: _t.html('modes.add_area.title'), + button: 'area', + description: _t.html('modes.add_area.description'), + preset: _mainPresetIndex.item('area'), + key: '3' + })]; + + function enabled() { + return osmEditable(); + } + + function osmEditable() { + return context.editable(); + } + + modes.forEach(function (mode) { + context.keybinding().on(mode.key, function () { + if (!enabled()) return; + + if (mode.id === context.mode().id) { + context.enter(modeBrowse(context)); + } else { + context.enter(mode); + } + }); + }); + + tool.render = function (selection) { + var wrap = selection.append('div').attr('class', 'joined').style('display', 'flex'); + + var debouncedUpdate = debounce(update, 500, { + leading: true, + trailing: true + }); + + context.map().on('move.modes', debouncedUpdate).on('drawn.modes', debouncedUpdate); + context.on('enter.modes', update); + update(); + + function update() { + var buttons = wrap.selectAll('button.add-button').data(modes, function (d) { + return d.id; + }); // exit + + buttons.exit().remove(); // enter + + var buttonsEnter = buttons.enter().append('button').attr('class', function (d) { + return d.id + ' add-button bar-button'; + }).on('click.mode-buttons', function (d3_event, d) { + if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042 + + var currMode = context.mode().id; + if (/^draw/.test(currMode)) return; + + if (d.id === currMode) { + context.enter(modeBrowse(context)); + } else { + context.enter(d); + } + }).call(uiTooltip().placement('bottom').title(function (d) { + return d.description; + }).keys(function (d) { + return [d.key]; + }).scrollContainer(context.container().select('.top-toolbar'))); + buttonsEnter.each(function (d) { + select(this).call(svgIcon('#iD-icon-' + d.button)); + }); + buttonsEnter.append('span').attr('class', 'label').html(function (mode) { + return mode.title; + }); // if we are adding/removing the buttons, check if toolbar has overflowed + + if (buttons.enter().size() || buttons.exit().size()) { + context.ui().checkOverflow('.top-toolbar', true); + } // update + + + buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) { + return !enabled(); + }).classed('active', function (d) { + return context.mode() && context.mode().button === d.button; + }); + } + }; + + return tool; + } + + function uiToolNotes(context) { + var tool = { + id: 'notes', + label: _t.html('modes.add_note.label') + }; + var mode = modeAddNote(context); + + function enabled() { + return notesEnabled() && notesEditable(); + } + + function notesEnabled() { + var noteLayer = context.layers().layer('notes'); + return noteLayer && noteLayer.enabled(); + } + + function notesEditable() { + var mode = context.mode(); + return context.map().notesEditable() && mode && mode.id !== 'save'; + } + + context.keybinding().on(mode.key, function () { + if (!enabled()) return; + + if (mode.id === context.mode().id) { + context.enter(modeBrowse(context)); + } else { + context.enter(mode); + } + }); + + tool.render = function (selection) { + var debouncedUpdate = debounce(update, 500, { + leading: true, + trailing: true + }); + + context.map().on('move.notes', debouncedUpdate).on('drawn.notes', debouncedUpdate); + context.on('enter.notes', update); + update(); + + function update() { + var showNotes = notesEnabled(); + var data = showNotes ? [mode] : []; + var buttons = selection.selectAll('button.add-button').data(data, function (d) { + return d.id; + }); // exit + + buttons.exit().remove(); // enter + + var buttonsEnter = buttons.enter().append('button').attr('class', function (d) { + return d.id + ' add-button bar-button'; + }).on('click.notes', function (d3_event, d) { + if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042 + + var currMode = context.mode().id; + if (/^draw/.test(currMode)) return; + + if (d.id === currMode) { + context.enter(modeBrowse(context)); + } else { + context.enter(d); + } + }).call(uiTooltip().placement('bottom').title(function (d) { + return d.description; + }).keys(function (d) { + return [d.key]; + }).scrollContainer(context.container().select('.top-toolbar'))); + buttonsEnter.each(function (d) { + select(this).call(svgIcon(d.icon || '#iD-icon-' + d.button)); + }); // if we are adding/removing the buttons, check if toolbar has overflowed + + if (buttons.enter().size() || buttons.exit().size()) { + context.ui().checkOverflow('.top-toolbar', true); + } // update + + + buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) { + return !enabled(); + }).classed('active', function (d) { + return context.mode() && context.mode().button === d.button; + }); + } + }; + + tool.uninstall = function () { + context.on('enter.editor.notes', null).on('exit.editor.notes', null).on('enter.notes', null); + context.map().on('move.notes', null).on('drawn.notes', null); + }; + + return tool; + } + + function uiToolSave(context) { + var tool = { + id: 'save', + label: _t.html('save.title') + }; + var button = null; + var tooltipBehavior = null; + var history = context.history(); + var key = uiCmd('⌘S'); + var _numChanges = 0; + + function isSaving() { + var mode = context.mode(); + return mode && mode.id === 'save'; + } + + function isDisabled() { + return _numChanges === 0 || isSaving(); + } + + function save(d3_event) { + d3_event.preventDefault(); + + if (!context.inIntro() && !isSaving() && history.hasChanges()) { + context.enter(modeSave(context)); + } + } + + function bgColor() { + var step; + + if (_numChanges === 0) { + return null; + } else if (_numChanges <= 50) { + step = _numChanges / 50; + return d3_interpolateRgb('#fff', '#ff8')(step); // white -> yellow + } else { + step = Math.min((_numChanges - 50) / 50, 1.0); + return d3_interpolateRgb('#ff8', '#f88')(step); // yellow -> red + } + } + + function updateCount() { + var val = history.difference().summary().length; + if (val === _numChanges) return; + _numChanges = val; + + if (tooltipBehavior) { + tooltipBehavior.title(_t.html(_numChanges > 0 ? 'save.help' : 'save.no_changes')).keys([key]); + } + + if (button) { + button.classed('disabled', isDisabled()).style('background', bgColor()); + button.select('span.count').html(_numChanges); + } + } + + tool.render = function (selection) { + tooltipBehavior = uiTooltip().placement('bottom').title(_t.html('save.no_changes')).keys([key]).scrollContainer(context.container().select('.top-toolbar')); + var lastPointerUpType; + button = selection.append('button').attr('class', 'save disabled bar-button').on('pointerup', function (d3_event) { + lastPointerUpType = d3_event.pointerType; + }).on('click', function (d3_event) { + save(d3_event); + + if (_numChanges === 0 && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) { + // there are no tooltips for touch interactions so flash feedback instead + context.ui().flash.duration(2000).iconName('#iD-icon-save').iconClass('disabled').label(_t.html('save.no_changes'))(); + } + + lastPointerUpType = null; + }).call(tooltipBehavior); + button.call(svgIcon('#iD-icon-save')); + button.append('span').attr('class', 'count').attr('aria-hidden', 'true').html('0'); + updateCount(); + context.keybinding().on(key, save, true); + context.history().on('change.save', updateCount); + context.on('enter.save', function () { + if (button) { + button.classed('disabled', isDisabled()); + + if (isSaving()) { + button.call(tooltipBehavior.hide); + } + } + }); + }; + + tool.uninstall = function () { + context.keybinding().off(key, true); + context.history().on('change.save', null); + context.on('enter.save', null); + button = null; + tooltipBehavior = null; + }; + + return tool; + } + + function uiToolSidebarToggle(context) { + var tool = { + id: 'sidebar_toggle', + label: _t.html('toolbar.inspect') + }; + + tool.render = function (selection) { + selection.append('button').attr('class', 'bar-button').on('click', function () { + context.ui().sidebar.toggle(); + }).call(uiTooltip().placement('bottom').title(_t.html('sidebar.tooltip')).keys([_t('sidebar.key')]).scrollContainer(context.container().select('.top-toolbar'))).call(svgIcon('#iD-icon-sidebar-' + (_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left'))); + }; + + return tool; + } + + function uiToolUndoRedo(context) { + var tool = { + id: 'undo_redo', + label: _t.html('toolbar.undo_redo') + }; + var commands = [{ + id: 'undo', + cmd: uiCmd('⌘Z'), + action: function action() { + context.undo(); + }, + annotation: function annotation() { + return context.history().undoAnnotation(); + }, + icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo') + }, { + id: 'redo', + cmd: uiCmd('⌘⇧Z'), + action: function action() { + context.redo(); + }, + annotation: function annotation() { + return context.history().redoAnnotation(); + }, + icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo') + }]; + + function editable() { + return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true + /* ignore min zoom */ + ); + } + + tool.render = function (selection) { + var tooltipBehavior = uiTooltip().placement('bottom').title(function (d) { + return d.annotation() ? _t.html(d.id + '.tooltip', { + action: d.annotation() + }) : _t.html(d.id + '.nothing'); + }).keys(function (d) { + return [d.cmd]; + }).scrollContainer(context.container().select('.top-toolbar')); + var lastPointerUpType; + var buttons = selection.selectAll('button').data(commands).enter().append('button').attr('class', function (d) { + return 'disabled ' + d.id + '-button bar-button'; + }).on('pointerup', function (d3_event) { + // `pointerup` is always called before `click` + lastPointerUpType = d3_event.pointerType; + }).on('click', function (d3_event, d) { + d3_event.preventDefault(); + var annotation = d.annotation(); + + if (editable() && annotation) { + d.action(); + } + + if (editable() && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) { + // there are no tooltips for touch interactions so flash feedback instead + var text = annotation ? _t(d.id + '.tooltip', { + action: annotation + }) : _t(d.id + '.nothing'); + context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass(annotation ? '' : 'disabled').label(text)(); + } + + lastPointerUpType = null; + }).call(tooltipBehavior); + buttons.each(function (d) { + select(this).call(svgIcon('#' + d.icon)); + }); + context.keybinding().on(commands[0].cmd, function (d3_event) { + d3_event.preventDefault(); + if (editable()) commands[0].action(); + }).on(commands[1].cmd, function (d3_event) { + d3_event.preventDefault(); + if (editable()) commands[1].action(); + }); + + var debouncedUpdate = debounce(update, 500, { + leading: true, + trailing: true + }); + + context.map().on('move.undo_redo', debouncedUpdate).on('drawn.undo_redo', debouncedUpdate); + context.history().on('change.undo_redo', function (difference) { + if (difference) update(); + }); + context.on('enter.undo_redo', update); + + function update() { + buttons.classed('disabled', function (d) { + return !editable() || !d.annotation(); + }).each(function () { + var selection = select(this); + + if (!selection.select('.tooltip.in').empty()) { + selection.call(tooltipBehavior.updateContent); + } + }); + } + }; + + tool.uninstall = function () { + context.keybinding().off(commands[0].cmd).off(commands[1].cmd); + context.map().on('move.undo_redo', null).on('drawn.undo_redo', null); + context.history().on('change.undo_redo', null); + context.on('enter.undo_redo', null); + }; + + return tool; + } + + function uiTopToolbar(context) { + var sidebarToggle = uiToolSidebarToggle(context), + modes = uiToolOldDrawModes(context), + notes = uiToolNotes(context), + undoRedo = uiToolUndoRedo(context), + save = uiToolSave(context); + + function notesEnabled() { + var noteLayer = context.layers().layer('notes'); + return noteLayer && noteLayer.enabled(); + } + + function topToolbar(bar) { + bar.on('wheel.topToolbar', function (d3_event) { + if (!d3_event.deltaX) { + // translate vertical scrolling into horizontal scrolling in case + // the user doesn't have an input device that can scroll horizontally + bar.node().scrollLeft += d3_event.deltaY; + } + }); + + var debouncedUpdate = debounce(update, 500, { + leading: true, + trailing: true + }); + + context.layers().on('change.topToolbar', debouncedUpdate); + update(); + + function update() { + var tools = [sidebarToggle, 'spacer', modes]; + tools.push('spacer'); + + if (notesEnabled()) { + tools = tools.concat([notes, 'spacer']); + } + + tools = tools.concat([undoRedo, save]); + var toolbarItems = bar.selectAll('.toolbar-item').data(tools, function (d) { + return d.id || d; + }); + toolbarItems.exit().each(function (d) { + if (d.uninstall) { + d.uninstall(); + } + }).remove(); + var itemsEnter = toolbarItems.enter().append('div').attr('class', function (d) { + var classes = 'toolbar-item ' + (d.id || d).replace('_', '-'); + if (d.klass) classes += ' ' + d.klass; + return classes; + }); + var actionableItems = itemsEnter.filter(function (d) { + return d !== 'spacer'; + }); + actionableItems.append('div').attr('class', 'item-content').each(function (d) { + select(this).call(d.render, bar); + }); + actionableItems.append('div').attr('class', 'item-label').html(function (d) { + return d.label; + }); + } + } + + return topToolbar; + } + + var sawVersion = null; + var isNewVersion = false; + var isNewUser = false; + function uiVersion(context) { + var currVersion = context.version; + var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/); + + if (sawVersion === null && matchedVersion !== null) { + if (corePreferences('sawVersion')) { + isNewUser = false; + isNewVersion = corePreferences('sawVersion') !== currVersion && currVersion.indexOf('-') === -1; + } else { + isNewUser = true; + isNewVersion = true; + } + + corePreferences('sawVersion', currVersion); + sawVersion = currVersion; + } + + return function (selection) { + selection.append('a').attr('target', '_blank').attr('href', 'https://github.com/openstreetmap/iD').html(currVersion); // only show new version indicator to users that have used iD before + + if (isNewVersion && !isNewUser) { + 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', { + version: currVersion + })).placement('top').scrollContainer(context.container().select('.main-footer-wrap'))); + } + }; + } + + function uiZoom(context) { + var zooms = [{ + id: 'zoom-in', + icon: 'iD-icon-plus', + title: _t.html('zoom.in'), + action: zoomIn, + disabled: function disabled() { + return !context.map().canZoomIn(); + }, + disabledTitle: _t.html('zoom.disabled.in'), + key: '+' + }, { + id: 'zoom-out', + icon: 'iD-icon-minus', + title: _t.html('zoom.out'), + action: zoomOut, + disabled: function disabled() { + return !context.map().canZoomOut(); + }, + disabledTitle: _t.html('zoom.disabled.out'), + key: '-' + }]; + + function zoomIn(d3_event) { + if (d3_event.shiftKey) return; + d3_event.preventDefault(); + context.map().zoomIn(); + } + + function zoomOut(d3_event) { + if (d3_event.shiftKey) return; + d3_event.preventDefault(); + context.map().zoomOut(); + } + + function zoomInFurther(d3_event) { + if (d3_event.shiftKey) return; + d3_event.preventDefault(); + context.map().zoomInFurther(); + } + + function zoomOutFurther(d3_event) { + if (d3_event.shiftKey) return; + d3_event.preventDefault(); + context.map().zoomOutFurther(); + } + + return function (selection) { + var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function (d) { + if (d.disabled()) { + return d.disabledTitle; + } + + return d.title; + }).keys(function (d) { + return [d.key]; + }); + var lastPointerUpType; + var buttons = selection.selectAll('button').data(zooms).enter().append('button').attr('class', function (d) { + return d.id; + }).on('pointerup.editor', function (d3_event) { + lastPointerUpType = d3_event.pointerType; + }).on('click.editor', function (d3_event, d) { + if (!d.disabled()) { + d.action(d3_event); + } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') { + context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass('disabled').label(d.disabledTitle)(); + } + + lastPointerUpType = null; + }).call(tooltipBehavior); + buttons.each(function (d) { + select(this).call(svgIcon('#' + d.icon, 'light')); + }); + utilKeybinding.plusKeys.forEach(function (key) { + context.keybinding().on([key], zoomIn); + context.keybinding().on([uiCmd('⌥' + key)], zoomInFurther); + }); + utilKeybinding.minusKeys.forEach(function (key) { + context.keybinding().on([key], zoomOut); + context.keybinding().on([uiCmd('⌥' + key)], zoomOutFurther); + }); + + function updateButtonStates() { + buttons.classed('disabled', function (d) { + return d.disabled(); + }).each(function () { + var selection = select(this); + + if (!selection.select('.tooltip.in').empty()) { + selection.call(tooltipBehavior.updateContent); + } + }); + } + + updateButtonStates(); + context.map().on('move.uiZoom', updateButtonStates); + }; + } + + function uiZoomToSelection(context) { + function isDisabled() { + var mode = context.mode(); + return !mode || !mode.zoomToSelected; + } + + var _lastPointerUpType; + + function pointerup(d3_event) { + _lastPointerUpType = d3_event.pointerType; + } + + function click(d3_event) { + d3_event.preventDefault(); + + if (isDisabled()) { + if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') { + context.ui().flash.duration(2000).iconName('#iD-icon-framed-dot').iconClass('disabled').label(_t.html('inspector.zoom_to.no_selection'))(); + } + } else { + var mode = context.mode(); + + if (mode && mode.zoomToSelected) { + mode.zoomToSelected(); + } + } + + _lastPointerUpType = null; + } + + return function (selection) { + var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function () { + if (isDisabled()) { + return _t.html('inspector.zoom_to.no_selection'); + } + + return _t.html('inspector.zoom_to.title'); + }).keys([_t('inspector.zoom_to.key')]); + var button = selection.append('button').on('pointerup', pointerup).on('click', click).call(svgIcon('#iD-icon-framed-dot', 'light')).call(tooltipBehavior); + + function setEnabledState() { + button.classed('disabled', isDisabled()); + + if (!button.select('.tooltip.in').empty()) { + button.call(tooltipBehavior.updateContent); + } + } + + context.on('enter.uiZoomToSelection', setEnabledState); + setEnabledState(); + }; + } + + function uiPane(id, context) { + var _key; + + var _label = ''; + var _description = ''; + var _iconName = ''; + + var _sections; // array of uiSection objects + + + var _paneSelection = select(null); + + var _paneTooltip; + + var pane = { + id: id + }; + + pane.label = function (val) { + if (!arguments.length) return _label; + _label = val; + return pane; + }; + + pane.key = function (val) { + if (!arguments.length) return _key; + _key = val; + return pane; + }; + + pane.description = function (val) { + if (!arguments.length) return _description; + _description = val; + return pane; + }; + + pane.iconName = function (val) { + if (!arguments.length) return _iconName; + _iconName = val; + return pane; + }; + + pane.sections = function (val) { + if (!arguments.length) return _sections; + _sections = val; + return pane; + }; + + pane.selection = function () { + return _paneSelection; + }; + + function hidePane() { + context.ui().togglePanes(); + } + + pane.togglePane = function (d3_event) { + if (d3_event) d3_event.preventDefault(); + + _paneTooltip.hide(); + + context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined); + }; + + pane.renderToggleButton = function (selection) { + if (!_paneTooltip) { + _paneTooltip = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(_description).keys([_key]); + } + + selection.append('button').on('click', pane.togglePane).call(svgIcon('#' + _iconName, 'light')).call(_paneTooltip); + }; + + pane.renderContent = function (selection) { + // override to fully customize content + if (_sections) { + _sections.forEach(function (section) { + selection.call(section.render); + }); + } + }; + + pane.renderPane = function (selection) { + _paneSelection = selection.append('div').attr('class', 'fillL map-pane hide ' + id + '-pane').attr('pane', id); + + var heading = _paneSelection.append('div').attr('class', 'pane-heading'); + + heading.append('h2').html(_label); + heading.append('button').on('click', hidePane).call(svgIcon('#iD-icon-close')); + + _paneSelection.append('div').attr('class', 'pane-content').call(pane.renderContent); + + if (_key) { + context.keybinding().on(_key, pane.togglePane); + } + }; + + return pane; + } + + function uiSectionBackgroundDisplayOptions(context) { + var section = uiSection('background-display-options', context).label(_t.html('background.display_options')).disclosureContent(renderDisclosureContent); + + var _detected = utilDetect(); + + var _storedOpacity = corePreferences('background-opacity'); + + var _minVal = 0; + + var _maxVal = _detected.cssfilters ? 3 : 1; + + var _sliders = _detected.cssfilters ? ['brightness', 'contrast', 'saturation', 'sharpness'] : ['brightness']; + + var _options = { + brightness: _storedOpacity !== null ? +_storedOpacity : 1, + contrast: 1, + saturation: 1, + sharpness: 1 + }; + + function clamp(x, min, max) { + return Math.max(min, Math.min(x, max)); + } + + function updateValue(d, val) { + val = clamp(val, _minVal, _maxVal); + _options[d] = val; + context.background()[d](val); + + if (d === 'brightness') { + corePreferences('background-opacity', val); + } + + section.reRender(); + } + + function renderDisclosureContent(selection) { + var container = selection.selectAll('.display-options-container').data([0]); + var containerEnter = container.enter().append('div').attr('class', 'display-options-container controls-list'); // add slider controls + + var slidersEnter = containerEnter.selectAll('.display-control').data(_sliders).enter().append('div').attr('class', function (d) { + return 'display-control display-control-' + d; + }); + slidersEnter.append('h5').html(function (d) { + return _t.html('background.' + d); + }).append('span').attr('class', function (d) { + return 'display-option-value display-option-value-' + d; + }); + var sildersControlEnter = slidersEnter.append('div').attr('class', 'control-wrap'); + sildersControlEnter.append('input').attr('class', function (d) { + return 'display-option-input display-option-input-' + d; + }).attr('type', 'range').attr('min', _minVal).attr('max', _maxVal).attr('step', '0.05').on('input', function (d3_event, d) { + var val = select(this).property('value'); + + if (!val && d3_event && d3_event.target) { + val = d3_event.target.value; + } + + updateValue(d, val); + }); + sildersControlEnter.append('button').attr('title', _t('background.reset')).attr('class', function (d) { + return 'display-option-reset display-option-reset-' + d; + }).on('click', function (d3_event, d) { + if (d3_event.button !== 0) return; + updateValue(d, 1); + }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo'))); // reset all button + + containerEnter.append('a').attr('class', 'display-option-resetlink').attr('href', '#').html(_t.html('background.reset_all')).on('click', function (d3_event) { + d3_event.preventDefault(); + + for (var i = 0; i < _sliders.length; i++) { + updateValue(_sliders[i], 1); + } + }); // update + + container = containerEnter.merge(container); + container.selectAll('.display-option-input').property('value', function (d) { + return _options[d]; + }); + container.selectAll('.display-option-value').html(function (d) { + return Math.floor(_options[d] * 100) + '%'; + }); + container.selectAll('.display-option-reset').classed('disabled', function (d) { + return _options[d] === 1; + }); // first time only, set brightness if needed + + if (containerEnter.size() && _options.brightness !== 1) { + context.background().brightness(_options.brightness); + } + } + + return section; + } + + function uiSettingsCustomBackground() { + var dispatch = dispatch$8('change'); + + function render(selection) { + // keep separate copies of original and current settings + var _origSettings = { + template: corePreferences('background-custom-template') + }; + var _currSettings = { + template: corePreferences('background-custom-template') + }; + var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png'; + var modal = uiConfirm(selection).okButton(); + modal.classed('settings-modal settings-custom-background', true); + modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_background.header')); + var textSection = modal.select('.modal-section.message-text'); + 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, "`"); + textSection.append('div').attr('class', 'instructions-template').html(marked_1(instructions)); + textSection.append('textarea').attr('class', 'field-template').attr('placeholder', _t('settings.custom_background.template.placeholder')).call(utilNoAuto).property('value', _currSettings.template); // insert a cancel button + + var buttonSection = modal.select('.modal-section.buttons'); + buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel')); + buttonSection.select('.cancel-button').on('click.cancel', clickCancel); + buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave); + + function isSaveDisabled() { + return null; + } // restore the original template + + + function clickCancel() { + textSection.select('.field-template').property('value', _origSettings.template); + corePreferences('background-custom-template', _origSettings.template); + this.blur(); + modal.close(); + } // accept the current template + + + function clickSave() { + _currSettings.template = textSection.select('.field-template').property('value'); + corePreferences('background-custom-template', _currSettings.template); + this.blur(); + modal.close(); + dispatch.call('change', this, _currSettings); + } + } + + return utilRebind(render, dispatch, 'on'); + } + + function uiSectionBackgroundList(context) { + var _backgroundList = select(null); + + var _customSource = context.background().findSource('custom'); + + var _settingsCustomBackground = uiSettingsCustomBackground().on('change', customChanged); + + var section = uiSection('background-list', context).label(_t.html('background.backgrounds')).disclosureContent(renderDisclosureContent); + + function previousBackgroundID() { + return corePreferences('background-last-used-toggle'); + } + + function renderDisclosureContent(selection) { + // the background list + var container = selection.selectAll('.layer-background-list').data([0]); + _backgroundList = container.enter().append('ul').attr('class', 'layer-list layer-background-list').attr('dir', 'auto').merge(container); // add minimap toggle below list + + var bgExtrasListEnter = selection.selectAll('.bg-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list bg-extras-list'); + var minimapLabelEnter = bgExtrasListEnter.append('li').attr('class', 'minimap-toggle-item').append('label').call(uiTooltip().title(_t.html('background.minimap.tooltip')).keys([_t('background.minimap.key')]).placement('top')); + minimapLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) { + d3_event.preventDefault(); + uiMapInMap.toggle(); + }); + minimapLabelEnter.append('span').html(_t.html('background.minimap.description')); + 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')); + panelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) { + d3_event.preventDefault(); + context.ui().info.toggle('background'); + }); + panelLabelEnter.append('span').html(_t.html('background.panel.description')); + 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')); + locPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) { + d3_event.preventDefault(); + context.ui().info.toggle('location'); + }); + locPanelLabelEnter.append('span').html(_t.html('background.location_panel.description')); // "Info / Report a Problem" link + + selection.selectAll('.imagery-faq').data([0]).enter().append('div').attr('class', 'imagery-faq').append('a').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).attr('href', 'https://github.com/openstreetmap/iD/blob/develop/FAQ.md#how-can-i-report-an-issue-with-background-imagery').append('span').html(_t.html('background.imagery_problem_faq')); + + _backgroundList.call(drawListItems, 'radio', function (d3_event, d) { + chooseBackground(d); + }, function (d) { + return !d.isHidden() && !d.overlay; + }); + } + + function setTooltips(selection) { + selection.each(function (d, i, nodes) { + var item = select(this).select('label'); + var span = item.select('span'); + var placement = i < nodes.length / 2 ? 'bottom' : 'top'; + var description = d.description(); + var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth'); + item.call(uiTooltip().destroyAny); + + if (d.id === previousBackgroundID()) { + item.call(uiTooltip().placement(placement).title('
    ' + _t.html('background.switch') + '
    ').keys([uiCmd('⌘' + _t('background.key'))])); + } else if (description || isOverflowing) { + item.call(uiTooltip().placement(placement).title(description || d.label())); + } + }); + } + + function drawListItems(layerList, type, change, filter) { + var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter).sort(function (a, b) { + return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0; + }); + var layerLinks = layerList.selectAll('li') // We have to be a bit inefficient about reordering the list since + // arrow key navigation of radio values likes to work in the order + // they were added, not the display document order. + .data(sources, function (d, i) { + return d.id + '---' + i; + }); + layerLinks.exit().remove(); + var enter = layerLinks.enter().append('li').classed('layer-custom', function (d) { + return d.id === 'custom'; + }).classed('best', function (d) { + return d.best(); + }); + var label = enter.append('label'); + label.append('input').attr('type', type).attr('name', 'background-layer').attr('value', function (d) { + return d.id; + }).on('change', change); + label.append('span').html(function (d) { + return d.label(); + }); + enter.filter(function (d) { + return d.id === 'custom'; + }).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) { + d3_event.preventDefault(); + editCustom(); + }).call(svgIcon('#iD-icon-more')); + enter.filter(function (d) { + return d.best(); + }).append('div').attr('class', 'best').call(uiTooltip().title(_t.html('background.best_imagery')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).append('span').html('★'); + layerList.call(updateLayerSelections); + } + + function updateLayerSelections(selection) { + function active(d) { + return context.background().showsLayer(d); + } + + selection.selectAll('li').classed('active', active).classed('switch', function (d) { + return d.id === previousBackgroundID(); + }).call(setTooltips).selectAll('input').property('checked', active); + } + + function chooseBackground(d) { + if (d.id === 'custom' && !d.template()) { + return editCustom(); + } + + var previousBackground = context.background().baseLayerSource(); + corePreferences('background-last-used-toggle', previousBackground.id); + corePreferences('background-last-used', d.id); + context.background().baseLayerSource(d); + } + + function customChanged(d) { + if (d && d.template) { + _customSource.template(d.template); + + chooseBackground(_customSource); + } else { + _customSource.template(''); + + chooseBackground(context.background().findSource('none')); + } + } + + function editCustom() { + context.container().call(_settingsCustomBackground); + } + + context.background().on('change.background_list', function () { + _backgroundList.call(updateLayerSelections); + }); + context.map().on('move.background_list', debounce(function () { + // layers in-view may have changed due to map move + window.requestIdleCallback(section.reRender); + }, 1000)); + return section; + } + + function uiSectionBackgroundOffset(context) { + var section = uiSection('background-offset', context).label(_t.html('background.fix_misalignment')).disclosureContent(renderDisclosureContent).expandedByDefault(false); + + var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; + + var _directions = [['top', [0, -0.5]], ['left', [-0.5, 0]], ['right', [0.5, 0]], ['bottom', [0, 0.5]]]; + + function updateValue() { + var meters = geoOffsetToMeters(context.background().offset()); + var x = +meters[0].toFixed(2); + var y = +meters[1].toFixed(2); + context.container().selectAll('.nudge-inner-rect').select('input').classed('error', false).property('value', x + ', ' + y); + context.container().selectAll('.nudge-reset').classed('disabled', function () { + return x === 0 && y === 0; + }); + } + + function resetOffset() { + context.background().offset([0, 0]); + updateValue(); + } + + function nudge(d) { + context.background().nudge(d, context.map().zoom()); + updateValue(); + } + + function inputOffset() { + var input = select(this); + var d = input.node().value; + if (d === '') return resetOffset(); + d = d.replace(/;/g, ',').split(',').map(function (n) { + // if n is NaN, it will always get mapped to false. + return !isNaN(n) && n; + }); + + if (d.length !== 2 || !d[0] || !d[1]) { + input.classed('error', true); + return; + } + + context.background().offset(geoMetersToOffset(d)); + updateValue(); + } + + function dragOffset(d3_event) { + if (d3_event.button !== 0) return; + var origin = [d3_event.clientX, d3_event.clientY]; + var pointerId = d3_event.pointerId || 'mouse'; + context.container().append('div').attr('class', 'nudge-surface'); + select(window).on(_pointerPrefix + 'move.drag-bg-offset', pointermove).on(_pointerPrefix + 'up.drag-bg-offset', pointerup); + + if (_pointerPrefix === 'pointer') { + select(window).on('pointercancel.drag-bg-offset', pointerup); + } + + function pointermove(d3_event) { + if (pointerId !== (d3_event.pointerId || 'mouse')) return; + var latest = [d3_event.clientX, d3_event.clientY]; + var d = [-(origin[0] - latest[0]) / 4, -(origin[1] - latest[1]) / 4]; + origin = latest; + nudge(d); + } + + function pointerup(d3_event) { + if (pointerId !== (d3_event.pointerId || 'mouse')) return; + if (d3_event.button !== 0) return; + context.container().selectAll('.nudge-surface').remove(); + select(window).on('.drag-bg-offset', null); + } + } + + function renderDisclosureContent(selection) { + var container = selection.selectAll('.nudge-container').data([0]); + var containerEnter = container.enter().append('div').attr('class', 'nudge-container'); + containerEnter.append('div').attr('class', 'nudge-instructions').html(_t.html('background.offset')); + var nudgeWrapEnter = containerEnter.append('div').attr('class', 'nudge-controls-wrap'); + var nudgeEnter = nudgeWrapEnter.append('div').attr('class', 'nudge-outer-rect').on(_pointerPrefix + 'down', dragOffset); + nudgeEnter.append('div').attr('class', 'nudge-inner-rect').append('input').attr('type', 'text').on('change', inputOffset); + nudgeWrapEnter.append('div').selectAll('button').data(_directions).enter().append('button').attr('class', function (d) { + return d[0] + ' nudge'; + }).on('click', function (d3_event, d) { + nudge(d[1]); + }); + nudgeWrapEnter.append('button').attr('title', _t('background.reset')).attr('class', 'nudge-reset disabled').on('click', function (d3_event) { + d3_event.preventDefault(); + resetOffset(); + }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo'))); + updateValue(); + } + + context.background().on('change.backgroundOffset-update', updateValue); + return section; + } + + function uiSectionOverlayList(context) { + var section = uiSection('overlay-list', context).label(_t.html('background.overlays')).disclosureContent(renderDisclosureContent); + + var _overlayList = select(null); + + function setTooltips(selection) { + selection.each(function (d, i, nodes) { + var item = select(this).select('label'); + var span = item.select('span'); + var placement = i < nodes.length / 2 ? 'bottom' : 'top'; + var description = d.description(); + var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth'); + item.call(uiTooltip().destroyAny); + + if (description || isOverflowing) { + item.call(uiTooltip().placement(placement).title(description || d.name())); + } + }); + } + + function updateLayerSelections(selection) { + function active(d) { + return context.background().showsLayer(d); + } + + selection.selectAll('li').classed('active', active).call(setTooltips).selectAll('input').property('checked', active); + } + + function chooseOverlay(d3_event, d) { + d3_event.preventDefault(); + context.background().toggleOverlayLayer(d); + + _overlayList.call(updateLayerSelections); + + document.activeElement.blur(); + } + + function drawListItems(layerList, type, change, filter) { + var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter); + var layerLinks = layerList.selectAll('li').data(sources, function (d) { + return d.name(); + }); + layerLinks.exit().remove(); + var enter = layerLinks.enter().append('li'); + var label = enter.append('label'); + label.append('input').attr('type', type).attr('name', 'layers').on('change', change); + label.append('span').html(function (d) { + return d.label(); + }); + layerList.selectAll('li').sort(sortSources); + layerList.call(updateLayerSelections); + + function sortSources(a, b) { + return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0; + } + } + + function renderDisclosureContent(selection) { + var container = selection.selectAll('.layer-overlay-list').data([0]); + _overlayList = container.enter().append('ul').attr('class', 'layer-list layer-overlay-list').attr('dir', 'auto').merge(container); + + _overlayList.call(drawListItems, 'checkbox', chooseOverlay, function (d) { + return !d.isHidden() && d.overlay; + }); + } + + context.map().on('move.overlay_list', debounce(function () { + // layers in-view may have changed due to map move + window.requestIdleCallback(section.reRender); + }, 1000)); + return section; + } + + function uiPaneBackground(context) { + var backgroundPane = uiPane('background', context).key(_t('background.key')).label(_t.html('background.title')).description(_t.html('background.description')).iconName('iD-icon-layers').sections([uiSectionBackgroundList(context), uiSectionOverlayList(context), uiSectionBackgroundDisplayOptions(context), uiSectionBackgroundOffset(context)]); + return backgroundPane; + } + + function uiPaneHelp(context) { + var docKeys = [['help', ['welcome', 'open_data_h', 'open_data', 'before_start_h', 'before_start', 'open_source_h', 'open_source', 'open_source_help']], ['overview', ['navigation_h', 'navigation_drag', 'navigation_zoom', 'features_h', 'features', 'nodes_ways']], ['editing', ['select_h', 'select_left_click', 'select_right_click', 'select_space', 'multiselect_h', 'multiselect', 'multiselect_shift_click', 'multiselect_lasso', 'undo_redo_h', 'undo_redo', 'save_h', 'save', 'save_validation', 'upload_h', 'upload', 'backups_h', 'backups', 'keyboard_h', 'keyboard']], ['feature_editor', ['intro', 'definitions', 'type_h', 'type', 'type_picker', 'fields_h', 'fields_all_fields', 'fields_example', 'fields_add_field', 'tags_h', 'tags_all_tags', 'tags_resources']], ['points', ['intro', 'add_point_h', 'add_point', 'add_point_finish', 'move_point_h', 'move_point', 'delete_point_h', 'delete_point', 'delete_point_command']], ['lines', ['intro', 'add_line_h', 'add_line', 'add_line_draw', 'add_line_continue', 'add_line_finish', 'modify_line_h', 'modify_line_dragnode', 'modify_line_addnode', 'connect_line_h', 'connect_line', 'connect_line_display', 'connect_line_drag', 'connect_line_tag', 'disconnect_line_h', 'disconnect_line_command', 'move_line_h', 'move_line_command', 'move_line_connected', 'delete_line_h', 'delete_line', 'delete_line_command']], ['areas', ['intro', 'point_or_area_h', 'point_or_area', 'add_area_h', 'add_area_command', 'add_area_draw', 'add_area_continue', 'add_area_finish', 'square_area_h', 'square_area_command', 'modify_area_h', 'modify_area_dragnode', 'modify_area_addnode', 'delete_area_h', 'delete_area', 'delete_area_command']], ['relations', ['intro', 'edit_relation_h', 'edit_relation', 'edit_relation_add', 'edit_relation_delete', 'maintain_relation_h', 'maintain_relation', 'relation_types_h', 'multipolygon_h', 'multipolygon', 'multipolygon_create', 'multipolygon_merge', 'turn_restriction_h', 'turn_restriction', 'turn_restriction_field', 'turn_restriction_editing', 'route_h', 'route', 'route_add', 'boundary_h', 'boundary', 'boundary_add']], ['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']]]; + var headings = { + 'help.help.open_data_h': 3, + 'help.help.before_start_h': 3, + 'help.help.open_source_h': 3, + 'help.overview.navigation_h': 3, + 'help.overview.features_h': 3, + 'help.editing.select_h': 3, + 'help.editing.multiselect_h': 3, + 'help.editing.undo_redo_h': 3, + 'help.editing.save_h': 3, + 'help.editing.upload_h': 3, + 'help.editing.backups_h': 3, + 'help.editing.keyboard_h': 3, + 'help.feature_editor.type_h': 3, + 'help.feature_editor.fields_h': 3, + 'help.feature_editor.tags_h': 3, + 'help.points.add_point_h': 3, + 'help.points.move_point_h': 3, + 'help.points.delete_point_h': 3, + 'help.lines.add_line_h': 3, + 'help.lines.modify_line_h': 3, + 'help.lines.connect_line_h': 3, + 'help.lines.disconnect_line_h': 3, + 'help.lines.move_line_h': 3, + 'help.lines.delete_line_h': 3, + 'help.areas.point_or_area_h': 3, + 'help.areas.add_area_h': 3, + 'help.areas.square_area_h': 3, + 'help.areas.modify_area_h': 3, + 'help.areas.delete_area_h': 3, + 'help.relations.edit_relation_h': 3, + 'help.relations.maintain_relation_h': 3, + 'help.relations.relation_types_h': 2, + 'help.relations.multipolygon_h': 3, + 'help.relations.turn_restriction_h': 3, + 'help.relations.route_h': 3, + 'help.relations.boundary_h': 3, + 'help.notes.add_note_h': 3, + 'help.notes.update_note_h': 3, + 'help.notes.save_note_h': 3, + 'help.imagery.sources_h': 3, + 'help.imagery.offsets_h': 3, + 'help.streetlevel.using_h': 3, + 'help.gps.using_h': 3, + 'help.qa.tools_h': 3, + 'help.qa.issues_h': 3 + }; // For each section, squash all the texts into a single markdown document + + var docs = docKeys.map(function (key) { + var helpkey = 'help.' + key[0]; + var helpPaneReplacements = { + version: context.version + }; + var text = key[1].reduce(function (all, part) { + var subkey = helpkey + '.' + part; + var depth = headings[subkey]; // is this subkey a heading? + + var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s + + return all + hhh + helpHtml(subkey, helpPaneReplacements) + '\n\n'; + }, ''); + return { + title: _t.html(helpkey + '.title'), + content: marked_1(text.trim()) // use keyboard key styling for shortcuts + .replace(//g, '').replace(/<\/code>/g, '<\/kbd>') + }; + }); + var helpPane = uiPane('help', context).key(_t('help.key')).label(_t.html('help.title')).description(_t.html('help.title')).iconName('iD-icon-help'); + + helpPane.renderContent = function (content) { + function clickHelp(d, i) { + var rtl = _mainLocalizer.textDirection() === 'rtl'; + content.property('scrollTop', 0); + helpPane.selection().select('.pane-heading h2').html(d.title); + body.html(d.content); + body.selectAll('a').attr('target', '_blank'); + menuItems.classed('selected', function (m) { + return m.title === d.title; + }); + nav.html(''); + + if (rtl) { + nav.call(drawNext).call(drawPrevious); + } else { + nav.call(drawPrevious).call(drawNext); + } + + function drawNext(selection) { + if (i < docs.length - 1) { + var nextLink = selection.append('a').attr('href', '#').attr('class', 'next').on('click', function (d3_event) { + d3_event.preventDefault(); + clickHelp(docs[i + 1], i + 1); + }); + nextLink.append('span').html(docs[i + 1].title).call(svgIcon(rtl ? '#iD-icon-backward' : '#iD-icon-forward', 'inline')); + } + } + + function drawPrevious(selection) { + if (i > 0) { + var prevLink = selection.append('a').attr('href', '#').attr('class', 'previous').on('click', function (d3_event) { + d3_event.preventDefault(); + clickHelp(docs[i - 1], i - 1); + }); + prevLink.call(svgIcon(rtl ? '#iD-icon-forward' : '#iD-icon-backward', 'inline')).append('span').html(docs[i - 1].title); + } + } + } + + function clickWalkthrough(d3_event) { + d3_event.preventDefault(); + if (context.inIntro()) return; + context.container().call(uiIntro(context)); + context.ui().togglePanes(); + } + + function clickShortcuts(d3_event) { + d3_event.preventDefault(); + context.container().call(context.ui().shortcuts, true); + } + + var toc = content.append('ul').attr('class', 'toc'); + var menuItems = toc.selectAll('li').data(docs).enter().append('li').append('a').attr('href', '#').html(function (d) { + return d.title; + }).on('click', function (d3_event, d) { + d3_event.preventDefault(); + clickHelp(d, docs.indexOf(d)); + }); + 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); + shortcuts.append('div').html(_t.html('shortcuts.title')); + var walkthrough = toc.append('li').attr('class', 'walkthrough').append('a').attr('href', '#').on('click', clickWalkthrough); + walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough'); + walkthrough.append('div').html(_t.html('splash.walkthrough')); + var helpContent = content.append('div').attr('class', 'left-content'); + var body = helpContent.append('div').attr('class', 'body'); + var nav = helpContent.append('div').attr('class', 'nav'); + clickHelp(docs[0], 0); + }; + + return helpPane; + } + + function uiSectionValidationIssues(id, severity, context) { + var _issues = []; + var section = uiSection(id, context).label(function () { + if (!_issues) return ''; + var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length); + return _t('inspector.title_count', { + title: _t.html('issues.' + severity + 's.list_title'), + count: issueCountText + }); + }).disclosureContent(renderDisclosureContent).shouldDisplay(function () { + return _issues && _issues.length; + }); + + function getOptions() { + return { + what: corePreferences('validate-what') || 'edited', + where: corePreferences('validate-where') || 'all' + }; + } // get and cache the issues to display, unordered + + + function reloadIssues() { + _issues = context.validator().getIssuesBySeverity(getOptions())[severity]; + } + + function renderDisclosureContent(selection) { + var center = context.map().center(); + var graph = context.graph(); // sort issues by distance away from the center of the map + + var issues = _issues.map(function withDistance(issue) { + var extent = issue.extent(graph); + var dist = extent ? geoSphericalDistance(center, extent.center()) : 0; + return Object.assign(issue, { + dist: dist + }); + }).sort(function byDistance(a, b) { + return a.dist - b.dist; + }); // cut off at 1000 + + + issues = issues.slice(0, 1000); //renderIgnoredIssuesReset(_warningsSelection); + + selection.call(drawIssuesList, issues); + } + + function drawIssuesList(selection, issues) { + var list = selection.selectAll('.issues-list').data([0]); + list = list.enter().append('ul').attr('class', 'layer-list issues-list ' + severity + 's-list').merge(list); + var items = list.selectAll('li').data(issues, function (d) { + return d.key; + }); // Exit + + items.exit().remove(); // Enter + + var itemsEnter = items.enter().append('li').attr('class', function (d) { + return 'issue severity-' + d.severity; + }); + var labelsEnter = itemsEnter.append('button').attr('class', 'issue-label').on('click', function (d3_event, d) { + context.validator().focusIssue(d); + }).on('mouseover', function (d3_event, d) { + utilHighlightEntities(d.entityIds, true, context); + }).on('mouseout', function (d3_event, d) { + utilHighlightEntities(d.entityIds, false, context); + }); + var textEnter = labelsEnter.append('span').attr('class', 'issue-text'); + textEnter.append('span').attr('class', 'issue-icon').each(function (d) { + var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error'); + select(this).call(svgIcon(iconName)); + }); + textEnter.append('span').attr('class', 'issue-message'); + /* + labelsEnter + .append('span') + .attr('class', 'issue-autofix') + .each(function(d) { + if (!d.autoFix) return; + d3_select(this) + .append('button') + .attr('title', t('issues.fix_one.title')) + .datum(d.autoFix) // set button datum to the autofix + .attr('class', 'autofix action') + .on('click', function(d3_event, d) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + var issuesEntityIDs = d.issue.entityIds; + utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context); + context.perform.apply(context, d.autoArgs); + context.validator().validate(); + }) + .call(svgIcon('#iD-icon-wrench')); + }); + */ + // Update + + items = items.merge(itemsEnter).order(); + items.selectAll('.issue-message').html(function (d) { + return d.message(context); + }); + /* + // autofix + var canAutoFix = issues.filter(function(issue) { return issue.autoFix; }); + var autoFixAll = selection.selectAll('.autofix-all') + .data(canAutoFix.length ? [0] : []); + // exit + autoFixAll.exit() + .remove(); + // enter + var autoFixAllEnter = autoFixAll.enter() + .insert('div', '.issues-list') + .attr('class', 'autofix-all'); + var linkEnter = autoFixAllEnter + .append('a') + .attr('class', 'autofix-all-link') + .attr('href', '#'); + linkEnter + .append('span') + .attr('class', 'autofix-all-link-text') + .html(t.html('issues.fix_all.title')); + linkEnter + .append('span') + .attr('class', 'autofix-all-link-icon') + .call(svgIcon('#iD-icon-wrench')); + if (severity === 'warning') { + renderIgnoredIssuesReset(selection); + } + // update + autoFixAll = autoFixAll + .merge(autoFixAllEnter); + autoFixAll.selectAll('.autofix-all-link') + .on('click', function() { + context.pauseChangeDispatch(); + context.perform(actionNoop()); + canAutoFix.forEach(function(issue) { + var args = issue.autoFix.autoArgs.slice(); // copy + if (typeof args[args.length - 1] !== 'function') { + args.pop(); + } + args.push(t('issues.fix_all.annotation')); + context.replace.apply(context, args); + }); + context.resumeChangeDispatch(); + context.validator().validate(); + }); + */ + } + + context.validator().on('validated.uiSectionValidationIssues' + id, function () { + window.requestIdleCallback(function () { + reloadIssues(); + section.reRender(); + }); + }); + context.map().on('move.uiSectionValidationIssues' + id, debounce(function () { + window.requestIdleCallback(function () { + if (getOptions().where === 'visible') { + // must refetch issues if they are viewport-dependent + reloadIssues(); + } // always reload list to re-sort-by-distance + + + section.reRender(); + }); + }, 1000)); + return section; + } + + function uiSectionValidationOptions(context) { + var section = uiSection('issues-options', context).content(renderContent); + + function renderContent(selection) { + var container = selection.selectAll('.issues-options-container').data([0]); + container = container.enter().append('div').attr('class', 'issues-options-container').merge(container); + var data = [{ + key: 'what', + values: ['edited', 'all'] + }, { + key: 'where', + values: ['visible', 'all'] + }]; + var options = container.selectAll('.issues-option').data(data, function (d) { + return d.key; + }); + var optionsEnter = options.enter().append('div').attr('class', function (d) { + return 'issues-option issues-option-' + d.key; + }); + optionsEnter.append('div').attr('class', 'issues-option-title').html(function (d) { + return _t.html('issues.options.' + d.key + '.title'); + }); + var valuesEnter = optionsEnter.selectAll('label').data(function (d) { + return d.values.map(function (val) { + return { + value: val, + key: d.key + }; + }); + }).enter().append('label'); + valuesEnter.append('input').attr('type', 'radio').attr('name', function (d) { + return 'issues-option-' + d.key; + }).attr('value', function (d) { + return d.value; + }).property('checked', function (d) { + return getOptions()[d.key] === d.value; + }).on('change', function (d3_event, d) { + updateOptionValue(d3_event, d.key, d.value); + }); + valuesEnter.append('span').html(function (d) { + return _t.html('issues.options.' + d.key + '.' + d.value); + }); + } + + function getOptions() { + return { + what: corePreferences('validate-what') || 'edited', + // 'all', 'edited' + where: corePreferences('validate-where') || 'all' // 'all', 'visible' + + }; + } + + function updateOptionValue(d3_event, d, val) { + if (!val && d3_event && d3_event.target) { + val = d3_event.target.value; + } + + corePreferences('validate-' + d, val); + context.validator().validate(); + } + + return section; + } + + function uiSectionValidationRules(context) { + var MINSQUARE = 0; + var MAXSQUARE = 20; + var DEFAULTSQUARE = 5; // see also unsquare_way.js + + var section = uiSection('issues-rules', context).disclosureContent(renderDisclosureContent).label(_t.html('issues.rules.title')); + + var _ruleKeys = context.validator().getRuleKeys().filter(function (key) { + return key !== 'maprules'; + }).sort(function (key1, key2) { + // alphabetize by localized title + return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1; + }); + + function renderDisclosureContent(selection) { + var container = selection.selectAll('.issues-rulelist-container').data([0]); + var containerEnter = container.enter().append('div').attr('class', 'issues-rulelist-container'); + containerEnter.append('ul').attr('class', 'layer-list issue-rules-list'); + var ruleLinks = containerEnter.append('div').attr('class', 'issue-rules-links section-footer'); + ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) { + d3_event.preventDefault(); + context.validator().disableRules(_ruleKeys); + }); + ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) { + d3_event.preventDefault(); + context.validator().disableRules([]); + }); // Update + + container = container.merge(containerEnter); + container.selectAll('.issue-rules-list').call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled); + } + + function drawListItems(selection, data, type, name, change, active) { + var items = selection.selectAll('li').data(data); // Exit + + items.exit().remove(); // Enter + + var enter = items.enter().append('li'); + + if (name === 'rule') { + enter.call(uiTooltip().title(function (d) { + return _t.html('issues.' + d + '.tip'); + }).placement('top')); + } + + var label = enter.append('label'); + label.append('input').attr('type', type).attr('name', name).on('change', change); + label.append('span').html(function (d) { + var params = {}; + + if (d === 'unsquare_way') { + params.val = ''; + } + + return _t.html('issues.' + d + '.title', params); + }); // Update + + items = items.merge(enter); + items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false); // user-configurable square threshold + + var degStr = corePreferences('validate-square-degrees'); + + if (degStr === null) { + degStr = DEFAULTSQUARE.toString(); + } + + var span = items.selectAll('.square-degrees'); + var input = span.selectAll('.square-degrees-input').data([0]); // enter / update + + input.enter().append('input').attr('type', 'number').attr('min', MINSQUARE.toString()).attr('max', MAXSQUARE.toString()).attr('step', '0.5').attr('class', 'square-degrees-input').call(utilNoAuto).on('click', function (d3_event) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + this.select(); + }).on('keyup', function (d3_event) { + if (d3_event.keyCode === 13) { + // ↩ Return + this.blur(); + this.select(); + } + }).on('blur', changeSquare).merge(input).property('value', degStr); + } + + function changeSquare() { + var input = select(this); + var degStr = utilGetSetValue(input).trim(); + var degNum = parseFloat(degStr, 10); + + if (!isFinite(degNum)) { + degNum = DEFAULTSQUARE; + } else if (degNum > MAXSQUARE) { + degNum = MAXSQUARE; + } else if (degNum < MINSQUARE) { + degNum = MINSQUARE; + } + + degNum = Math.round(degNum * 10) / 10; // round to 1 decimal + + degStr = degNum.toString(); + input.property('value', degStr); + corePreferences('validate-square-degrees', degStr); + context.validator().revalidateUnsquare(); + } + + function isRuleEnabled(d) { + return context.validator().isRuleEnabled(d); + } + + function toggleRule(d3_event, d) { + context.validator().toggleRule(d); + } + + context.validator().on('validated.uiSectionValidationRules', function () { + window.requestIdleCallback(section.reRender); + }); + return section; + } + + function uiSectionValidationStatus(context) { + var section = uiSection('issues-status', context).content(renderContent).shouldDisplay(function () { + var issues = context.validator().getIssues(getOptions()); + return issues.length === 0; + }); + + function getOptions() { + return { + what: corePreferences('validate-what') || 'edited', + where: corePreferences('validate-where') || 'all' + }; + } + + function renderContent(selection) { + var box = selection.selectAll('.box').data([0]); + var boxEnter = box.enter().append('div').attr('class', 'box'); + boxEnter.append('div').call(svgIcon('#iD-icon-apply', 'pre-text')); + var noIssuesMessage = boxEnter.append('span'); + noIssuesMessage.append('strong').attr('class', 'message'); + noIssuesMessage.append('br'); + noIssuesMessage.append('span').attr('class', 'details'); + renderIgnoredIssuesReset(selection); + setNoIssuesText(selection); + } + + function renderIgnoredIssuesReset(selection) { + var ignoredIssues = context.validator().getIssues({ + what: 'all', + where: 'all', + includeDisabledRules: true, + includeIgnored: 'only' + }); + var resetIgnored = selection.selectAll('.reset-ignored').data(ignoredIssues.length ? [0] : []); // exit + + resetIgnored.exit().remove(); // enter + + var resetIgnoredEnter = resetIgnored.enter().append('div').attr('class', 'reset-ignored section-footer'); + resetIgnoredEnter.append('a').attr('href', '#'); // update + + resetIgnored = resetIgnored.merge(resetIgnoredEnter); + resetIgnored.select('a').html(_t('inspector.title_count', { + title: _t.html('issues.reset_ignored'), + count: ignoredIssues.length + })); + resetIgnored.on('click', function (d3_event) { + d3_event.preventDefault(); + context.validator().resetIgnoredIssues(); + }); + } + + function setNoIssuesText(selection) { + var opts = getOptions(); + + function checkForHiddenIssues(cases) { + for (var type in cases) { + var hiddenOpts = cases[type]; + var hiddenIssues = context.validator().getIssues(hiddenOpts); + + if (hiddenIssues.length) { + selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.' + type, { + count: hiddenIssues.length.toString() + })); + return; + } + } + + selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.none')); + } + + var messageType; + + if (opts.what === 'edited' && opts.where === 'visible') { + messageType = 'edits_in_view'; + checkForHiddenIssues({ + elsewhere: { + what: 'edited', + where: 'all' + }, + everything_else: { + what: 'all', + where: 'visible' + }, + disabled_rules: { + what: 'edited', + where: 'visible', + includeDisabledRules: 'only' + }, + everything_else_elsewhere: { + what: 'all', + where: 'all' + }, + disabled_rules_elsewhere: { + what: 'edited', + where: 'all', + includeDisabledRules: 'only' + }, + ignored_issues: { + what: 'edited', + where: 'visible', + includeIgnored: 'only' + }, + ignored_issues_elsewhere: { + what: 'edited', + where: 'all', + includeIgnored: 'only' + } + }); + } else if (opts.what === 'edited' && opts.where === 'all') { + messageType = 'edits'; + checkForHiddenIssues({ + everything_else: { + what: 'all', + where: 'all' + }, + disabled_rules: { + what: 'edited', + where: 'all', + includeDisabledRules: 'only' + }, + ignored_issues: { + what: 'edited', + where: 'all', + includeIgnored: 'only' + } + }); + } else if (opts.what === 'all' && opts.where === 'visible') { + messageType = 'everything_in_view'; + checkForHiddenIssues({ + elsewhere: { + what: 'all', + where: 'all' + }, + disabled_rules: { + what: 'all', + where: 'visible', + includeDisabledRules: 'only' + }, + disabled_rules_elsewhere: { + what: 'all', + where: 'all', + includeDisabledRules: 'only' + }, + ignored_issues: { + what: 'all', + where: 'visible', + includeIgnored: 'only' + }, + ignored_issues_elsewhere: { + what: 'all', + where: 'all', + includeIgnored: 'only' + } + }); + } else if (opts.what === 'all' && opts.where === 'all') { + messageType = 'everything'; + checkForHiddenIssues({ + disabled_rules: { + what: 'all', + where: 'all', + includeDisabledRules: 'only' + }, + ignored_issues: { + what: 'all', + where: 'all', + includeIgnored: 'only' + } + }); + } + + if (opts.what === 'edited' && context.history().difference().summary().length === 0) { + messageType = 'no_edits'; + } + + selection.select('.box .message').html(_t.html('issues.no_issues.message.' + messageType)); + } + + context.validator().on('validated.uiSectionValidationStatus', function () { + window.requestIdleCallback(section.reRender); + }); + context.map().on('move.uiSectionValidationStatus', debounce(function () { + window.requestIdleCallback(section.reRender); + }, 1000)); + return section; + } + + function uiPaneIssues(context) { + var issuesPane = uiPane('issues', context).key(_t('issues.key')).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)]); + return issuesPane; + } + + function uiSettingsCustomData(context) { + var dispatch = dispatch$8('change'); + + function render(selection) { + var dataLayer = context.layers().layer('data'); // keep separate copies of original and current settings + + var _origSettings = { + fileList: dataLayer && dataLayer.fileList() || null, + url: corePreferences('settings-custom-data-url') + }; + var _currSettings = { + fileList: dataLayer && dataLayer.fileList() || null, + url: corePreferences('settings-custom-data-url') + }; // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png'; + + var modal = uiConfirm(selection).okButton(); + modal.classed('settings-modal settings-custom-data', true); + modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_data.header')); + var textSection = modal.select('.modal-section.message-text'); + textSection.append('pre').attr('class', 'instructions-file').html(_t.html('settings.custom_data.file.instructions')); + textSection.append('input').attr('class', 'field-file').attr('type', 'file').property('files', _currSettings.fileList) // works for all except IE11 + .on('change', function (d3_event) { + var files = d3_event.target.files; + + if (files && files.length) { + _currSettings.url = ''; + textSection.select('.field-url').property('value', ''); + _currSettings.fileList = files; + } else { + _currSettings.fileList = null; + } + }); + textSection.append('h4').html(_t.html('settings.custom_data.or')); + textSection.append('pre').attr('class', 'instructions-url').html(_t.html('settings.custom_data.url.instructions')); + textSection.append('textarea').attr('class', 'field-url').attr('placeholder', _t('settings.custom_data.url.placeholder')).call(utilNoAuto).property('value', _currSettings.url); // insert a cancel button + + var buttonSection = modal.select('.modal-section.buttons'); + buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel')); + buttonSection.select('.cancel-button').on('click.cancel', clickCancel); + buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave); + + function isSaveDisabled() { + return null; + } // restore the original url + + + function clickCancel() { + textSection.select('.field-url').property('value', _origSettings.url); + corePreferences('settings-custom-data-url', _origSettings.url); + this.blur(); + modal.close(); + } // accept the current url + + + function clickSave() { + _currSettings.url = textSection.select('.field-url').property('value').trim(); // one or the other but not both + + if (_currSettings.url) { + _currSettings.fileList = null; + } + + if (_currSettings.fileList) { + _currSettings.url = ''; + } + + corePreferences('settings-custom-data-url', _currSettings.url); + this.blur(); + modal.close(); + dispatch.call('change', this, _currSettings); + } + } + + return utilRebind(render, dispatch, 'on'); + } + + function uiSectionDataLayers(context) { + var settingsCustomData = uiSettingsCustomData(context).on('change', customChanged); + var layers = context.layers(); + var section = uiSection('data-layers', context).label(_t.html('map_data.data_layers')).disclosureContent(renderDisclosureContent); + + function renderDisclosureContent(selection) { + var container = selection.selectAll('.data-layer-container').data([0]); + container.enter().append('div').attr('class', 'data-layer-container').merge(container).call(drawOsmItems).call(drawQAItems).call(drawCustomDataItems).call(drawVectorItems) // Beta - Detroit mapping challenge + .call(drawPanelItems); + } + + function showsLayer(which) { + var layer = layers.layer(which); + + if (layer) { + return layer.enabled(); + } + + return false; + } + + function setLayer(which, enabled) { + // Don't allow layer changes while drawing - #6584 + var mode = context.mode(); + if (mode && /^draw/.test(mode.id)) return; + var layer = layers.layer(which); + + if (layer) { + layer.enabled(enabled); + + if (!enabled && (which === 'osm' || which === 'notes')) { + context.enter(modeBrowse(context)); + } + } + } + + function toggleLayer(which) { + setLayer(which, !showsLayer(which)); + } + + function drawOsmItems(selection) { + var osmKeys = ['osm', 'notes']; + var osmLayers = layers.all().filter(function (obj) { + return osmKeys.indexOf(obj.id) !== -1; + }); + var ul = selection.selectAll('.layer-list-osm').data([0]); + ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-osm').merge(ul); + var li = ul.selectAll('.list-item').data(osmLayers); + li.exit().remove(); + var liEnter = li.enter().append('li').attr('class', function (d) { + return 'list-item list-item-' + d.id; + }); + var labelEnter = liEnter.append('label').each(function (d) { + if (d.id === 'osm') { + select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))]).placement('bottom')); + } else { + select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom')); + } + }); + labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) { + toggleLayer(d.id); + }); + labelEnter.append('span').html(function (d) { + return _t.html('map_data.layers.' + d.id + '.title'); + }); // Update + + li.merge(liEnter).classed('active', function (d) { + return d.layer.enabled(); + }).selectAll('input').property('checked', function (d) { + return d.layer.enabled(); + }); + } + + function drawQAItems(selection) { + var qaKeys = ['keepRight', 'improveOSM', 'osmose']; + var qaLayers = layers.all().filter(function (obj) { + return qaKeys.indexOf(obj.id) !== -1; + }); + var ul = selection.selectAll('.layer-list-qa').data([0]); + ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-qa').merge(ul); + var li = ul.selectAll('.list-item').data(qaLayers); + li.exit().remove(); + var liEnter = li.enter().append('li').attr('class', function (d) { + return 'list-item list-item-' + d.id; + }); + var labelEnter = liEnter.append('label').each(function (d) { + select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom')); + }); + labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) { + toggleLayer(d.id); + }); + labelEnter.append('span').html(function (d) { + return _t.html('map_data.layers.' + d.id + '.title'); + }); // Update + + li.merge(liEnter).classed('active', function (d) { + return d.layer.enabled(); + }).selectAll('input').property('checked', function (d) { + return d.layer.enabled(); + }); + } // Beta feature - sample vector layers to support Detroit Mapping Challenge + // https://github.com/osmus/detroit-mapping-challenge + + + function drawVectorItems(selection) { + var dataLayer = layers.layer('data'); + var vtData = [{ + name: 'Detroit Neighborhoods/Parks', + src: 'neighborhoods-parks', + tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.', + template: 'https://{switch:a,b,c,d}.tiles.mapbox.com/v4/jonahadkins.cjksmur6x34562qp9iv1u3ksf-54hev,jonahadkins.cjksmqxdx33jj2wp90xd9x2md-4e5y2/{z}/{x}/{y}.vector.pbf?access_token=pk.eyJ1Ijoiam9uYWhhZGtpbnMiLCJhIjoiRlVVVkx3VSJ9.9sdVEK_B_VkEXPjssU5MqA' + }, { + name: 'Detroit Composite POIs', + src: 'composite-poi', + tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.', + template: 'https://{switch:a,b,c,d}.tiles.mapbox.com/v4/jonahadkins.cjksmm6a02sli31myxhsr7zf3-2sw8h/{z}/{x}/{y}.vector.pbf?access_token=pk.eyJ1Ijoiam9uYWhhZGtpbnMiLCJhIjoiRlVVVkx3VSJ9.9sdVEK_B_VkEXPjssU5MqA' + }, { + name: 'Detroit All-The-Places POIs', + src: 'alltheplaces-poi', + tooltip: 'Public domain business location data created by web scrapers.', + template: 'https://{switch:a,b,c,d}.tiles.mapbox.com/v4/jonahadkins.cjksmswgk340g2vo06p1w9w0j-8fjjc/{z}/{x}/{y}.vector.pbf?access_token=pk.eyJ1Ijoiam9uYWhhZGtpbnMiLCJhIjoiRlVVVkx3VSJ9.9sdVEK_B_VkEXPjssU5MqA' + }]; // Only show this if the map is around Detroit.. + + var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]); + var showVectorItems = context.map().zoom() > 9 && detroit.contains(context.map().center()); + var container = selection.selectAll('.vectortile-container').data(showVectorItems ? [0] : []); + container.exit().remove(); + var containerEnter = container.enter().append('div').attr('class', 'vectortile-container'); + containerEnter.append('h4').attr('class', 'vectortile-header').html('Detroit Vector Tiles (Beta)'); + containerEnter.append('ul').attr('class', 'layer-list layer-list-vectortile'); + containerEnter.append('div').attr('class', 'vectortile-footer').append('a').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).attr('href', 'https://github.com/osmus/detroit-mapping-challenge').append('span').html('About these layers'); + container = container.merge(containerEnter); + var ul = container.selectAll('.layer-list-vectortile'); + var li = ul.selectAll('.list-item').data(vtData); + li.exit().remove(); + var liEnter = li.enter().append('li').attr('class', function (d) { + return 'list-item list-item-' + d.src; + }); + var labelEnter = liEnter.append('label').each(function (d) { + select(this).call(uiTooltip().title(d.tooltip).placement('top')); + }); + labelEnter.append('input').attr('type', 'radio').attr('name', 'vectortile').on('change', selectVTLayer); + labelEnter.append('span').html(function (d) { + return d.name; + }); // Update + + li.merge(liEnter).classed('active', isVTLayerSelected).selectAll('input').property('checked', isVTLayerSelected); + + function isVTLayerSelected(d) { + return dataLayer && dataLayer.template() === d.template; + } + + function selectVTLayer(d3_event, d) { + corePreferences('settings-custom-data-url', d.template); + + if (dataLayer) { + dataLayer.template(d.template, d.src); + dataLayer.enabled(true); + } + } + } + + function drawCustomDataItems(selection) { + var dataLayer = layers.layer('data'); + var hasData = dataLayer && dataLayer.hasData(); + var showsData = hasData && dataLayer.enabled(); + var ul = selection.selectAll('.layer-list-data').data(dataLayer ? [0] : []); // Exit + + ul.exit().remove(); // Enter + + var ulEnter = ul.enter().append('ul').attr('class', 'layer-list layer-list-data'); + var liEnter = ulEnter.append('li').attr('class', 'list-item-data'); + var labelEnter = liEnter.append('label').call(uiTooltip().title(_t.html('map_data.layers.custom.tooltip')).placement('top')); + labelEnter.append('input').attr('type', 'checkbox').on('change', function () { + toggleLayer('data'); + }); + labelEnter.append('span').html(_t.html('map_data.layers.custom.title')); + 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) { + d3_event.preventDefault(); + editCustom(); + }).call(svgIcon('#iD-icon-more')); + 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) { + if (select(this).classed('disabled')) return; + d3_event.preventDefault(); + d3_event.stopPropagation(); + dataLayer.fitZoom(); + }).call(svgIcon('#iD-icon-framed-dot', 'monochrome')); // Update + + ul = ul.merge(ulEnter); + ul.selectAll('.list-item-data').classed('active', showsData).selectAll('label').classed('deemphasize', !hasData).selectAll('input').property('disabled', !hasData).property('checked', showsData); + ul.selectAll('button.zoom-to-data').classed('disabled', !hasData); + } + + function editCustom() { + context.container().call(settingsCustomData); + } + + function customChanged(d) { + var dataLayer = layers.layer('data'); + + if (d && d.url) { + dataLayer.url(d.url); + } else if (d && d.fileList) { + dataLayer.fileList(d.fileList); + } + } + + function drawPanelItems(selection) { + var panelsListEnter = selection.selectAll('.md-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list md-extras-list'); + var historyPanelLabelEnter = panelsListEnter.append('li').attr('class', 'history-panel-toggle-item').append('label').call(uiTooltip().title(_t.html('map_data.history_panel.tooltip')).keys([uiCmd('⌘⇧' + _t('info_panels.history.key'))]).placement('top')); + historyPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) { + d3_event.preventDefault(); + context.ui().info.toggle('history'); + }); + historyPanelLabelEnter.append('span').html(_t.html('map_data.history_panel.title')); + 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')); + measurementPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) { + d3_event.preventDefault(); + context.ui().info.toggle('measurement'); + }); + measurementPanelLabelEnter.append('span').html(_t.html('map_data.measurement_panel.title')); + } + + context.layers().on('change.uiSectionDataLayers', section.reRender); + context.map().on('move.uiSectionDataLayers', debounce(function () { + // Detroit layers may have moved in or out of view + window.requestIdleCallback(section.reRender); + }, 1000)); + return section; + } + + function uiSectionMapFeatures(context) { + var _features = context.features().keys(); + + var section = uiSection('map-features', context).label(_t.html('map_data.map_features')).disclosureContent(renderDisclosureContent).expandedByDefault(false); + + function renderDisclosureContent(selection) { + var container = selection.selectAll('.layer-feature-list-container').data([0]); + var containerEnter = container.enter().append('div').attr('class', 'layer-feature-list-container'); + containerEnter.append('ul').attr('class', 'layer-list layer-feature-list'); + var footer = containerEnter.append('div').attr('class', 'feature-list-links section-footer'); + footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) { + d3_event.preventDefault(); + context.features().disableAll(); + }); + footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) { + d3_event.preventDefault(); + context.features().enableAll(); + }); // Update + + container = container.merge(containerEnter); + container.selectAll('.layer-feature-list').call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature); + } + + function drawListItems(selection, data, type, name, change, active) { + var items = selection.selectAll('li').data(data); // Exit + + items.exit().remove(); // Enter + + var enter = items.enter().append('li').call(uiTooltip().title(function (d) { + var tip = _t.html(name + '.' + d + '.tooltip'); + + if (autoHiddenFeature(d)) { + var msg = showsLayer('osm') ? _t.html('map_data.autohidden') : _t.html('map_data.osmhidden'); + tip += '
    ' + msg + '
    '; + } + + return tip; + }).placement('top')); + var label = enter.append('label'); + label.append('input').attr('type', type).attr('name', name).on('change', change); + label.append('span').html(function (d) { + return _t.html(name + '.' + d + '.description'); + }); // Update + + items = items.merge(enter); + items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', autoHiddenFeature); + } + + function autoHiddenFeature(d) { + return context.features().autoHidden(d); + } + + function showsFeature(d) { + return context.features().enabled(d); + } + + function clickFeature(d3_event, d) { + context.features().toggle(d); + } + + function showsLayer(id) { + var layer = context.layers().layer(id); + return layer && layer.enabled(); + } // add listeners + + + context.features().on('change.map_features', section.reRender); + return section; + } + + function uiSectionMapStyleOptions(context) { + var section = uiSection('fill-area', context).label(_t.html('map_data.style_options')).disclosureContent(renderDisclosureContent).expandedByDefault(false); + + function renderDisclosureContent(selection) { + var container = selection.selectAll('.layer-fill-list').data([0]); + container.enter().append('ul').attr('class', 'layer-list layer-fill-list').merge(container).call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill); + var container2 = selection.selectAll('.layer-visual-diff-list').data([0]); + container2.enter().append('ul').attr('class', 'layer-list layer-visual-diff-list').merge(container2).call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function () { + return context.surface().classed('highlight-edited'); + }); + } + + function drawListItems(selection, data, type, name, change, active) { + var items = selection.selectAll('li').data(data); // Exit + + items.exit().remove(); // Enter + + var enter = items.enter().append('li').call(uiTooltip().title(function (d) { + return _t.html(name + '.' + d + '.tooltip'); + }).keys(function (d) { + var key = d === 'wireframe' ? _t('area_fill.wireframe.key') : null; + if (d === 'highlight_edits') key = _t('map_data.highlight_edits.key'); + return key ? [key] : null; + }).placement('top')); + var label = enter.append('label'); + label.append('input').attr('type', type).attr('name', name).on('change', change); + label.append('span').html(function (d) { + return _t.html(name + '.' + d + '.description'); + }); // Update + + items = items.merge(enter); + items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false); + } + + function isActiveFill(d) { + return context.map().activeAreaFill() === d; + } + + function toggleHighlightEdited(d3_event) { + d3_event.preventDefault(); + context.map().toggleHighlightEdited(); + } + + function setFill(d3_event, d) { + context.map().activeAreaFill(d); + } + + context.map().on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender); + return section; + } + + function uiSectionPhotoOverlays(context) { + var layers = context.layers(); + var section = uiSection('photo-overlays', context).label(_t.html('photo_overlays.title')).disclosureContent(renderDisclosureContent).expandedByDefault(false); + + function renderDisclosureContent(selection) { + var container = selection.selectAll('.photo-overlay-container').data([0]); + container.enter().append('div').attr('class', 'photo-overlay-container').merge(container).call(drawPhotoItems).call(drawPhotoTypeItems).call(drawDateFilter).call(drawUsernameFilter); + } + + function drawPhotoItems(selection) { + var photoKeys = context.photos().overlayLayerIDs(); + var photoLayers = layers.all().filter(function (obj) { + return photoKeys.indexOf(obj.id) !== -1; + }); + var data = photoLayers.filter(function (obj) { + return obj.layer.supported(); + }); + + function layerSupported(d) { + return d.layer && d.layer.supported(); + } + + function layerEnabled(d) { + return layerSupported(d) && d.layer.enabled(); + } + + var ul = selection.selectAll('.layer-list-photos').data([0]); + ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photos').merge(ul); + var li = ul.selectAll('.list-item-photos').data(data); + li.exit().remove(); + var liEnter = li.enter().append('li').attr('class', function (d) { + var classes = 'list-item-photos list-item-' + d.id; + + if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') { + classes += ' indented'; + } + + return classes; + }); + var labelEnter = liEnter.append('label').each(function (d) { + var titleID; + if (d.id === 'mapillary-signs') titleID = 'mapillary.signs.tooltip';else if (d.id === 'mapillary') titleID = 'mapillary_images.tooltip';else if (d.id === 'openstreetcam') titleID = 'openstreetcam_images.tooltip';else titleID = d.id.replace(/-/g, '_') + '.tooltip'; + select(this).call(uiTooltip().title(_t.html(titleID)).placement('top')); + }); + labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) { + toggleLayer(d.id); + }); + labelEnter.append('span').html(function (d) { + var id = d.id; + if (id === 'mapillary-signs') id = 'photo_overlays.traffic_signs'; + return _t.html(id.replace(/-/g, '_') + '.title'); + }); // Update + + li.merge(liEnter).classed('active', layerEnabled).selectAll('input').property('checked', layerEnabled); + } + + function drawPhotoTypeItems(selection) { + var data = context.photos().allPhotoTypes(); + + function typeEnabled(d) { + return context.photos().showsPhotoType(d); + } + + var ul = selection.selectAll('.layer-list-photo-types').data([0]); + ul.exit().remove(); + ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photo-types').merge(ul); + var li = ul.selectAll('.list-item-photo-types').data(context.photos().shouldFilterByPhotoType() ? data : []); + li.exit().remove(); + var liEnter = li.enter().append('li').attr('class', function (d) { + return 'list-item-photo-types list-item-' + d; + }); + var labelEnter = liEnter.append('label').each(function (d) { + select(this).call(uiTooltip().title(_t.html('photo_overlays.photo_type.' + d + '.tooltip')).placement('top')); + }); + labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) { + context.photos().togglePhotoType(d); + }); + labelEnter.append('span').html(function (d) { + return _t.html('photo_overlays.photo_type.' + d + '.title'); + }); // Update + + li.merge(liEnter).classed('active', typeEnabled).selectAll('input').property('checked', typeEnabled); + } + + function drawDateFilter(selection) { + var data = context.photos().dateFilters(); + + function filterEnabled(d) { + return context.photos().dateFilterValue(d); + } + + var ul = selection.selectAll('.layer-list-date-filter').data([0]); + ul.exit().remove(); + ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-date-filter').merge(ul); + var li = ul.selectAll('.list-item-date-filter').data(context.photos().shouldFilterByDate() ? data : []); + li.exit().remove(); + var liEnter = li.enter().append('li').attr('class', 'list-item-date-filter'); + var labelEnter = liEnter.append('label').each(function (d) { + select(this).call(uiTooltip().title(_t.html('photo_overlays.date_filter.' + d + '.tooltip')).placement('top')); + }); + labelEnter.append('span').html(function (d) { + return _t.html('photo_overlays.date_filter.' + d + '.title'); + }); + labelEnter.append('input').attr('type', 'date').attr('class', 'list-item-input').attr('placeholder', _t('units.year_month_day')).call(utilNoAuto).each(function (d) { + utilGetSetValue(select(this), context.photos().dateFilterValue(d) || ''); + }).on('change', function (d3_event, d) { + var value = utilGetSetValue(select(this)).trim(); + context.photos().setDateFilter(d, value, true); // reload the displayed dates + + li.selectAll('input').each(function (d) { + utilGetSetValue(select(this), context.photos().dateFilterValue(d) || ''); + }); + }); + li = li.merge(liEnter).classed('active', filterEnabled); + } + + function drawUsernameFilter(selection) { + function filterEnabled() { + return context.photos().usernames(); + } + + var ul = selection.selectAll('.layer-list-username-filter').data([0]); + ul.exit().remove(); + ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-username-filter').merge(ul); + var li = ul.selectAll('.list-item-username-filter').data(context.photos().shouldFilterByUsername() ? ['username-filter'] : []); + li.exit().remove(); + var liEnter = li.enter().append('li').attr('class', 'list-item-username-filter'); + var labelEnter = liEnter.append('label').each(function () { + select(this).call(uiTooltip().title(_t.html('photo_overlays.username_filter.tooltip')).placement('top')); + }); + labelEnter.append('span').html(_t.html('photo_overlays.username_filter.title')); + labelEnter.append('input').attr('type', 'text').attr('class', 'list-item-input').call(utilNoAuto).property('value', usernameValue).on('change', function () { + var value = select(this).property('value'); + context.photos().setUsernameFilter(value, true); + select(this).property('value', usernameValue); + }); + li.merge(liEnter).classed('active', filterEnabled); + + function usernameValue() { + var usernames = context.photos().usernames(); + if (usernames) return usernames.join('; '); + return usernames; + } + } + + function toggleLayer(which) { + setLayer(which, !showsLayer(which)); + } + + function showsLayer(which) { + var layer = layers.layer(which); + + if (layer) { + return layer.enabled(); + } + + return false; + } + + function setLayer(which, enabled) { + var layer = layers.layer(which); + + if (layer) { + layer.enabled(enabled); + } + } + + context.layers().on('change.uiSectionPhotoOverlays', section.reRender); + context.photos().on('change.uiSectionPhotoOverlays', section.reRender); + return section; + } + + function uiPaneMapData(context) { + var mapDataPane = uiPane('map-data', context).key(_t('map_data.key')).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)]); + return mapDataPane; + } + + function uiSectionPrivacy(context) { + var section = uiSection('preferences-third-party', context).label(_t.html('preferences.privacy.title')).disclosureContent(renderDisclosureContent); + + var _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true'; + + function renderDisclosureContent(selection) { + // enter + var privacyOptionsListEnter = selection.selectAll('.privacy-options-list').data([0]).enter().append('ul').attr('class', 'layer-list privacy-options-list'); + 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')); + thirdPartyIconsEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) { + d3_event.preventDefault(); + _showThirdPartyIcons = _showThirdPartyIcons === 'true' ? 'false' : 'true'; + corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons); + update(); + }); + thirdPartyIconsEnter.append('span').html(_t.html('preferences.privacy.third_party_icons.description')); // Privacy Policy link + + selection.selectAll('.privacy-link').data([0]).enter().append('div').attr('class', 'privacy-link').append('a').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).attr('href', 'https://github.com/openstreetmap/iD/blob/release/PRIVACY.md').append('span').html(_t.html('preferences.privacy.privacy_link')); + update(); + + function update() { + selection.selectAll('.privacy-third-party-icons-item').classed('active', _showThirdPartyIcons === 'true').select('input').property('checked', _showThirdPartyIcons === 'true'); + } + } + + return section; + } + + function uiPanePreferences(context) { + 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)]); + return preferencesPane; + } + + function uiInit(context) { + var _initCounter = 0; + var _needWidth = {}; + + var _lastPointerType; + + function render(container) { + container.on('click.ui', function (d3_event) { + // we're only concerned with the primary mouse button + if (d3_event.button !== 0) return; + if (!d3_event.composedPath) return; // some targets have default click events we don't want to override + + var isOkayTarget = d3_event.composedPath().some(function (node) { + // we only care about element nodes + return node.nodeType === 1 && ( // clicking focuses it and/or changes a value + node.nodeName === 'INPUT' || // clicking