3 var bootstrap = (typeof exports.bootstrap === "object") ?
5 (exports.bootstrap = {});
7 bootstrap.tooltip = function() {
9 var tooltip = function(selection) {
10 selection.each(setup);
12 animation = d3.functor(false),
13 html = d3.functor(false),
15 var title = this.getAttribute("data-original-title");
19 title = this.getAttribute("title");
20 this.removeAttribute("title");
21 this.setAttribute("data-original-title", title);
25 over = "mouseenter.tooltip",
26 out = "mouseleave.tooltip",
27 placements = "top left bottom right".split(" "),
28 placement = d3.functor("top");
30 tooltip.title = function(_) {
31 if (arguments.length) {
32 title = d3.functor(_);
39 tooltip.html = function(_) {
40 if (arguments.length) {
48 tooltip.placement = function(_) {
49 if (arguments.length) {
50 placement = d3.functor(_);
57 tooltip.show = function(selection) {
61 tooltip.hide = function(selection) {
65 tooltip.toggle = function(selection) {
66 selection.each(toggle);
69 tooltip.destroy = function(selection) {
73 .attr("title", function() {
74 return this.getAttribute("data-original-title") || this.getAttribute("title");
76 .attr("data-original-title", null)
82 var root = d3.select(this),
83 animate = animation.apply(this, arguments),
84 tip = root.append("div")
85 .attr("class", "tooltip");
88 tip.classed("fade", true);
91 // TODO "inside" checks?
94 .attr("class", "tooltip-arrow");
96 .attr("class", "tooltip-inner");
98 var place = placement.apply(this, arguments);
99 tip.classed(place, true);
106 var root = d3.select(this),
107 content = title.apply(this, arguments),
108 tip = root.select(".tooltip")
109 .classed("in", true),
110 markup = html.apply(this, arguments),
111 innercontent = tip.select(".tooltip-inner")[markup ? "html" : "text"](content),
112 place = placement.apply(this, arguments),
113 outer = getPosition(root.node()),
114 inner = getPosition(tip.node()),
119 pos = {x: outer.x + (outer.w - inner.w) / 2, y: outer.y - inner.h};
122 pos = {x: outer.x + outer.w, y: outer.y + (outer.h - inner.h) / 2};
125 pos = {x: outer.x - inner.w, y: outer.y + (outer.h - inner.h) / 2};
128 pos = {x: Math.max(0, outer.x + (outer.w - inner.w) / 2), y: outer.y + outer.h};
133 {left: ~~pos.x + "px", top: ~~pos.y + "px"} :
134 {left: null, top: null});
136 this.tooltipVisible = true;
140 d3.select(this).select(".tooltip")
141 .classed("in", false);
143 this.tooltipVisible = false;
147 if (this.tooltipVisible) {
148 hide.apply(this, arguments);
150 show.apply(this, arguments);
157 function getPosition(node) {
158 var mode = d3.select(node).style('position');
159 if (mode === 'absolute' || mode === 'static') {
178 var d3 = {version: "3.5.5"}; // semver
179 d3.ascending = d3_ascending;
181 function d3_ascending(a, b) {
182 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
184 d3.descending = function(a, b) {
185 return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
187 d3.min = function(array, f) {
192 if (arguments.length === 1) {
193 while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; }
194 while (++i < n) if ((b = array[i]) != null && a > b) a = b;
196 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = b; break; }
197 while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
201 d3.max = function(array, f) {
206 if (arguments.length === 1) {
207 while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; }
208 while (++i < n) if ((b = array[i]) != null && b > a) a = b;
210 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = b; break; }
211 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
215 d3.extent = function(array, f) {
221 if (arguments.length === 1) {
222 while (++i < n) if ((b = array[i]) != null && b >= b) { a = c = b; break; }
223 while (++i < n) if ((b = array[i]) != null) {
228 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = c = b; break; }
229 while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
236 function d3_number(x) {
237 return x === null ? NaN : +x;
240 function d3_numeric(x) {
244 d3.sum = function(array, f) {
249 if (arguments.length === 1) {
250 while (++i < n) if (d3_numeric(a = +array[i])) s += a; // zero and null are equivalent
252 while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a;
257 d3.mean = function(array, f) {
263 if (arguments.length === 1) {
264 while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j;
266 while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j;
270 // R-7 per <http://en.wikipedia.org/wiki/Quantile>
271 d3.quantile = function(values, p) {
272 var H = (values.length - 1) * p + 1,
276 return e ? v + e * (values[h] - v) : v;
279 d3.median = function(array, f) {
284 if (arguments.length === 1) {
285 while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a);
287 while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a);
289 if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5);
292 d3.variance = function(array, f) {
293 var n = array.length,
300 if (arguments.length === 1) {
302 if (d3_numeric(a = d3_number(array[i]))) {
310 if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) {
317 if (j > 1) return s / (j - 1);
320 d3.deviation = function() {
321 var v = d3.variance.apply(this, arguments);
322 return v ? Math.sqrt(v) : v;
325 function d3_bisector(compare) {
327 left: function(a, x, lo, hi) {
328 if (arguments.length < 3) lo = 0;
329 if (arguments.length < 4) hi = a.length;
331 var mid = lo + hi >>> 1;
332 if (compare(a[mid], x) < 0) lo = mid + 1;
337 right: function(a, x, lo, hi) {
338 if (arguments.length < 3) lo = 0;
339 if (arguments.length < 4) hi = a.length;
341 var mid = lo + hi >>> 1;
342 if (compare(a[mid], x) > 0) hi = mid;
350 var d3_bisect = d3_bisector(d3_ascending);
351 d3.bisectLeft = d3_bisect.left;
352 d3.bisect = d3.bisectRight = d3_bisect.right;
354 d3.bisector = function(f) {
355 return d3_bisector(f.length === 1
356 ? function(d, x) { return d3_ascending(f(d), x); }
359 d3.shuffle = function(array, i0, i1) {
360 if ((m = arguments.length) < 3) { i1 = array.length; if (m < 2) i0 = 0; }
361 var m = i1 - i0, t, i;
363 i = Math.random() * m-- | 0;
364 t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t;
368 d3.permute = function(array, indexes) {
369 var i = indexes.length, permutes = new Array(i);
370 while (i--) permutes[i] = array[indexes[i]];
373 d3.pairs = function(array) {
374 var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
375 while (i < n) pairs[i] = [p0 = p1, p1 = array[++i]];
379 d3.zip = function() {
380 if (!(n = arguments.length)) return [];
381 for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m;) {
382 for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n;) {
383 zip[j] = arguments[j][i];
389 function d3_zipLength(d) {
393 d3.transpose = function(matrix) {
394 return d3.zip.apply(d3, matrix);
396 d3.keys = function(map) {
398 for (var key in map) keys.push(key);
401 d3.values = function(map) {
403 for (var key in map) values.push(map[key]);
406 d3.entries = function(map) {
408 for (var key in map) entries.push({key: key, value: map[key]});
411 d3.merge = function(arrays) {
412 var n = arrays.length,
419 while (++i < n) j += arrays[i].length;
420 merged = new Array(j);
426 merged[--j] = array[m];
434 d3.range = function(start, stop, step) {
435 if (arguments.length < 3) {
437 if (arguments.length < 2) {
442 if ((stop - start) / step === Infinity) throw new Error("infinite range");
444 k = d3_range_integerScale(abs(step)),
447 start *= k, stop *= k, step *= k;
448 if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k);
449 else while ((j = start + step * ++i) < stop) range.push(j / k);
453 function d3_range_integerScale(x) {
455 while (x * k % 1) k *= 10;
458 function d3_class(ctor, properties) {
459 for (var key in properties) {
460 Object.defineProperty(ctor.prototype, key, {
461 value: properties[key],
467 d3.map = function(object, f) {
468 var map = new d3_Map;
469 if (object instanceof d3_Map) {
470 object.forEach(function(key, value) { map.set(key, value); });
471 } else if (Array.isArray(object)) {
475 if (arguments.length === 1) while (++i < n) map.set(i, object[i]);
476 else while (++i < n) map.set(f.call(object, o = object[i], i), o);
478 for (var key in object) map.set(key, object[key]);
484 this._ = Object.create(null);
487 var d3_map_proto = "__proto__",
493 return this._[d3_map_escape(key)];
495 set: function(key, value) {
496 return this._[d3_map_escape(key)] = value;
498 remove: d3_map_remove,
502 for (var key in this._) values.push(this._[key]);
505 entries: function() {
507 for (var key in this._) entries.push({key: d3_map_unescape(key), value: this._[key]});
512 forEach: function(f) {
513 for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]);
517 function d3_map_escape(key) {
518 return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key;
521 function d3_map_unescape(key) {
522 return (key += "")[0] === d3_map_zero ? key.slice(1) : key;
525 function d3_map_has(key) {
526 return d3_map_escape(key) in this._;
529 function d3_map_remove(key) {
530 return (key = d3_map_escape(key)) in this._ && delete this._[key];
533 function d3_map_keys() {
535 for (var key in this._) keys.push(d3_map_unescape(key));
539 function d3_map_size() {
541 for (var key in this._) ++size;
545 function d3_map_empty() {
546 for (var key in this._) return false;
550 d3.nest = function() {
557 function map(mapType, array, depth) {
558 if (depth >= keys.length) return rollup
559 ? rollup.call(nest, array) : (sortValues
560 ? array.sort(sortValues)
569 valuesByKey = new d3_Map,
573 if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
576 valuesByKey.set(keyValue, [object]);
582 setter = function(keyValue, values) {
583 object.set(keyValue, map(mapType, values, depth));
587 setter = function(keyValue, values) {
588 object[keyValue] = map(mapType, values, depth);
592 valuesByKey.forEach(setter);
596 function entries(map, depth) {
597 if (depth >= keys.length) return map;
600 sortKey = sortKeys[depth++];
602 map.forEach(function(key, keyMap) {
603 array.push({key: key, values: entries(keyMap, depth)});
607 ? array.sort(function(a, b) { return sortKey(a.key, b.key); })
611 nest.map = function(array, mapType) {
612 return map(mapType, array, 0);
615 nest.entries = function(array) {
616 return entries(map(d3.map, array, 0), 0);
619 nest.key = function(d) {
624 // Specifies the order for the most-recently specified key.
625 // Note: only applies to entries. Map keys are unordered!
626 nest.sortKeys = function(order) {
627 sortKeys[keys.length - 1] = order;
631 // Specifies the order for leaf values.
632 // Applies to both maps and entries array.
633 nest.sortValues = function(order) {
638 nest.rollup = function(f) {
646 d3.set = function(array) {
647 var set = new d3_Set;
648 if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
653 this._ = Object.create(null);
659 this._[d3_map_escape(key += "")] = true;
662 remove: d3_map_remove,
666 forEach: function(f) {
667 for (var key in this._) f.call(this, d3_map_unescape(key));
671 var d3_document = this.document;
673 function d3_documentElement(node) {
675 && (node.ownerDocument // node is a Node
676 || node.document // node is a Window
677 || node).documentElement; // node is a Document
680 function d3_window(node) {
682 && ((node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
683 || (node.document && node) // node is a Window
684 || node.defaultView); // node is a Document
686 // Copies a variable number of methods from source to target.
687 d3.rebind = function(target, source) {
688 var i = 1, n = arguments.length, method;
689 while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
693 // Method is assumed to be a standard D3 getter-setter:
694 // If passed with no arguments, gets the value.
695 // If passed with arguments, sets the value and returns the target.
696 function d3_rebind(target, source, method) {
698 var value = method.apply(source, arguments);
699 return value === source ? target : value;
702 function d3_vendorSymbol(object, name) {
703 if (name in object) return name;
704 name = name.charAt(0).toUpperCase() + name.slice(1);
705 for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
706 var prefixName = d3_vendorPrefixes[i] + name;
707 if (prefixName in object) return prefixName;
711 var d3_vendorPrefixes = ["webkit", "ms", "moz", "Moz", "o", "O"];
712 var d3_arraySlice = [].slice,
713 d3_array = function(list) { return d3_arraySlice.call(list); }; // conversion for NodeLists
714 function d3_noop() {}
716 d3.dispatch = function() {
717 var dispatch = new d3_dispatch,
719 n = arguments.length;
720 while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
724 function d3_dispatch() {}
726 d3_dispatch.prototype.on = function(type, listener) {
727 var i = type.indexOf("."),
730 // Extract optional namespace, e.g., "click.foo"
732 name = type.slice(i + 1);
733 type = type.slice(0, i);
736 if (type) return arguments.length < 2
737 ? this[type].on(name)
738 : this[type].on(name, listener);
740 if (arguments.length === 2) {
741 if (listener == null) for (type in this) {
742 if (this.hasOwnProperty(type)) this[type].on(name, null);
748 function d3_dispatch_event(dispatch) {
750 listenerByName = new d3_Map;
753 var z = listeners, // defensive reference
757 while (++i < n) if (l = z[i].on) l.apply(this, arguments);
761 event.on = function(name, listener) {
762 var l = listenerByName.get(name),
765 // return the current listener, if any
766 if (arguments.length < 2) return l && l.on;
768 // remove the old listener, if any (with copy-on-write)
771 listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
772 listenerByName.remove(name);
775 // add the new listener, if any
776 if (listener) listeners.push(listenerByName.set(name, {on: listener}));
786 function d3_eventPreventDefault() {
787 d3.event.preventDefault();
790 function d3_eventCancel() {
791 d3.event.preventDefault();
792 d3.event.stopPropagation();
795 function d3_eventSource() {
797 while (s = e.sourceEvent) e = s;
801 // Like d3.dispatch, but for custom events abstracting native UI events. These
802 // events have a target component (such as a brush), a target element (such as
803 // the svg:g element containing the brush) and the standard arguments `d` (the
804 // target element's data) and `i` (the selection index of the target element).
805 function d3_eventDispatch(target) {
806 var dispatch = new d3_dispatch,
808 n = arguments.length;
810 while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
812 // Creates a dispatch context for the specified `thiz` (typically, the target
813 // DOM element that received the source event) and `argumentz` (typically, the
814 // data `d` and index `i` of the target element). The returned function can be
815 // used to dispatch an event to any registered listeners; the function takes a
816 // single argument as input, being the event to dispatch. The event must have
817 // a "type" attribute which corresponds to a type registered in the
818 // constructor. This context will automatically populate the "sourceEvent" and
819 // "target" attributes of the event, as well as setting the `d3.event` global
820 // for the duration of the notification.
821 dispatch.of = function(thiz, argumentz) {
822 return function(e1) {
825 e1.sourceEvent = d3.event;
828 dispatch[e1.type].apply(thiz, argumentz);
837 d3.requote = function(s) {
838 return s.replace(d3_requote_re, "\\$&");
841 var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
842 var d3_subclass = {}.__proto__?
844 // Until ECMAScript supports array subclassing, prototype injection works well.
845 function(object, prototype) {
846 object.__proto__ = prototype;
849 // And if your browser doesn't support __proto__, we'll use direct extension.
850 function(object, prototype) {
851 for (var property in prototype) object[property] = prototype[property];
854 function d3_selection(groups) {
855 d3_subclass(groups, d3_selectionPrototype);
859 var d3_select = function(s, n) { return n.querySelector(s); },
860 d3_selectAll = function(s, n) { return n.querySelectorAll(s); },
861 d3_selectMatches = function(n, s) {
862 var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")];
863 d3_selectMatches = function(n, s) {
864 return d3_selectMatcher.call(n, s);
866 return d3_selectMatches(n, s);
869 // Prefer Sizzle, if available.
870 if (typeof Sizzle === "function") {
871 d3_select = function(s, n) { return Sizzle(s, n)[0] || null; };
872 d3_selectAll = Sizzle;
873 d3_selectMatches = Sizzle.matchesSelector;
876 d3.selection = function() {
877 return d3.select(d3_document.documentElement);
880 var d3_selectionPrototype = d3.selection.prototype = [];
883 d3_selectionPrototype.select = function(selector) {
890 selector = d3_selection_selector(selector);
892 for (var j = -1, m = this.length; ++j < m;) {
893 subgroups.push(subgroup = []);
894 subgroup.parentNode = (group = this[j]).parentNode;
895 for (var i = -1, n = group.length; ++i < n;) {
896 if (node = group[i]) {
897 subgroup.push(subnode = selector.call(node, node.__data__, i, j));
898 if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
905 return d3_selection(subgroups);
908 function d3_selection_selector(selector) {
909 return typeof selector === "function" ? selector : function() {
910 return d3_select(selector, this);
914 d3_selectionPrototype.selectAll = function(selector) {
919 selector = d3_selection_selectorAll(selector);
921 for (var j = -1, m = this.length; ++j < m;) {
922 for (var group = this[j], i = -1, n = group.length; ++i < n;) {
923 if (node = group[i]) {
924 subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j)));
925 subgroup.parentNode = node;
930 return d3_selection(subgroups);
933 function d3_selection_selectorAll(selector) {
934 return typeof selector === "function" ? selector : function() {
935 return d3_selectAll(selector, this);
939 svg: "http://www.w3.org/2000/svg",
940 xhtml: "http://www.w3.org/1999/xhtml",
941 xlink: "http://www.w3.org/1999/xlink",
942 xml: "http://www.w3.org/XML/1998/namespace",
943 xmlns: "http://www.w3.org/2000/xmlns/"
948 qualify: function(name) {
949 var i = name.indexOf(":"),
952 prefix = name.slice(0, i);
953 name = name.slice(i + 1);
955 return d3_nsPrefix.hasOwnProperty(prefix)
956 ? {space: d3_nsPrefix[prefix], local: name}
961 d3_selectionPrototype.attr = function(name, value) {
962 if (arguments.length < 2) {
964 // For attr(string), return the attribute value for the first node.
965 if (typeof name === "string") {
966 var node = this.node();
967 name = d3.ns.qualify(name);
969 ? node.getAttributeNS(name.space, name.local)
970 : node.getAttribute(name);
973 // For attr(object), the object specifies the names and values of the
974 // attributes to set or remove. The values may be functions that are
975 // evaluated for each element.
976 for (value in name) this.each(d3_selection_attr(value, name[value]));
980 return this.each(d3_selection_attr(name, value));
983 function d3_selection_attr(name, value) {
984 name = d3.ns.qualify(name);
986 // For attr(string, null), remove the attribute with the specified name.
987 function attrNull() {
988 this.removeAttribute(name);
990 function attrNullNS() {
991 this.removeAttributeNS(name.space, name.local);
994 // For attr(string, string), set the attribute with the specified name.
995 function attrConstant() {
996 this.setAttribute(name, value);
998 function attrConstantNS() {
999 this.setAttributeNS(name.space, name.local, value);
1002 // For attr(string, function), evaluate the function for each element, and set
1003 // or remove the attribute as appropriate.
1004 function attrFunction() {
1005 var x = value.apply(this, arguments);
1006 if (x == null) this.removeAttribute(name);
1007 else this.setAttribute(name, x);
1009 function attrFunctionNS() {
1010 var x = value.apply(this, arguments);
1011 if (x == null) this.removeAttributeNS(name.space, name.local);
1012 else this.setAttributeNS(name.space, name.local, x);
1015 return value == null
1016 ? (name.local ? attrNullNS : attrNull) : (typeof value === "function"
1017 ? (name.local ? attrFunctionNS : attrFunction)
1018 : (name.local ? attrConstantNS : attrConstant));
1020 function d3_collapse(s) {
1021 return s.trim().replace(/\s+/g, " ");
1024 d3_selectionPrototype.classed = function(name, value) {
1025 if (arguments.length < 2) {
1027 // For classed(string), return true only if the first node has the specified
1028 // class or classes. Note that even if the browser supports DOMTokenList, it
1029 // probably doesn't support it on SVG elements (which can be animated).
1030 if (typeof name === "string") {
1031 var node = this.node(),
1032 n = (name = d3_selection_classes(name)).length,
1034 if (value = node.classList) {
1035 while (++i < n) if (!value.contains(name[i])) return false;
1037 value = node.getAttribute("class");
1038 while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
1043 // For classed(object), the object specifies the names of classes to add or
1044 // remove. The values may be functions that are evaluated for each element.
1045 for (value in name) this.each(d3_selection_classed(value, name[value]));
1049 // Otherwise, both a name and a value are specified, and are handled as below.
1050 return this.each(d3_selection_classed(name, value));
1053 function d3_selection_classedRe(name) {
1054 return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
1057 function d3_selection_classes(name) {
1058 return (name + "").trim().split(/^|\s+/);
1061 // Multiple class names are allowed (e.g., "foo bar").
1062 function d3_selection_classed(name, value) {
1063 name = d3_selection_classes(name).map(d3_selection_classedName);
1064 var n = name.length;
1066 function classedConstant() {
1068 while (++i < n) name[i](this, value);
1071 // When the value is a function, the function is still evaluated only once per
1072 // element even if there are multiple class names.
1073 function classedFunction() {
1074 var i = -1, x = value.apply(this, arguments);
1075 while (++i < n) name[i](this, x);
1078 return typeof value === "function"
1083 function d3_selection_classedName(name) {
1084 var re = d3_selection_classedRe(name);
1085 return function(node, value) {
1086 if (c = node.classList) return value ? c.add(name) : c.remove(name);
1087 var c = node.getAttribute("class") || "";
1090 if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
1092 node.setAttribute("class", d3_collapse(c.replace(re, " ")));
1097 d3_selectionPrototype.style = function(name, value, priority) {
1098 var n = arguments.length;
1101 // For style(object) or style(object, string), the object specifies the
1102 // names and values of the attributes to set or remove. The values may be
1103 // functions that are evaluated for each element. The optional string
1104 // specifies the priority.
1105 if (typeof name !== "string") {
1106 if (n < 2) value = "";
1107 for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
1111 // For style(string), return the computed style value for the first node.
1113 var node = this.node();
1114 return d3_window(node).getComputedStyle(node, null).getPropertyValue(name);
1117 // For style(string, string) or style(string, function), use the default
1118 // priority. The priority is ignored for style(string, null).
1122 // Otherwise, a name, value and priority are specified, and handled as below.
1123 return this.each(d3_selection_style(name, value, priority));
1126 function d3_selection_style(name, value, priority) {
1128 // For style(name, null) or style(name, null, priority), remove the style
1129 // property with the specified name. The priority is ignored.
1130 function styleNull() {
1131 this.style.removeProperty(name);
1134 // For style(name, string) or style(name, string, priority), set the style
1135 // property with the specified name, using the specified priority.
1136 function styleConstant() {
1137 this.style.setProperty(name, value, priority);
1140 // For style(name, function) or style(name, function, priority), evaluate the
1141 // function for each element, and set or remove the style property as
1142 // appropriate. When setting, use the specified priority.
1143 function styleFunction() {
1144 var x = value.apply(this, arguments);
1145 if (x == null) this.style.removeProperty(name);
1146 else this.style.setProperty(name, x, priority);
1149 return value == null
1150 ? styleNull : (typeof value === "function"
1151 ? styleFunction : styleConstant);
1154 d3_selectionPrototype.property = function(name, value) {
1155 if (arguments.length < 2) {
1157 // For property(string), return the property value for the first node.
1158 if (typeof name === "string") return this.node()[name];
1160 // For property(object), the object specifies the names and values of the
1161 // properties to set or remove. The values may be functions that are
1162 // evaluated for each element.
1163 for (value in name) this.each(d3_selection_property(value, name[value]));
1167 // Otherwise, both a name and a value are specified, and are handled as below.
1168 return this.each(d3_selection_property(name, value));
1171 function d3_selection_property(name, value) {
1173 // For property(name, null), remove the property with the specified name.
1174 function propertyNull() {
1178 // For property(name, string), set the property with the specified name.
1179 function propertyConstant() {
1183 // For property(name, function), evaluate the function for each element, and
1184 // set or remove the property as appropriate.
1185 function propertyFunction() {
1186 var x = value.apply(this, arguments);
1187 if (x == null) delete this[name];
1188 else this[name] = x;
1191 return value == null
1192 ? propertyNull : (typeof value === "function"
1193 ? propertyFunction : propertyConstant);
1196 d3_selectionPrototype.text = function(value) {
1197 return arguments.length
1198 ? this.each(typeof value === "function"
1199 ? function() { var v = value.apply(this, arguments); this.textContent = v == null ? "" : v; } : value == null
1200 ? function() { if (this.textContent !== "") this.textContent = ""; }
1201 : function() { if (this.textContent !== value) this.textContent = value; })
1202 : this.node().textContent;
1205 d3_selectionPrototype.html = function(value) {
1206 return arguments.length
1207 ? this.each(typeof value === "function"
1208 ? function() { var v = value.apply(this, arguments); this.innerHTML = v == null ? "" : v; } : value == null
1209 ? function() { this.innerHTML = ""; }
1210 : function() { this.innerHTML = value; })
1211 : this.node().innerHTML;
1214 d3_selectionPrototype.append = function(name) {
1215 name = d3_selection_creator(name);
1216 return this.select(function() {
1217 return this.appendChild(name.apply(this, arguments));
1221 function d3_selection_creator(name) {
1224 var document = this.ownerDocument,
1225 namespace = this.namespaceURI;
1227 ? document.createElementNS(namespace, name)
1228 : document.createElement(name);
1231 function createNS() {
1232 return this.ownerDocument.createElementNS(name.space, name.local);
1235 return typeof name === "function" ? name
1236 : (name = d3.ns.qualify(name)).local ? createNS
1240 d3_selectionPrototype.insert = function(name, before) {
1241 name = d3_selection_creator(name);
1242 before = d3_selection_selector(before);
1243 return this.select(function() {
1244 return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
1248 // TODO remove(selector)?
1249 // TODO remove(node)?
1250 // TODO remove(function)?
1251 d3_selectionPrototype.remove = function() {
1252 return this.each(d3_selectionRemove);
1255 function d3_selectionRemove() {
1256 var parent = this.parentNode;
1257 if (parent) parent.removeChild(this);
1260 d3_selectionPrototype.data = function(value, key) {
1266 // If no value is specified, return the first value.
1267 if (!arguments.length) {
1268 value = new Array(n = (group = this[0]).length);
1270 if (node = group[i]) {
1271 value[i] = node.__data__;
1277 function bind(group, groupData) {
1280 m = groupData.length,
1281 n0 = Math.min(n, m),
1282 updateNodes = new Array(m),
1283 enterNodes = new Array(m),
1284 exitNodes = new Array(n),
1289 var nodeByKeyValue = new d3_Map,
1290 keyValues = new Array(n),
1293 for (i = -1; ++i < n;) {
1294 if (nodeByKeyValue.has(keyValue = key.call(node = group[i], node.__data__, i))) {
1295 exitNodes[i] = node; // duplicate selection key
1297 nodeByKeyValue.set(keyValue, node);
1299 keyValues[i] = keyValue;
1302 for (i = -1; ++i < m;) {
1303 if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) {
1304 enterNodes[i] = d3_selection_dataNode(nodeData);
1305 } else if (node !== true) { // no duplicate data key
1306 updateNodes[i] = node;
1307 node.__data__ = nodeData;
1309 nodeByKeyValue.set(keyValue, true);
1312 for (i = -1; ++i < n;) {
1313 if (nodeByKeyValue.get(keyValues[i]) !== true) {
1314 exitNodes[i] = group[i];
1318 for (i = -1; ++i < n0;) {
1320 nodeData = groupData[i];
1322 node.__data__ = nodeData;
1323 updateNodes[i] = node;
1325 enterNodes[i] = d3_selection_dataNode(nodeData);
1328 for (; i < m; ++i) {
1329 enterNodes[i] = d3_selection_dataNode(groupData[i]);
1331 for (; i < n; ++i) {
1332 exitNodes[i] = group[i];
1339 enterNodes.parentNode
1340 = updateNodes.parentNode
1341 = exitNodes.parentNode
1344 enter.push(enterNodes);
1345 update.push(updateNodes);
1346 exit.push(exitNodes);
1349 var enter = d3_selection_enter([]),
1350 update = d3_selection([]),
1351 exit = d3_selection([]);
1353 if (typeof value === "function") {
1355 bind(group = this[i], value.call(group, group.parentNode.__data__, i));
1359 bind(group = this[i], value);
1363 update.enter = function() { return enter; };
1364 update.exit = function() { return exit; };
1368 function d3_selection_dataNode(data) {
1369 return {__data__: data};
1372 d3_selectionPrototype.datum = function(value) {
1373 return arguments.length
1374 ? this.property("__data__", value)
1375 : this.property("__data__");
1378 d3_selectionPrototype.filter = function(filter) {
1384 if (typeof filter !== "function") filter = d3_selection_filter(filter);
1386 for (var j = 0, m = this.length; j < m; j++) {
1387 subgroups.push(subgroup = []);
1388 subgroup.parentNode = (group = this[j]).parentNode;
1389 for (var i = 0, n = group.length; i < n; i++) {
1390 if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
1391 subgroup.push(node);
1396 return d3_selection(subgroups);
1399 function d3_selection_filter(selector) {
1401 return d3_selectMatches(this, selector);
1405 d3_selectionPrototype.order = function() {
1406 for (var j = -1, m = this.length; ++j < m;) {
1407 for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
1408 if (node = group[i]) {
1409 if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
1417 d3_selectionPrototype.sort = function(comparator) {
1418 comparator = d3_selection_sortComparator.apply(this, arguments);
1419 for (var j = -1, m = this.length; ++j < m;) this[j].sort(comparator);
1420 return this.order();
1423 function d3_selection_sortComparator(comparator) {
1424 if (!arguments.length) comparator = d3_ascending;
1425 return function(a, b) {
1426 return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
1430 d3_selectionPrototype.each = function(callback) {
1431 return d3_selection_each(this, function(node, i, j) {
1432 callback.call(node, node.__data__, i, j);
1436 function d3_selection_each(groups, callback) {
1437 for (var j = 0, m = groups.length; j < m; j++) {
1438 for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
1439 if (node = group[i]) callback(node, i, j);
1445 d3_selectionPrototype.call = function(callback) {
1446 var args = d3_array(arguments);
1447 callback.apply(args[0] = this, args);
1451 d3_selectionPrototype.empty = function() {
1452 return !this.node();
1455 d3_selectionPrototype.node = function() {
1456 for (var j = 0, m = this.length; j < m; j++) {
1457 for (var group = this[j], i = 0, n = group.length; i < n; i++) {
1458 var node = group[i];
1459 if (node) return node;
1465 d3_selectionPrototype.size = function() {
1467 d3_selection_each(this, function() { ++n; });
1471 function d3_selection_enter(selection) {
1472 d3_subclass(selection, d3_selection_enterPrototype);
1476 var d3_selection_enterPrototype = [];
1478 d3.selection.enter = d3_selection_enter;
1479 d3.selection.enter.prototype = d3_selection_enterPrototype;
1481 d3_selection_enterPrototype.append = d3_selectionPrototype.append;
1482 d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
1483 d3_selection_enterPrototype.node = d3_selectionPrototype.node;
1484 d3_selection_enterPrototype.call = d3_selectionPrototype.call;
1485 d3_selection_enterPrototype.size = d3_selectionPrototype.size;
1488 d3_selection_enterPrototype.select = function(selector) {
1496 for (var j = -1, m = this.length; ++j < m;) {
1497 upgroup = (group = this[j]).update;
1498 subgroups.push(subgroup = []);
1499 subgroup.parentNode = group.parentNode;
1500 for (var i = -1, n = group.length; ++i < n;) {
1501 if (node = group[i]) {
1502 subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j));
1503 subnode.__data__ = node.__data__;
1505 subgroup.push(null);
1510 return d3_selection(subgroups);
1513 d3_selection_enterPrototype.insert = function(name, before) {
1514 if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
1515 return d3_selectionPrototype.insert.call(this, name, before);
1518 function d3_selection_enterInsertBefore(enter) {
1520 return function(d, i, j) {
1521 var group = enter[j].update,
1524 if (j != j0) j0 = j, i0 = 0;
1525 if (i >= i0) i0 = i + 1;
1526 while (!(node = group[i0]) && ++i0 < n);
1531 // TODO fast singleton implementation?
1532 d3.select = function(node) {
1534 if (typeof node === "string") {
1535 group = [d3_select(node, d3_document)];
1536 group.parentNode = d3_document.documentElement;
1539 group.parentNode = d3_documentElement(node);
1541 return d3_selection([group]);
1544 d3.selectAll = function(nodes) {
1546 if (typeof nodes === "string") {
1547 group = d3_array(d3_selectAll(nodes, d3_document));
1548 group.parentNode = d3_document.documentElement;
1551 group.parentNode = null;
1553 return d3_selection([group]);
1556 d3_selectionPrototype.on = function(type, listener, capture) {
1557 var n = arguments.length;
1560 // For on(object) or on(object, boolean), the object specifies the event
1561 // types and listeners to add or remove. The optional boolean specifies
1562 // whether the listener captures events.
1563 if (typeof type !== "string") {
1564 if (n < 2) listener = false;
1565 for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
1569 // For on(string), return the listener for the first node.
1570 if (n < 2) return (n = this.node()["__on" + type]) && n._;
1572 // For on(string, function), use the default capture.
1576 // Otherwise, a type, listener and capture are specified, and handled as below.
1577 return this.each(d3_selection_on(type, listener, capture));
1580 function d3_selection_on(type, listener, capture) {
1581 var name = "__on" + type,
1582 i = type.indexOf("."),
1583 wrap = d3_selection_onListener;
1585 if (i > 0) type = type.slice(0, i);
1586 var filter = d3_selection_onFilters.get(type);
1587 if (filter) type = filter, wrap = d3_selection_onFilter;
1589 function onRemove() {
1592 this.removeEventListener(type, l, l.$);
1598 var l = wrap(listener, d3_array(arguments));
1599 if (typeof Raven !== 'undefined') l = Raven.wrap(l);
1600 onRemove.call(this);
1601 this.addEventListener(type, this[name] = l, l.$ = capture);
1605 function removeAll() {
1606 var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"),
1608 for (var name in this) {
1609 if (match = name.match(re)) {
1611 this.removeEventListener(match[1], l, l.$);
1618 ? listener ? onAdd : onRemove
1619 : listener ? d3_noop : removeAll;
1622 var d3_selection_onFilters = d3.map({
1623 mouseenter: "mouseover",
1624 mouseleave: "mouseout"
1628 d3_selection_onFilters.forEach(function(k) {
1629 if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
1633 function d3_selection_onListener(listener, argumentz) {
1634 return function(e) {
1635 var o = d3.event; // Events can be reentrant (e.g., focus).
1637 argumentz[0] = this.__data__;
1639 listener.apply(this, argumentz);
1646 function d3_selection_onFilter(listener, argumentz) {
1647 var l = d3_selection_onListener(listener, argumentz);
1648 return function(e) {
1649 var target = this, related = e.relatedTarget;
1650 if (!related || (related !== target && !(related.compareDocumentPosition(target) & 8))) {
1656 var d3_event_dragSelect,
1657 d3_event_dragId = 0;
1659 function d3_event_dragSuppress(node) {
1660 var name = ".dragsuppress-" + ++d3_event_dragId,
1661 click = "click" + name,
1662 w = d3.select(d3_window(node))
1663 .on("touchmove" + name, d3_eventPreventDefault)
1664 .on("dragstart" + name, d3_eventPreventDefault)
1665 .on("selectstart" + name, d3_eventPreventDefault);
1667 if (d3_event_dragSelect == null) {
1668 d3_event_dragSelect = "onselectstart" in node ? false
1669 : d3_vendorSymbol(node.style, "userSelect");
1672 if (d3_event_dragSelect) {
1673 var style = d3_documentElement(node).style,
1674 select = style[d3_event_dragSelect];
1675 style[d3_event_dragSelect] = "none";
1678 return function(suppressClick) {
1680 if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
1681 if (suppressClick) { // suppress the next click, but only if it’s immediate
1682 var off = function() { w.on(click, null); };
1683 w.on(click, function() { d3_eventCancel(); off(); }, true);
1689 d3.mouse = function(container) {
1690 return d3_mousePoint(container, d3_eventSource());
1693 // https://bugs.webkit.org/show_bug.cgi?id=44083
1694 var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0;
1696 function d3_mousePoint(container, e) {
1697 if (e.changedTouches) e = e.changedTouches[0];
1698 var svg = container.ownerSVGElement || container;
1699 if (svg.createSVGPoint) {
1700 var point = svg.createSVGPoint();
1701 if (d3_mouse_bug44083 < 0) {
1702 var window = d3_window(container);
1703 if (window.scrollX || window.scrollY) {
1704 svg = d3.select("body").append("svg").style({
1705 position: "absolute",
1712 var ctm = svg[0][0].getScreenCTM();
1713 d3_mouse_bug44083 = !(ctm.f || ctm.e);
1717 if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY;
1718 else point.x = e.clientX, point.y = e.clientY;
1719 point = point.matrixTransform(container.getScreenCTM().inverse());
1720 return [point.x, point.y];
1722 var rect = container.getBoundingClientRect();
1723 return [e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop];
1726 d3.touches = function(container, touches) {
1727 if (arguments.length < 2) touches = d3_eventSource().touches;
1728 return touches ? d3_array(touches).map(function(touch) {
1729 var point = d3_mousePoint(container, touch);
1730 point.identifier = touch.identifier;
1740 d3_radians = π / 180,
1741 d3_degrees = 180 / π;
1743 function d3_sgn(x) {
1744 return x > 0 ? 1 : x < 0 ? -1 : 0;
1747 // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
1748 // the 3D cross product in a quadrant I Cartesian coordinate system (+x is
1749 // right, +y is up). Returns a positive value if ABC is counter-clockwise,
1750 // negative if clockwise, and zero if the points are collinear.
1751 function d3_cross2d(a, b, c) {
1752 return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
1755 function d3_acos(x) {
1756 return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
1759 function d3_asin(x) {
1760 return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
1763 function d3_sinh(x) {
1764 return ((x = Math.exp(x)) - 1 / x) / 2;
1767 function d3_cosh(x) {
1768 return ((x = Math.exp(x)) + 1 / x) / 2;
1771 function d3_tanh(x) {
1772 return ((x = Math.exp(2 * x)) - 1) / (x + 1);
1775 function d3_haversin(x) {
1776 return (x = Math.sin(x / 2)) * x;
1783 // p0 = [ux0, uy0, w0]
1784 // p1 = [ux1, uy1, w1]
1785 d3.interpolateZoom = function(p0, p1) {
1786 var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],
1787 ux1 = p1[0], uy1 = p1[1], w1 = p1[2];
1791 d2 = dx * dx + dy * dy,
1793 b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1),
1794 b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1),
1795 r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
1796 r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1),
1798 S = (dr || Math.log(w1 / w0)) / ρ;
1800 function interpolate(t) {
1804 var coshr0 = d3_cosh(r0),
1805 u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0));
1809 w0 * coshr0 / d3_cosh(ρ * s + r0)
1812 // Special case for u0 ~= u1.
1816 w0 * Math.exp(ρ * s)
1820 interpolate.duration = S * 1000;
1825 d3.behavior.zoom = function() {
1826 var view = {x: 0, y: 0, k: 1},
1827 translate0, // translate when we started zooming (to avoid drift)
1828 center0, // implicit desired position of translate0 after zooming
1829 center, // explicit desired position of translate0 after zooming
1830 size = [960, 500], // viewport size; required for zoom interpolation
1831 scaleExtent = d3_behavior_zoomInfinity,
1834 mousedown = "mousedown.zoom",
1835 mousemove = "mousemove.zoom",
1836 mouseup = "mouseup.zoom",
1838 touchstart = "touchstart.zoom",
1839 touchtime, // time of last touchstart (to detect double-tap)
1840 event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"),
1846 // Lazily determine the DOM’s support for Wheel events.
1847 // https://developer.mozilla.org/en-US/docs/Mozilla_event_reference/wheel
1848 if (!d3_behavior_zoomWheel) {
1849 d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); }, "wheel")
1850 : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { return d3.event.wheelDelta; }, "mousewheel")
1851 : (d3_behavior_zoomDelta = function() { return -d3.event.detail; }, "MozMousePixelScroll");
1855 g .on(mousedown, mousedowned)
1856 .on(d3_behavior_zoomWheel + ".zoom", mousewheeled)
1857 .on("dblclick.zoom", dblclicked)
1858 .on(touchstart, touchstarted);
1861 zoom.event = function(g) {
1863 var dispatch = event.of(this, arguments),
1865 if (d3_transitionInheritId) {
1866 d3.select(this).transition()
1867 .each("start.zoom", function() {
1868 view = this.__chart__ || {x: 0, y: 0, k: 1}; // pre-transition state
1869 zoomstarted(dispatch);
1871 .tween("zoom:zoom", function() {
1874 cx = center0 ? center0[0] : dx / 2,
1875 cy = center0 ? center0[1] : dy / 2,
1876 i = d3.interpolateZoom(
1877 [(cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k],
1878 [(cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k]
1880 return function(t) {
1881 var l = i(t), k = dx / l[2];
1882 this.__chart__ = view = {x: cx - l[0] * k, y: cy - l[1] * k, k: k};
1886 .each("interrupt.zoom", function() {
1887 zoomended(dispatch);
1889 .each("end.zoom", function() {
1890 zoomended(dispatch);
1893 this.__chart__ = view;
1894 zoomstarted(dispatch);
1896 zoomended(dispatch);
1901 zoom.translate = function(_) {
1902 if (!arguments.length) return [view.x, view.y];
1903 view = {x: +_[0], y: +_[1], k: view.k}; // copy-on-write
1908 zoom.scale = function(_) {
1909 if (!arguments.length) return view.k;
1910 view = {x: view.x, y: view.y, k: +_}; // copy-on-write
1915 zoom.scaleExtent = function(_) {
1916 if (!arguments.length) return scaleExtent;
1917 scaleExtent = _ == null ? d3_behavior_zoomInfinity : [+_[0], +_[1]];
1921 zoom.center = function(_) {
1922 if (!arguments.length) return center;
1923 center = _ && [+_[0], +_[1]];
1927 zoom.size = function(_) {
1928 if (!arguments.length) return size;
1929 size = _ && [+_[0], +_[1]];
1933 zoom.duration = function(_) {
1934 if (!arguments.length) return duration;
1935 duration = +_; // TODO function based on interpolateZoom distance?
1939 zoom.x = function(z) {
1940 if (!arguments.length) return x1;
1943 view = {x: 0, y: 0, k: 1}; // copy-on-write
1947 zoom.y = function(z) {
1948 if (!arguments.length) return y1;
1951 view = {x: 0, y: 0, k: 1}; // copy-on-write
1955 function location(p) {
1956 return [(p[0] - view.x) / view.k, (p[1] - view.y) / view.k];
1960 return [l[0] * view.k + view.x, l[1] * view.k + view.y];
1963 function scaleTo(s) {
1964 view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
1967 function translateTo(p, l) {
1969 view.x += p[0] - l[0];
1970 view.y += p[1] - l[1];
1973 function zoomTo(that, p, l, k) {
1974 that.__chart__ = {x: view.x, y: view.y, k: view.k};
1976 scaleTo(Math.pow(2, k));
1977 translateTo(center0 = p, l);
1979 that = d3.select(that);
1980 if (duration > 0) that = that.transition().duration(duration);
1981 that.call(zoom.event);
1984 function rescale() {
1985 if (x1) x1.domain(x0.range().map(function(x) { return (x - view.x) / view.k; }).map(x0.invert));
1986 if (y1) y1.domain(y0.range().map(function(y) { return (y - view.y) / view.k; }).map(y0.invert));
1989 function zoomstarted(dispatch) {
1990 if (!zooming++) dispatch({type: "zoomstart"});
1993 function zoomed(dispatch) {
1995 dispatch({type: "zoom", scale: view.k, translate: [view.x, view.y]});
1998 function zoomended(dispatch) {
1999 if (!--zooming) dispatch({type: "zoomend"});
2003 function mousedowned() {
2005 target = d3.event.target,
2006 dispatch = event.of(that, arguments),
2008 subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended),
2009 location0 = location(d3.mouse(that)),
2010 dragRestore = d3_event_dragSuppress(that);
2012 d3_selection_interrupt.call(that);
2013 zoomstarted(dispatch);
2017 translateTo(d3.mouse(that), location0);
2022 subject.on(mousemove, null).on(mouseup, null);
2023 dragRestore(dragged && d3.event.target === target);
2024 zoomended(dispatch);
2028 // These closures persist for as long as at least one touch is active.
2029 function touchstarted() {
2031 dispatch = event.of(that, arguments),
2032 locations0 = {}, // touchstart locations
2033 distance0 = 0, // distance² between initial touches
2034 scale0, // scale when we started touching
2035 zoomName = ".zoom-" + d3.event.changedTouches[0].identifier,
2036 touchmove = "touchmove" + zoomName,
2037 touchend = "touchend" + zoomName,
2039 subject = d3.select(that),
2040 dragRestore = d3_event_dragSuppress(that);
2043 zoomstarted(dispatch);
2045 // Workaround for Chrome issue 412723: the touchstart listener must be set
2046 // after the touchmove listener.
2047 subject.on(mousedown, null).on(touchstart, started); // prevent duplicate events
2049 // Updates locations of any touches in locations0.
2050 function relocate() {
2051 var touches = d3.touches(that);
2053 touches.forEach(function(t) {
2054 if (t.identifier in locations0) locations0[t.identifier] = location(t);
2059 // Temporarily override touchstart while gesture is active.
2060 function started() {
2062 // Listen for touchmove and touchend on the target of touchstart.
2063 var target = d3.event.target;
2064 d3.select(target).on(touchmove, moved).on(touchend, ended);
2065 targets.push(target);
2067 // Only track touches started on the same subject element.
2068 var changed = d3.event.changedTouches;
2069 for (var i = 0, n = changed.length; i < n; ++i) {
2070 locations0[changed[i].identifier] = null;
2073 var touches = relocate(),
2076 if (touches.length === 1) {
2077 if (now - touchtime < 500) { // dbltap
2079 zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1);
2080 d3_eventPreventDefault();
2083 } else if (touches.length > 1) {
2084 var p = touches[0], q = touches[1],
2085 dx = p[0] - q[0], dy = p[1] - q[1];
2086 distance0 = dx * dx + dy * dy;
2091 var touches = d3.touches(that),
2095 d3_selection_interrupt.call(that);
2097 for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
2099 if (l1 = locations0[p1.identifier]) {
2106 var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1,
2107 scale1 = distance0 && Math.sqrt(distance1 / distance0);
2108 p0 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
2109 l0 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
2110 scaleTo(scale1 * scale0);
2114 translateTo(p0, l0);
2119 // If there are any globally-active touches remaining, remove the ended
2120 // touches from locations0.
2121 if (d3.event.touches.length) {
2122 var changed = d3.event.changedTouches;
2123 for (var i = 0, n = changed.length; i < n; ++i) {
2124 delete locations0[changed[i].identifier];
2126 // If locations0 is not empty, then relocate and continue listening for
2127 // touchmove and touchend.
2128 for (var identifier in locations0) {
2129 return void relocate(); // locations may have detached due to rotation
2132 // Otherwise, remove touchmove and touchend listeners.
2133 d3.selectAll(targets).on(zoomName, null);
2134 subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
2136 zoomended(dispatch);
2140 function mousewheeled() {
2141 var dispatch = event.of(this, arguments);
2142 if (mousewheelTimer) clearTimeout(mousewheelTimer);
2143 else translate0 = location(center0 = center || d3.mouse(this)), d3_selection_interrupt.call(this), zoomstarted(dispatch);
2144 mousewheelTimer = setTimeout(function() { mousewheelTimer = null; zoomended(dispatch); }, 50);
2145 d3_eventPreventDefault();
2146 scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
2147 translateTo(center0, translate0);
2151 function dblclicked() {
2152 var p = d3.mouse(this),
2153 k = Math.log(view.k) / Math.LN2;
2155 zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1);
2158 return d3.rebind(zoom, event, "on");
2161 var d3_behavior_zoomInfinity = [0, Infinity], // default scale extent
2162 d3_behavior_zoomDelta, // initialized lazily
2163 d3_behavior_zoomWheel;
2164 function d3_functor(v) {
2165 return typeof v === "function" ? v : function() { return v; };
2168 d3.functor = d3_functor;
2170 d3.touch = function(container, touches, identifier) {
2171 if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
2172 if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
2173 if ((touch = touches[i]).identifier === identifier) {
2174 return d3_mousePoint(container, touch);
2179 var d3_timer_queueHead,
2181 d3_timer_interval, // is an interval (or frame) active?
2182 d3_timer_timeout, // is a timeout active?
2183 d3_timer_active, // active timer object
2184 d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { setTimeout(callback, 17); };
2186 // The timer will continue to fire until callback returns true.
2187 d3.timer = function(callback, delay, then) {
2188 var n = arguments.length;
2189 if (n < 2) delay = 0;
2190 if (n < 3) then = Date.now();
2192 // Add the callback to the tail of the queue.
2193 var time = then + delay, timer = {c: callback, t: time, f: false, n: null};
2194 if (d3_timer_queueTail) d3_timer_queueTail.n = timer;
2195 else d3_timer_queueHead = timer;
2196 d3_timer_queueTail = timer;
2199 if (!d3_timer_interval) {
2200 d3_timer_timeout = clearTimeout(d3_timer_timeout);
2201 d3_timer_interval = 1;
2202 d3_timer_frame(d3_timer_step);
2206 function d3_timer_step() {
2207 var now = d3_timer_mark(),
2208 delay = d3_timer_sweep() - now;
2210 if (isFinite(delay)) {
2211 clearTimeout(d3_timer_timeout);
2212 d3_timer_timeout = setTimeout(d3_timer_step, delay);
2214 d3_timer_interval = 0;
2216 d3_timer_interval = 1;
2217 d3_timer_frame(d3_timer_step);
2221 d3.timer.flush = function() {
2226 function d3_timer_mark() {
2227 var now = Date.now();
2228 d3_timer_active = d3_timer_queueHead;
2229 while (d3_timer_active) {
2230 if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t);
2231 d3_timer_active = d3_timer_active.n;
2236 // Flush after callbacks to avoid concurrent queue modification.
2237 // Returns the time of the earliest active timer, post-sweep.
2238 function d3_timer_sweep() {
2240 t1 = d3_timer_queueHead,
2244 t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
2246 if (t1.t < time) time = t1.t;
2250 d3_timer_queueTail = t0;
2255 d3.geo.stream = function(object, listener) {
2256 if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
2257 d3_geo_streamObjectType[object.type](object, listener);
2259 d3_geo_streamGeometry(object, listener);
2263 function d3_geo_streamGeometry(geometry, listener) {
2264 if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
2265 d3_geo_streamGeometryType[geometry.type](geometry, listener);
2269 var d3_geo_streamObjectType = {
2270 Feature: function(feature, listener) {
2271 d3_geo_streamGeometry(feature.geometry, listener);
2273 FeatureCollection: function(object, listener) {
2274 var features = object.features, i = -1, n = features.length;
2275 while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
2279 var d3_geo_streamGeometryType = {
2280 Sphere: function(object, listener) {
2283 Point: function(object, listener) {
2284 object = object.coordinates;
2285 listener.point(object[0], object[1], object[2]);
2287 MultiPoint: function(object, listener) {
2288 var coordinates = object.coordinates, i = -1, n = coordinates.length;
2289 while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
2291 LineString: function(object, listener) {
2292 d3_geo_streamLine(object.coordinates, listener, 0);
2294 MultiLineString: function(object, listener) {
2295 var coordinates = object.coordinates, i = -1, n = coordinates.length;
2296 while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
2298 Polygon: function(object, listener) {
2299 d3_geo_streamPolygon(object.coordinates, listener);
2301 MultiPolygon: function(object, listener) {
2302 var coordinates = object.coordinates, i = -1, n = coordinates.length;
2303 while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
2305 GeometryCollection: function(object, listener) {
2306 var geometries = object.geometries, i = -1, n = geometries.length;
2307 while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
2311 function d3_geo_streamLine(coordinates, listener, closed) {
2312 var i = -1, n = coordinates.length - closed, coordinate;
2313 listener.lineStart();
2314 while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
2318 function d3_geo_streamPolygon(coordinates, listener) {
2319 var i = -1, n = coordinates.length;
2320 listener.polygonStart();
2321 while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
2322 listener.polygonEnd();
2325 d3.geo.length = function(object) {
2326 d3_geo_lengthSum = 0;
2327 d3.geo.stream(object, d3_geo_length);
2328 return d3_geo_lengthSum;
2331 var d3_geo_lengthSum;
2333 var d3_geo_length = {
2336 lineStart: d3_geo_lengthLineStart,
2338 polygonStart: d3_noop,
2342 function d3_geo_lengthLineStart() {
2343 var λ0, sinφ0, cosφ0;
2345 d3_geo_length.point = function(λ, φ) {
2346 λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ);
2347 d3_geo_length.point = nextPoint;
2350 d3_geo_length.lineEnd = function() {
2351 d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
2354 function nextPoint(λ, φ) {
2355 var sinφ = Math.sin(φ *= d3_radians),
2357 t = abs((λ *= d3_radians) - λ0),
2358 cosΔλ = Math.cos(t);
2359 d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ);
2360 λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ;
2363 function d3_identity(d) {
2366 function d3_true() {
2370 function d3_geo_spherical(cartesian) {
2372 Math.atan2(cartesian[1], cartesian[0]),
2373 d3_asin(cartesian[2])
2377 function d3_geo_sphericalEqual(a, b) {
2378 return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
2381 // General spherical polygon clipping algorithm: takes a polygon, cuts it into
2382 // visible line segments and rejoins the segments by interpolating along the
2384 function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
2388 segments.forEach(function(segment) {
2389 if ((n = segment.length - 1) <= 0) return;
2390 var n, p0 = segment[0], p1 = segment[n];
2392 // If the first and last points of a segment are coincident, then treat as
2394 // TODO if all rings are closed, then the winding order of the exterior
2395 // ring should be checked.
2396 if (d3_geo_sphericalEqual(p0, p1)) {
2397 listener.lineStart();
2398 for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
2403 var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true),
2404 b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
2408 a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
2409 b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
2415 d3_geo_clipPolygonLinkCircular(subject);
2416 d3_geo_clipPolygonLinkCircular(clip);
2417 if (!subject.length) return;
2419 for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
2420 clip[i].e = entry = !entry;
2423 var start = subject[0],
2427 // Find first unvisited intersection.
2428 var current = start,
2430 while (current.v) if ((current = current.n) === start) return;
2432 listener.lineStart();
2434 current.v = current.o.v = true;
2437 for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
2439 interpolate(current.x, current.n.x, 1, listener);
2441 current = current.n;
2444 points = current.p.z;
2445 for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
2447 interpolate(current.x, current.p.x, -1, listener);
2449 current = current.p;
2451 current = current.o;
2453 isSubject = !isSubject;
2454 } while (!current.v);
2459 function d3_geo_clipPolygonLinkCircular(array) {
2460 if (!(n = array.length)) return;
2474 function d3_geo_clipPolygonIntersection(point, points, other, entry) {
2477 this.o = other; // another intersection
2478 this.e = entry; // is an entry?
2479 this.v = false; // visited
2480 this.n = this.p = null; // next & previous
2483 function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
2484 return function(rotate, listener) {
2485 var line = clipLine(listener),
2486 rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
2490 lineStart: lineStart,
2492 polygonStart: function() {
2493 clip.point = pointRing;
2494 clip.lineStart = ringStart;
2495 clip.lineEnd = ringEnd;
2499 polygonEnd: function() {
2501 clip.lineStart = lineStart;
2502 clip.lineEnd = lineEnd;
2504 segments = d3.merge(segments);
2505 var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
2506 if (segments.length) {
2507 if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2508 d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
2509 } else if (clipStartInside) {
2510 if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2511 listener.lineStart();
2512 interpolate(null, null, 1, listener);
2515 if (polygonStarted) listener.polygonEnd(), polygonStarted = false;
2516 segments = polygon = null;
2518 sphere: function() {
2519 listener.polygonStart();
2520 listener.lineStart();
2521 interpolate(null, null, 1, listener);
2523 listener.polygonEnd();
2527 function point(λ, φ) {
2528 var point = rotate(λ, φ);
2529 if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
2531 function pointLine(λ, φ) {
2532 var point = rotate(λ, φ);
2533 line.point(point[0], point[1]);
2535 function lineStart() { clip.point = pointLine; line.lineStart(); }
2536 function lineEnd() { clip.point = point; line.lineEnd(); }
2540 var buffer = d3_geo_clipBufferListener(),
2541 ringListener = clipLine(buffer),
2542 polygonStarted = false,
2546 function pointRing(λ, φ) {
2548 var point = rotate(λ, φ);
2549 ringListener.point(point[0], point[1]);
2552 function ringStart() {
2553 ringListener.lineStart();
2557 function ringEnd() {
2558 pointRing(ring[0][0], ring[0][1]);
2559 ringListener.lineEnd();
2561 var clean = ringListener.clean(),
2562 ringSegments = buffer.buffer(),
2564 n = ringSegments.length;
2572 // No intersections.
2574 segment = ringSegments[0];
2575 var n = segment.length - 1,
2579 if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2580 listener.lineStart();
2581 while (++i < n) listener.point((point = segment[i])[0], point[1]);
2587 // Rejoin connected segments.
2588 // TODO reuse bufferListener.rejoin()?
2589 if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
2591 segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
2598 function d3_geo_clipSegmentLength1(segment) {
2599 return segment.length > 1;
2602 function d3_geo_clipBufferListener() {
2606 lineStart: function() { lines.push(line = []); },
2607 point: function(λ, φ) { line.push([λ, φ]); },
2609 buffer: function() {
2615 rejoin: function() {
2616 if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
2621 // Intersection points are sorted along the clip edge. For both antimeridian
2622 // cutting and circle clipping, the same comparison is used.
2623 function d3_geo_clipSort(a, b) {
2624 return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1])
2625 - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]);
2628 var d3_geo_clipAntimeridian = d3_geo_clip(
2630 d3_geo_clipAntimeridianLine,
2631 d3_geo_clipAntimeridianInterpolate,
2634 // Takes a line and cuts into visible segments. Return values:
2635 // 0: there were intersections or the line was empty.
2636 // 1: no intersections.
2637 // 2: there were intersections, and the first and last segments should be
2639 function d3_geo_clipAntimeridianLine(listener) {
2643 clean; // no intersections
2646 lineStart: function() {
2647 listener.lineStart();
2650 point: function(λ1, φ1) {
2651 var sλ1 = λ1 > 0 ? π : -π,
2653 if (abs(dλ - π) < ε) { // line crosses a pole
2654 listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
2655 listener.point(sλ0, φ0);
2657 listener.lineStart();
2658 listener.point(sλ1, φ0);
2659 listener.point(λ1, φ0);
2661 } else if (sλ0 !== sλ1 && dλ >= π) { // line crosses antimeridian
2662 // handle degeneracies
2663 if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
2664 if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
2665 φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
2666 listener.point(sλ0, φ0);
2668 listener.lineStart();
2669 listener.point(sλ1, φ0);
2672 listener.point(λ0 = λ1, φ0 = φ1);
2675 lineEnd: function() {
2679 // if there are intersections, we always rejoin the first and last segments.
2680 clean: function() { return 2 - clean; }
2684 function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
2687 sinλ0_λ1 = Math.sin(λ0 - λ1);
2688 return abs(sinλ0_λ1) > ε
2689 ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1)
2690 - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0))
2691 / (cosφ0 * cosφ1 * sinλ0_λ1))
2695 function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
2698 φ = direction * halfπ;
2699 listener.point(-π, φ);
2700 listener.point( 0, φ);
2701 listener.point( π, φ);
2702 listener.point( π, 0);
2703 listener.point( π, -φ);
2704 listener.point( 0, -φ);
2705 listener.point(-π, -φ);
2706 listener.point(-π, 0);
2707 listener.point(-π, φ);
2708 } else if (abs(from[0] - to[0]) > ε) {
2709 var s = from[0] < to[0] ? π : -π;
2710 φ = direction * s / 2;
2711 listener.point(-s, φ);
2712 listener.point( 0, φ);
2713 listener.point( s, φ);
2715 listener.point(to[0], to[1]);
2719 // cross and scale return new vectors,
2720 // whereas add and normalize operate in-place
2722 function d3_geo_cartesian(spherical) {
2723 var λ = spherical[0],
2733 function d3_geo_cartesianDot(a, b) {
2734 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
2737 function d3_geo_cartesianCross(a, b) {
2739 a[1] * b[2] - a[2] * b[1],
2740 a[2] * b[0] - a[0] * b[2],
2741 a[0] * b[1] - a[1] * b[0]
2745 function d3_geo_cartesianAdd(a, b) {
2751 function d3_geo_cartesianScale(vector, k) {
2759 function d3_geo_cartesianNormalize(d) {
2760 var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
2765 function d3_geo_compose(a, b) {
2767 function compose(x, y) {
2768 return x = a(x, y), b(x[0], x[1]);
2771 if (a.invert && b.invert) compose.invert = function(x, y) {
2772 return x = b.invert(x, y), x && a.invert(x[0], x[1]);
2778 function d3_geo_equirectangular(λ, φ) {
2782 (d3.geo.equirectangular = function() {
2783 return d3_geo_projection(d3_geo_equirectangular);
2784 }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
2786 d3.geo.rotation = function(rotate) {
2787 rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
2789 function forward(coordinates) {
2790 coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
2791 return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
2794 forward.invert = function(coordinates) {
2795 coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
2796 return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
2802 function d3_geo_identityRotation(λ, φ) {
2803 return [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ];
2806 d3_geo_identityRotation.invert = d3_geo_equirectangular;
2808 // Note: |δλ| must be < 2π
2809 function d3_geo_rotation(δλ, δφ, δγ) {
2810 return δλ ? (δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ))
2811 : d3_geo_rotationλ(δλ))
2812 : (δφ || δγ ? d3_geo_rotationφγ(δφ, δγ)
2813 : d3_geo_identityRotation);
2816 function d3_geo_forwardRotationλ(δλ) {
2817 return function(λ, φ) {
2818 return λ += δλ, [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ];
2822 function d3_geo_rotationλ(δλ) {
2823 var rotation = d3_geo_forwardRotationλ(δλ);
2824 rotation.invert = d3_geo_forwardRotationλ(-δλ);
2828 function d3_geo_rotationφγ(δφ, δγ) {
2829 var cosδφ = Math.cos(δφ),
2830 sinδφ = Math.sin(δφ),
2831 cosδγ = Math.cos(δγ),
2832 sinδγ = Math.sin(δγ);
2834 function rotation(λ, φ) {
2835 var cosφ = Math.cos(φ),
2836 x = Math.cos(λ) * cosφ,
2837 y = Math.sin(λ) * cosφ,
2839 k = z * cosδφ + x * sinδφ;
2841 Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ),
2842 d3_asin(k * cosδγ + y * sinδγ)
2846 rotation.invert = function(λ, φ) {
2847 var cosφ = Math.cos(φ),
2848 x = Math.cos(λ) * cosφ,
2849 y = Math.sin(λ) * cosφ,
2851 k = z * cosδγ - y * sinδγ;
2853 Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ),
2854 d3_asin(k * cosδφ - x * sinδφ)
2861 d3.geo.circle = function() {
2862 var origin = [0, 0],
2868 var center = typeof origin === "function" ? origin.apply(this, arguments) : origin,
2869 rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert,
2872 interpolate(null, null, 1, {
2873 point: function(x, y) {
2874 ring.push(x = rotate(x, y));
2875 x[0] *= d3_degrees, x[1] *= d3_degrees;
2879 return {type: "Polygon", coordinates: [ring]};
2882 circle.origin = function(x) {
2883 if (!arguments.length) return origin;
2888 circle.angle = function(x) {
2889 if (!arguments.length) return angle;
2890 interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
2894 circle.precision = function(_) {
2895 if (!arguments.length) return precision;
2896 interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
2900 return circle.angle(90);
2903 // Interpolates along a circle centered at [0°, 0°], with a given radius and
2905 function d3_geo_circleInterpolate(radius, precision) {
2906 var cr = Math.cos(radius),
2907 sr = Math.sin(radius);
2908 return function(from, to, direction, listener) {
2909 var step = direction * precision;
2911 from = d3_geo_circleAngle(cr, from);
2912 to = d3_geo_circleAngle(cr, to);
2913 if (direction > 0 ? from < to: from > to) from += direction * τ;
2915 from = radius + direction * τ;
2916 to = radius - .5 * step;
2918 for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
2919 listener.point((point = d3_geo_spherical([
2928 // Signed angle of a cartesian point relative to [cr, 0, 0].
2929 function d3_geo_circleAngle(cr, point) {
2930 var a = d3_geo_cartesian(point);
2932 d3_geo_cartesianNormalize(a);
2933 var angle = d3_acos(-a[1]);
2934 return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
2936 // Adds floating point numbers with twice the normal precision.
2937 // Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and
2938 // Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)
2940 // Code adapted from GeographicLib by Charles F. F. Karney,
2941 // http://geographiclib.sourceforge.net/
2942 // See lib/geographiclib/LICENSE for details.
2944 function d3_adder() {}
2946 d3_adder.prototype = {
2947 s: 0, // rounded value
2948 t: 0, // exact error
2950 d3_adderSum(y, this.t, d3_adderTemp);
2951 d3_adderSum(d3_adderTemp.s, this.s, this);
2952 if (this.s) this.t += d3_adderTemp.t;
2953 else this.s = d3_adderTemp.t;
2956 this.s = this.t = 0;
2958 valueOf: function() {
2963 var d3_adderTemp = new d3_adder;
2965 function d3_adderSum(a, b, o) {
2966 var x = o.s = a + b, // a + b
2967 bv = x - a, av = x - bv; // b_virtual & a_virtual
2968 o.t = (a - av) + (b - bv); // a_roundoff + b_roundoff
2971 d3.geo.area = function(object) {
2973 d3.geo.stream(object, d3_geo_area);
2974 return d3_geo_areaSum;
2978 d3_geo_areaRingSum = new d3_adder;
2981 sphere: function() { d3_geo_areaSum += 4 * π; },
2986 // Only count area for polygon rings.
2987 polygonStart: function() {
2988 d3_geo_areaRingSum.reset();
2989 d3_geo_area.lineStart = d3_geo_areaRingStart;
2991 polygonEnd: function() {
2992 var area = 2 * d3_geo_areaRingSum;
2993 d3_geo_areaSum += area < 0 ? 4 * π + area : area;
2994 d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
2998 function d3_geo_areaRingStart() {
2999 var λ00, φ00, λ0, cosφ0, sinφ0; // start point and previous point
3001 // For the first point, …
3002 d3_geo_area.point = function(λ, φ) {
3003 d3_geo_area.point = nextPoint;
3004 λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), sinφ0 = Math.sin(φ);
3007 // For subsequent points, …
3008 function nextPoint(λ, φ) {
3010 φ = φ * d3_radians / 2 + π / 4; // half the angular distance from south pole
3012 // Spherical excess E for a spherical triangle with vertices: south pole,
3013 // previous point, current point. Uses a formula derived from Cagnoli’s
3014 // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
3016 sdλ = dλ >= 0 ? 1 : -1,
3021 u = cosφ0 * cosφ + k * Math.cos(adλ),
3022 v = k * sdλ * Math.sin(adλ);
3023 d3_geo_areaRingSum.add(Math.atan2(v, u));
3025 // Advance the previous points.
3026 λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
3029 // For the last point, return to the start.
3030 d3_geo_area.lineEnd = function() {
3031 nextPoint(λ00, φ00);
3035 function d3_geo_pointInPolygon(point, polygon) {
3036 var meridian = point[0],
3037 parallel = point[1],
3038 meridianNormal = [Math.sin(meridian), -Math.cos(meridian), 0],
3041 d3_geo_areaRingSum.reset();
3043 for (var i = 0, n = polygon.length; i < n; ++i) {
3044 var ring = polygon[i],
3047 var point0 = ring[0],
3049 φ0 = point0[1] / 2 + π / 4,
3050 sinφ0 = Math.sin(φ0),
3051 cosφ0 = Math.cos(φ0),
3058 φ = point[1] / 2 + π / 4,
3062 sdλ = dλ >= 0 ? 1 : -1,
3064 antimeridian = adλ > π,
3066 d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
3068 polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
3070 // Are the longitudes either side of the point's meridian, and are the
3071 // latitudes smaller than the parallel?
3072 if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
3073 var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
3074 d3_geo_cartesianNormalize(arc);
3075 var intersection = d3_geo_cartesianCross(meridianNormal, arc);
3076 d3_geo_cartesianNormalize(intersection);
3077 var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
3078 if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
3079 winding += antimeridian ^ dλ >= 0 ? 1 : -1;
3083 λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
3087 // First, determine whether the South pole is inside or outside:
3090 // * the polygon winds around it in a clockwise direction.
3091 // * the polygon does not (cumulatively) wind around it, but has a negative
3092 // (counter-clockwise) area.
3094 // Second, count the (signed) number of times a segment crosses a meridian
3095 // from the point to the South pole. If it is zero, then the point is the
3096 // same side as the South pole.
3098 return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ (winding & 1);
3101 // Clip features against a small circle centered at [0°, 0°].
3102 function d3_geo_clipCircle(radius) {
3103 var cr = Math.cos(radius),
3104 smallRadius = cr > 0,
3105 notHemisphere = abs(cr) > ε, // TODO optimise for this common case
3106 interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
3108 return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-π, radius - π]);
3110 function visible(λ, φ) {
3111 return Math.cos(λ) * Math.cos(φ) > cr;
3114 // Takes a line and cuts into visible segments. Return values used for
3115 // polygon clipping:
3116 // 0: there were intersections or the line was empty.
3117 // 1: no intersections.
3118 // 2: there were intersections, and the first and last segments should be
3120 function clipLine(listener) {
3121 var point0, // previous point
3122 c0, // code for previous point
3123 v0, // visibility of previous point
3124 v00, // visibility of first point
3125 clean; // no intersections
3127 lineStart: function() {
3131 point: function(λ, φ) {
3132 var point1 = [λ, φ],
3136 ? v ? 0 : code(λ, φ)
3137 : v ? code(λ + (λ < 0 ? π : -π), φ) : 0;
3138 if (!point0 && (v00 = v0 = v)) listener.lineStart();
3139 // Handle degeneracies.
3140 // TODO ignore if not clipping polygons.
3142 point2 = intersect(point0, point1);
3143 if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
3146 v = visible(point1[0], point1[1]);
3153 listener.lineStart();
3154 point2 = intersect(point1, point0);
3155 listener.point(point2[0], point2[1]);
3158 point2 = intersect(point0, point1);
3159 listener.point(point2[0], point2[1]);
3163 } else if (notHemisphere && point0 && smallRadius ^ v) {
3165 // If the codes for two points are different, or are both zero,
3166 // and there this segment intersects with the small circle.
3167 if (!(c & c0) && (t = intersect(point1, point0, true))) {
3170 listener.lineStart();
3171 listener.point(t[0][0], t[0][1]);
3172 listener.point(t[1][0], t[1][1]);
3175 listener.point(t[1][0], t[1][1]);
3177 listener.lineStart();
3178 listener.point(t[0][0], t[0][1]);
3182 if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
3183 listener.point(point1[0], point1[1]);
3185 point0 = point1, v0 = v, c0 = c;
3187 lineEnd: function() {
3188 if (v0) listener.lineEnd();
3191 // Rejoin first and last segments if there were intersections and the first
3192 // and last points were visible.
3193 clean: function() { return clean | ((v00 && v0) << 1); }
3197 // Intersects the great circle between a and b with the clip circle.
3198 function intersect(a, b, two) {
3199 var pa = d3_geo_cartesian(a),
3200 pb = d3_geo_cartesian(b);
3202 // We have two planes, n1.p = d1 and n2.p = d2.
3203 // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
3204 var n1 = [1, 0, 0], // normal
3205 n2 = d3_geo_cartesianCross(pa, pb),
3206 n2n2 = d3_geo_cartesianDot(n2, n2),
3207 n1n2 = n2[0], // d3_geo_cartesianDot(n1, n2),
3208 determinant = n2n2 - n1n2 * n1n2;
3210 // Two polar points.
3211 if (!determinant) return !two && a;
3213 var c1 = cr * n2n2 / determinant,
3214 c2 = -cr * n1n2 / determinant,
3215 n1xn2 = d3_geo_cartesianCross(n1, n2),
3216 A = d3_geo_cartesianScale(n1, c1),
3217 B = d3_geo_cartesianScale(n2, c2);
3218 d3_geo_cartesianAdd(A, B);
3220 // Solve |p(t)|^2 = 1.
3222 w = d3_geo_cartesianDot(A, u),
3223 uu = d3_geo_cartesianDot(u, u),
3224 t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1);
3228 var t = Math.sqrt(t2),
3229 q = d3_geo_cartesianScale(u, (-w - t) / uu);
3230 d3_geo_cartesianAdd(q, A);
3231 q = d3_geo_spherical(q);
3234 // Two intersection points.
3240 if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
3242 polar = abs(δλ - π) < ε,
3243 meridian = polar || δλ < ε;
3245 if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
3247 // Check that the first point is between a and b.
3250 ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1)
3251 : φ0 <= q[1] && q[1] <= φ1
3252 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) {
3253 var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
3254 d3_geo_cartesianAdd(q1, A);
3255 return [q, d3_geo_spherical(q1)];
3259 // Generates a 4-bit vector representing the location of a point relative to
3260 // the small circle's bounding box.
3261 function code(λ, φ) {
3262 var r = smallRadius ? radius : π - radius,
3264 if (λ < -r) code |= 1; // left
3265 else if (λ > r) code |= 2; // right
3266 if (φ < -r) code |= 4; // below
3267 else if (φ > r) code |= 8; // above
3272 // Liang–Barsky line clipping.
3273 function d3_geom_clipLine(x0, y0, x1, y1) {
3274 return function(line) {
3288 if (!dx && r > 0) return;
3293 } else if (dx > 0) {
3299 if (!dx && r < 0) return;
3304 } else if (dx > 0) {
3310 if (!dy && r > 0) return;
3315 } else if (dy > 0) {
3321 if (!dy && r < 0) return;
3326 } else if (dy > 0) {
3331 if (t0 > 0) line.a = {x: ax + t0 * dx, y: ay + t0 * dy};
3332 if (t1 < 1) line.b = {x: ax + t1 * dx, y: ay + t1 * dy};
3337 var d3_geo_clipExtentMAX = 1e9;
3339 d3.geo.clipExtent = function() {
3344 stream: function(output) {
3345 if (stream) stream.valid = false;
3346 stream = clip(output);
3347 stream.valid = true; // allow caching by d3.geo.path
3350 extent: function(_) {
3351 if (!arguments.length) return [[x0, y0], [x1, y1]];
3352 clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
3353 if (stream) stream.valid = false, stream = null;
3357 return clipExtent.extent([[0, 0], [960, 500]]);
3360 function d3_geo_clipExtent(x0, y0, x1, y1) {
3361 return function(listener) {
3362 var listener_ = listener,
3363 bufferListener = d3_geo_clipBufferListener(),
3364 clipLine = d3_geom_clipLine(x0, y0, x1, y1),
3371 lineStart: lineStart,
3373 polygonStart: function() {
3374 listener = bufferListener;
3379 polygonEnd: function() {
3380 listener = listener_;
3381 segments = d3.merge(segments);
3382 var clipStartInside = insidePolygon([x0, y1]),
3383 inside = clean && clipStartInside,
3384 visible = segments.length;
3385 if (inside || visible) {
3386 listener.polygonStart();
3388 listener.lineStart();
3389 interpolate(null, null, 1, listener);
3393 d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
3395 listener.polygonEnd();
3397 segments = polygon = ring = null;
3401 function insidePolygon(p) {
3402 var wn = 0, // the winding number counter
3406 for (var i = 0; i < n; ++i) {
3407 for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
3410 if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn;
3412 if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn;
3420 function interpolate(from, to, direction, listener) {
3423 (a = corner(from, direction)) !== (a1 = corner(to, direction)) ||
3424 comparePoints(from, to) < 0 ^ direction > 0) {
3426 listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
3427 } while ((a = (a + direction + 4) % 4) !== a1);
3429 listener.point(to[0], to[1]);
3433 function pointVisible(x, y) {
3434 return x0 <= x && x <= x1 && y0 <= y && y <= y1;
3437 function point(x, y) {
3438 if (pointVisible(x, y)) listener.point(x, y);
3441 var x__, y__, v__, // first point
3442 x_, y_, v_, // previous point
3446 function lineStart() {
3447 clip.point = linePoint;
3448 if (polygon) polygon.push(ring = []);
3454 function lineEnd() {
3455 // TODO rather than special-case polygons, simply handle them separately.
3456 // Ideally, coincident intersection points should be jittered to avoid
3459 linePoint(x__, y__);
3460 if (v__ && v_) bufferListener.rejoin();
3461 segments.push(bufferListener.buffer());
3464 if (v_) listener.lineEnd();
3467 function linePoint(x, y) {
3468 x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
3469 y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
3470 var v = pointVisible(x, y);
3471 if (polygon) ring.push([x, y]);
3473 x__ = x, y__ = y, v__ = v;
3476 listener.lineStart();
3477 listener.point(x, y);
3480 if (v && v_) listener.point(x, y);
3482 var l = {a: {x: x_, y: y_}, b: {x: x, y: y}};
3485 listener.lineStart();
3486 listener.point(l.a.x, l.a.y);
3488 listener.point(l.b.x, l.b.y);
3489 if (!v) listener.lineEnd();
3492 listener.lineStart();
3493 listener.point(x, y);
3498 x_ = x, y_ = y, v_ = v;
3504 function corner(p, direction) {
3505 return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3
3506 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1
3507 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0
3508 : direction > 0 ? 3 : 2; // abs(p[1] - y1) < ε
3511 function compare(a, b) {
3512 return comparePoints(a.x, b.x);
3515 function comparePoints(a, b) {
3516 var ca = corner(a, 1),
3518 return ca !== cb ? ca - cb
3519 : ca === 0 ? b[1] - a[1]
3520 : ca === 1 ? a[0] - b[0]
3521 : ca === 2 ? a[1] - b[1]
3526 function d3_geo_conic(projectAt) {
3529 m = d3_geo_projectionMutator(projectAt),
3532 p.parallels = function(_) {
3533 if (!arguments.length) return [φ0 / π * 180, φ1 / π * 180];
3534 return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180);
3540 function d3_geo_conicEqualArea(φ0, φ1) {
3541 var sinφ0 = Math.sin(φ0),
3542 n = (sinφ0 + Math.sin(φ1)) / 2,
3543 C = 1 + sinφ0 * (2 * n - sinφ0),
3544 ρ0 = Math.sqrt(C) / n;
3546 function forward(λ, φ) {
3547 var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n;
3549 ρ * Math.sin(λ *= n),
3550 ρ0 - ρ * Math.cos(λ)
3554 forward.invert = function(x, y) {
3557 Math.atan2(x, ρ0_y) / n,
3558 d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n))
3565 (d3.geo.conicEqualArea = function() {
3566 return d3_geo_conic(d3_geo_conicEqualArea);
3567 }).raw = d3_geo_conicEqualArea;
3570 d3.geo.albers = function() {
3571 return d3.geo.conicEqualArea()
3573 .center([-.6, 38.7])
3574 .parallels([29.5, 45.5])
3578 // A composite projection for the United States, configured by default for
3579 // 960×500. Also works quite well at 960×600 with scale 1285. The set of
3580 // standard parallels for each region comes from USGS, which is published here:
3581 // http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers
3582 d3.geo.albersUsa = function() {
3583 var lower48 = d3.geo.albers();
3586 var alaska = d3.geo.conicEqualArea()
3589 .parallels([55, 65]);
3592 var hawaii = d3.geo.conicEqualArea()
3595 .parallels([8, 18]);
3598 pointStream = {point: function(x, y) { point = [x, y]; }},
3603 function albersUsa(coordinates) {
3604 var x = coordinates[0], y = coordinates[1];
3606 (lower48Point(x, y), point)
3607 || (alaskaPoint(x, y), point)
3608 || hawaiiPoint(x, y);
3612 albersUsa.invert = function(coordinates) {
3613 var k = lower48.scale(),
3614 t = lower48.translate(),
3615 x = (coordinates[0] - t[0]) / k,
3616 y = (coordinates[1] - t[1]) / k;
3617 return (y >= .120 && y < .234 && x >= -.425 && x < -.214 ? alaska
3618 : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii
3619 : lower48).invert(coordinates);
3622 // A naïve multi-projection stream.
3623 // The projections must have mutually exclusive clip regions on the sphere,
3624 // as this will avoid emitting interleaving lines and polygons.
3625 albersUsa.stream = function(stream) {
3626 var lower48Stream = lower48.stream(stream),
3627 alaskaStream = alaska.stream(stream),
3628 hawaiiStream = hawaii.stream(stream);
3630 point: function(x, y) {
3631 lower48Stream.point(x, y);
3632 alaskaStream.point(x, y);
3633 hawaiiStream.point(x, y);
3635 sphere: function() {
3636 lower48Stream.sphere();
3637 alaskaStream.sphere();
3638 hawaiiStream.sphere();
3640 lineStart: function() {
3641 lower48Stream.lineStart();
3642 alaskaStream.lineStart();
3643 hawaiiStream.lineStart();
3645 lineEnd: function() {
3646 lower48Stream.lineEnd();
3647 alaskaStream.lineEnd();
3648 hawaiiStream.lineEnd();
3650 polygonStart: function() {
3651 lower48Stream.polygonStart();
3652 alaskaStream.polygonStart();
3653 hawaiiStream.polygonStart();
3655 polygonEnd: function() {
3656 lower48Stream.polygonEnd();
3657 alaskaStream.polygonEnd();
3658 hawaiiStream.polygonEnd();
3663 albersUsa.precision = function(_) {
3664 if (!arguments.length) return lower48.precision();
3665 lower48.precision(_);
3666 alaska.precision(_);
3667 hawaii.precision(_);
3671 albersUsa.scale = function(_) {
3672 if (!arguments.length) return lower48.scale();
3674 alaska.scale(_ * .35);
3676 return albersUsa.translate(lower48.translate());
3679 albersUsa.translate = function(_) {
3680 if (!arguments.length) return lower48.translate();
3681 var k = lower48.scale(), x = +_[0], y = +_[1];
3683 lower48Point = lower48
3685 .clipExtent([[x - .455 * k, y - .238 * k], [x + .455 * k, y + .238 * k]])
3686 .stream(pointStream).point;
3688 alaskaPoint = alaska
3689 .translate([x - .307 * k, y + .201 * k])
3690 .clipExtent([[x - .425 * k + ε, y + .120 * k + ε], [x - .214 * k - ε, y + .234 * k - ε]])
3691 .stream(pointStream).point;
3693 hawaiiPoint = hawaii
3694 .translate([x - .205 * k, y + .212 * k])
3695 .clipExtent([[x - .214 * k + ε, y + .166 * k + ε], [x - .115 * k - ε, y + .234 * k - ε]])
3696 .stream(pointStream).point;
3701 return albersUsa.scale(1070);
3704 d3.geo.bounds = (function() {
3705 var λ0, φ0, λ1, φ1, // bounds
3706 λ_, // previous λ-coordinate
3707 λ__, φ__, // first point
3708 p0, // previous 3D point
3715 lineStart: lineStart,
3718 polygonStart: function() {
3719 bound.point = ringPoint;
3720 bound.lineStart = ringStart;
3721 bound.lineEnd = ringEnd;
3723 d3_geo_area.polygonStart();
3725 polygonEnd: function() {
3726 d3_geo_area.polygonEnd();
3727 bound.point = point;
3728 bound.lineStart = lineStart;
3729 bound.lineEnd = lineEnd;
3730 if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90);
3731 else if (dλSum > ε) φ1 = 90;
3732 else if (dλSum < -ε) φ0 = -90;
3733 range[0] = λ0, range[1] = λ1;
3737 function point(λ, φ) {
3738 ranges.push(range = [λ0 = λ, λ1 = λ]);
3743 function linePoint(λ, φ) {
3744 var p = d3_geo_cartesian([λ * d3_radians, φ * d3_radians]);
3746 var normal = d3_geo_cartesianCross(p0, p),
3747 equatorial = [normal[1], -normal[0], 0],
3748 inflection = d3_geo_cartesianCross(equatorial, normal);
3749 d3_geo_cartesianNormalize(inflection);
3750 inflection = d3_geo_spherical(inflection);
3752 s = dλ > 0 ? 1 : -1,
3753 λi = inflection[0] * d3_degrees * s,
3754 antimeridian = abs(dλ) > 180;
3755 if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
3756 var φi = inflection[1] * d3_degrees;
3757 if (φi > φ1) φ1 = φi;
3758 } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
3759 var φi = -inflection[1] * d3_degrees;
3760 if (φi < φ0) φ0 = φi;
3767 if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3769 if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3777 if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3779 if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3789 function lineStart() { bound.point = linePoint; }
3790 function lineEnd() {
3791 range[0] = λ0, range[1] = λ1;
3792 bound.point = point;
3796 function ringPoint(λ, φ) {
3799 dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
3800 } else λ__ = λ, φ__ = φ;
3801 d3_geo_area.point(λ, φ);
3805 function ringStart() {
3806 d3_geo_area.lineStart();
3809 function ringEnd() {
3810 ringPoint(λ__, φ__);
3811 d3_geo_area.lineEnd();
3812 if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
3813 range[0] = λ0, range[1] = λ1;
3817 // Finds the left-right distance between two longitudes.
3818 // This is almost the same as (λ1 - λ0 + 360°) % 360°, except that we want
3819 // the distance between ±180° to be 360°.
3820 function angle(λ0, λ1) { return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; }
3822 function compareRanges(a, b) { return a[0] - b[0]; }
3824 function withinRange(x, range) {
3825 return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
3828 return function(feature) {
3829 φ1 = λ1 = -(λ0 = φ0 = Infinity);
3832 d3.geo.stream(feature, bound);
3834 var n = ranges.length;
3836 // First, sort ranges by their minimum longitudes.
3837 ranges.sort(compareRanges);
3839 // Then, merge any ranges that overlap.
3840 for (var i = 1, a = ranges[0], b, merged = [a]; i < n; ++i) {
3842 if (withinRange(b[0], a) || withinRange(b[1], a)) {
3843 if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
3844 if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
3850 // Finally, find the largest gap between the merged ranges.
3851 // The final bounding box will be the inverse of this gap.
3852 var best = -Infinity, dλ;
3853 for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
3855 if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
3858 ranges = range = null;
3860 return λ0 === Infinity || φ0 === Infinity
3861 ? [[NaN, NaN], [NaN, NaN]]
3862 : [[λ0, φ0], [λ1, φ1]];
3866 d3.geo.centroid = function(object) {
3867 d3_geo_centroidW0 = d3_geo_centroidW1 =
3868 d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 =
3869 d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 =
3870 d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
3871 d3.geo.stream(object, d3_geo_centroid);
3873 var x = d3_geo_centroidX2,
3874 y = d3_geo_centroidY2,
3875 z = d3_geo_centroidZ2,
3876 m = x * x + y * y + z * z;
3878 // If the area-weighted centroid is undefined, fall back to length-weighted centroid.
3880 x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
3881 // If the feature has zero length, fall back to arithmetic mean of point vectors.
3882 if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
3883 m = x * x + y * y + z * z;
3884 // If the feature still has an undefined centroid, then return.
3885 if (m < ε2) return [NaN, NaN];
3888 return [Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees];
3891 var d3_geo_centroidW0,
3903 var d3_geo_centroid = {
3905 point: d3_geo_centroidPoint,
3906 lineStart: d3_geo_centroidLineStart,
3907 lineEnd: d3_geo_centroidLineEnd,
3908 polygonStart: function() {
3909 d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
3911 polygonEnd: function() {
3912 d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
3916 // Arithmetic mean of Cartesian vectors.
3917 function d3_geo_centroidPoint(λ, φ) {
3919 var cosφ = Math.cos(φ *= d3_radians);
3920 d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
3923 function d3_geo_centroidPointXYZ(x, y, z) {
3924 ++d3_geo_centroidW0;
3925 d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
3926 d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
3927 d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
3930 function d3_geo_centroidLineStart() {
3931 var x0, y0, z0; // previous point
3933 d3_geo_centroid.point = function(λ, φ) {
3935 var cosφ = Math.cos(φ *= d3_radians);
3936 x0 = cosφ * Math.cos(λ);
3937 y0 = cosφ * Math.sin(λ);
3939 d3_geo_centroid.point = nextPoint;
3940 d3_geo_centroidPointXYZ(x0, y0, z0);
3943 function nextPoint(λ, φ) {
3945 var cosφ = Math.cos(φ *= d3_radians),
3946 x = cosφ * Math.cos(λ),
3947 y = cosφ * Math.sin(λ),
3950 Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w),
3951 x0 * x + y0 * y + z0 * z);
3952 d3_geo_centroidW1 += w;
3953 d3_geo_centroidX1 += w * (x0 + (x0 = x));
3954 d3_geo_centroidY1 += w * (y0 + (y0 = y));
3955 d3_geo_centroidZ1 += w * (z0 + (z0 = z));
3956 d3_geo_centroidPointXYZ(x0, y0, z0);
3960 function d3_geo_centroidLineEnd() {
3961 d3_geo_centroid.point = d3_geo_centroidPoint;
3964 // See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
3965 // J. Applied Mechanics 42, 239 (1975).
3966 function d3_geo_centroidRingStart() {
3967 var λ00, φ00, // first point
3968 x0, y0, z0; // previous point
3970 d3_geo_centroid.point = function(λ, φ) {
3972 d3_geo_centroid.point = nextPoint;
3974 var cosφ = Math.cos(φ *= d3_radians);
3975 x0 = cosφ * Math.cos(λ);
3976 y0 = cosφ * Math.sin(λ);
3978 d3_geo_centroidPointXYZ(x0, y0, z0);
3981 d3_geo_centroid.lineEnd = function() {
3982 nextPoint(λ00, φ00);
3983 d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
3984 d3_geo_centroid.point = d3_geo_centroidPoint;
3987 function nextPoint(λ, φ) {
3989 var cosφ = Math.cos(φ *= d3_radians),
3990 x = cosφ * Math.cos(λ),
3991 y = cosφ * Math.sin(λ),
3993 cx = y0 * z - z0 * y,
3994 cy = z0 * x - x0 * z,
3995 cz = x0 * y - y0 * x,
3996 m = Math.sqrt(cx * cx + cy * cy + cz * cz),
3997 u = x0 * x + y0 * y + z0 * z,
3998 v = m && -d3_acos(u) / m, // area weight
3999 w = Math.atan2(m, u); // line weight
4000 d3_geo_centroidX2 += v * cx;
4001 d3_geo_centroidY2 += v * cy;
4002 d3_geo_centroidZ2 += v * cz;
4003 d3_geo_centroidW1 += w;
4004 d3_geo_centroidX1 += w * (x0 + (x0 = x));
4005 d3_geo_centroidY1 += w * (y0 + (y0 = y));
4006 d3_geo_centroidZ1 += w * (z0 + (z0 = z));
4007 d3_geo_centroidPointXYZ(x0, y0, z0);
4011 // TODO Unify this code with d3.geom.polygon area?
4013 var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
4018 // Only count area for polygon rings.
4019 polygonStart: function() {
4020 d3_geo_pathAreaPolygon = 0;
4021 d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
4023 polygonEnd: function() {
4024 d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
4025 d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
4029 function d3_geo_pathAreaRingStart() {
4030 var x00, y00, x0, y0;
4032 // For the first point, …
4033 d3_geo_pathArea.point = function(x, y) {
4034 d3_geo_pathArea.point = nextPoint;
4035 x00 = x0 = x, y00 = y0 = y;
4038 // For subsequent points, …
4039 function nextPoint(x, y) {
4040 d3_geo_pathAreaPolygon += y0 * x - x0 * y;
4044 // For the last point, return to the start.
4045 d3_geo_pathArea.lineEnd = function() {
4046 nextPoint(x00, y00);
4050 var d3_geo_pathBoundsX0,
4051 d3_geo_pathBoundsY0,
4052 d3_geo_pathBoundsX1,
4053 d3_geo_pathBoundsY1;
4055 var d3_geo_pathBounds = {
4056 point: d3_geo_pathBoundsPoint,
4059 polygonStart: d3_noop,
4063 function d3_geo_pathBoundsPoint(x, y) {
4064 if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
4065 if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
4066 if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
4067 if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
4069 function d3_geo_pathBuffer() {
4070 var pointCircle = d3_geo_pathBufferCircle(4.5),
4076 // While inside a line, override point to moveTo then lineTo.
4077 lineStart: function() { stream.point = pointLineStart; },
4080 // While inside a polygon, override lineEnd to closePath.
4081 polygonStart: function() { stream.lineEnd = lineEndPolygon; },
4082 polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; },
4084 pointRadius: function(_) {
4085 pointCircle = d3_geo_pathBufferCircle(_);
4089 result: function() {
4090 if (buffer.length) {
4091 var result = buffer.join("");
4098 function point(x, y) {
4099 buffer.push("M", x, ",", y, pointCircle);
4102 function pointLineStart(x, y) {
4103 buffer.push("M", x, ",", y);
4104 stream.point = pointLine;
4107 function pointLine(x, y) {
4108 buffer.push("L", x, ",", y);
4111 function lineEnd() {
4112 stream.point = point;
4115 function lineEndPolygon() {
4122 function d3_geo_pathBufferCircle(radius) {
4123 return "m0," + radius
4124 + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius
4125 + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius
4129 // TODO Unify this code with d3.geom.polygon centroid?
4130 // TODO Enforce positive area for exterior, negative area for interior?
4132 var d3_geo_pathCentroid = {
4133 point: d3_geo_pathCentroidPoint,
4135 // For lines, weight by length.
4136 lineStart: d3_geo_pathCentroidLineStart,
4137 lineEnd: d3_geo_pathCentroidLineEnd,
4139 // For polygons, weight by area.
4140 polygonStart: function() {
4141 d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
4143 polygonEnd: function() {
4144 d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
4145 d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
4146 d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
4150 function d3_geo_pathCentroidPoint(x, y) {
4151 d3_geo_centroidX0 += x;
4152 d3_geo_centroidY0 += y;
4153 ++d3_geo_centroidZ0;
4156 function d3_geo_pathCentroidLineStart() {
4159 d3_geo_pathCentroid.point = function(x, y) {
4160 d3_geo_pathCentroid.point = nextPoint;
4161 d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4164 function nextPoint(x, y) {
4165 var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
4166 d3_geo_centroidX1 += z * (x0 + x) / 2;
4167 d3_geo_centroidY1 += z * (y0 + y) / 2;
4168 d3_geo_centroidZ1 += z;
4169 d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4173 function d3_geo_pathCentroidLineEnd() {
4174 d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
4177 function d3_geo_pathCentroidRingStart() {
4178 var x00, y00, x0, y0;
4180 // For the first point, …
4181 d3_geo_pathCentroid.point = function(x, y) {
4182 d3_geo_pathCentroid.point = nextPoint;
4183 d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
4186 // For subsequent points, …
4187 function nextPoint(x, y) {
4188 var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
4189 d3_geo_centroidX1 += z * (x0 + x) / 2;
4190 d3_geo_centroidY1 += z * (y0 + y) / 2;
4191 d3_geo_centroidZ1 += z;
4193 z = y0 * x - x0 * y;
4194 d3_geo_centroidX2 += z * (x0 + x);
4195 d3_geo_centroidY2 += z * (y0 + y);
4196 d3_geo_centroidZ2 += z * 3;
4197 d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4200 // For the last point, return to the start.
4201 d3_geo_pathCentroid.lineEnd = function() {
4202 nextPoint(x00, y00);
4206 function d3_geo_pathContext(context) {
4207 var pointRadius = 4.5;
4212 // While inside a line, override point to moveTo then lineTo.
4213 lineStart: function() { stream.point = pointLineStart; },
4216 // While inside a polygon, override lineEnd to closePath.
4217 polygonStart: function() { stream.lineEnd = lineEndPolygon; },
4218 polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; },
4220 pointRadius: function(_) {
4228 function point(x, y) {
4229 context.moveTo(x + pointRadius, y);
4230 context.arc(x, y, pointRadius, 0, τ);
4233 function pointLineStart(x, y) {
4234 context.moveTo(x, y);
4235 stream.point = pointLine;
4238 function pointLine(x, y) {
4239 context.lineTo(x, y);
4242 function lineEnd() {
4243 stream.point = point;
4246 function lineEndPolygon() {
4247 context.closePath();
4253 function d3_geo_resample(project) {
4254 var δ2 = .5, // precision, px²
4255 cosMinDistance = Math.cos(30 * d3_radians), // cos(minimum angular distance)
4258 function resample(stream) {
4259 return (maxDepth ? resampleRecursive : resampleNone)(stream);
4262 function resampleNone(stream) {
4263 return d3_geo_transformPoint(stream, function(x, y) {
4265 stream.point(x[0], x[1]);
4269 function resampleRecursive(stream) {
4270 var λ00, φ00, x00, y00, a00, b00, c00, // first point
4271 λ0, x0, y0, a0, b0, c0; // previous point
4275 lineStart: lineStart,
4277 polygonStart: function() { stream.polygonStart(); resample.lineStart = ringStart; },
4278 polygonEnd: function() { stream.polygonEnd(); resample.lineStart = lineStart; }
4281 function point(x, y) {
4283 stream.point(x[0], x[1]);
4286 function lineStart() {
4288 resample.point = linePoint;
4292 function linePoint(λ, φ) {
4293 var c = d3_geo_cartesian([λ, φ]), p = project(λ, φ);
4294 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);
4295 stream.point(x0, y0);
4298 function lineEnd() {
4299 resample.point = point;
4303 function ringStart() {
4305 resample.point = ringPoint;
4306 resample.lineEnd = ringEnd;
4309 function ringPoint(λ, φ) {
4310 linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
4311 resample.point = linePoint;
4314 function ringEnd() {
4315 resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
4316 resample.lineEnd = lineEnd;
4323 function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
4326 d2 = dx * dx + dy * dy;
4327 if (d2 > 4 * δ2 && depth--) {
4331 m = Math.sqrt(a * a + b * b + c * c),
4332 φ2 = Math.asin(c /= m),
4333 λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a),
4334 p = project(λ2, φ2),
4339 dz = dy * dx2 - dx * dy2;
4340 if (dz * dz / d2 > δ2 // perpendicular projected distance
4341 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 // midpoint close to an end
4342 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
4343 resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
4344 stream.point(x2, y2);
4345 resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
4350 resample.precision = function(_) {
4351 if (!arguments.length) return Math.sqrt(δ2);
4352 maxDepth = (δ2 = _ * _) > 0 && 16;
4359 d3.geo.path = function() {
4360 var pointRadius = 4.5,
4367 function path(object) {
4369 if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
4370 if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
4371 d3.geo.stream(object, cacheStream);
4373 return contextStream.result();
4376 path.area = function(object) {
4377 d3_geo_pathAreaSum = 0;
4378 d3.geo.stream(object, projectStream(d3_geo_pathArea));
4379 return d3_geo_pathAreaSum;
4382 path.centroid = function(object) {
4383 d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 =
4384 d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 =
4385 d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
4386 d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
4387 return d3_geo_centroidZ2 ? [d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2]
4388 : d3_geo_centroidZ1 ? [d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1]
4389 : d3_geo_centroidZ0 ? [d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0]
4393 path.bounds = function(object) {
4394 d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
4395 d3.geo.stream(object, projectStream(d3_geo_pathBounds));
4396 return [[d3_geo_pathBoundsX0, d3_geo_pathBoundsY0], [d3_geo_pathBoundsX1, d3_geo_pathBoundsY1]];
4399 path.projection = function(_) {
4400 if (!arguments.length) return projection;
4401 projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
4405 path.context = function(_) {
4406 if (!arguments.length) return context;
4407 contextStream = (context = _) == null ? new d3_geo_pathBuffer : new d3_geo_pathContext(_);
4408 if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
4412 path.pointRadius = function(_) {
4413 if (!arguments.length) return pointRadius;
4414 pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
4423 return path.projection(d3.geo.albersUsa()).context(null);
4426 function d3_geo_pathProjectStream(project) {
4427 var resample = d3_geo_resample(function(x, y) { return project([x * d3_degrees, y * d3_degrees]); });
4428 return function(stream) { return d3_geo_projectionRadians(resample(stream)); };
4431 d3.geo.transform = function(methods) {
4433 stream: function(stream) {
4434 var transform = new d3_geo_transform(stream);
4435 for (var k in methods) transform[k] = methods[k];
4441 function d3_geo_transform(stream) {
4442 this.stream = stream;
4445 d3_geo_transform.prototype = {
4446 point: function(x, y) { this.stream.point(x, y); },
4447 sphere: function() { this.stream.sphere(); },
4448 lineStart: function() { this.stream.lineStart(); },
4449 lineEnd: function() { this.stream.lineEnd(); },
4450 polygonStart: function() { this.stream.polygonStart(); },
4451 polygonEnd: function() { this.stream.polygonEnd(); }
4454 function d3_geo_transformPoint(stream, point) {
4457 sphere: function() { stream.sphere(); },
4458 lineStart: function() { stream.lineStart(); },
4459 lineEnd: function() { stream.lineEnd(); },
4460 polygonStart: function() { stream.polygonStart(); },
4461 polygonEnd: function() { stream.polygonEnd(); },
4465 d3.geo.projection = d3_geo_projection;
4466 d3.geo.projectionMutator = d3_geo_projectionMutator;
4468 function d3_geo_projection(project) {
4469 return d3_geo_projectionMutator(function() { return project; })();
4472 function d3_geo_projectionMutator(projectAt) {
4476 projectResample = d3_geo_resample(function(x, y) { x = project(x, y); return [x[0] * k + δx, δy - x[1] * k]; }),
4478 x = 480, y = 250, // translate
4479 λ = 0, φ = 0, // center
4480 δλ = 0, δφ = 0, δγ = 0, // rotate
4482 preclip = d3_geo_clipAntimeridian,
4483 postclip = d3_identity,
4488 function projection(point) {
4489 point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
4490 return [point[0] * k + δx, δy - point[1] * k];
4493 function invert(point) {
4494 point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k);
4495 return point && [point[0] * d3_degrees, point[1] * d3_degrees];
4498 projection.stream = function(output) {
4499 if (stream) stream.valid = false;
4500 stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
4501 stream.valid = true; // allow caching by d3.geo.path
4505 projection.clipAngle = function(_) {
4506 if (!arguments.length) return clipAngle;
4507 preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
4508 return invalidate();
4511 projection.clipExtent = function(_) {
4512 if (!arguments.length) return clipExtent;
4514 postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
4515 return invalidate();
4518 projection.scale = function(_) {
4519 if (!arguments.length) return k;
4524 projection.translate = function(_) {
4525 if (!arguments.length) return [x, y];
4531 projection.center = function(_) {
4532 if (!arguments.length) return [λ * d3_degrees, φ * d3_degrees];
4533 λ = _[0] % 360 * d3_radians;
4534 φ = _[1] % 360 * d3_radians;
4538 projection.rotate = function(_) {
4539 if (!arguments.length) return [δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees];
4540 δλ = _[0] % 360 * d3_radians;
4541 δφ = _[1] % 360 * d3_radians;
4542 δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
4546 d3.rebind(projection, projectResample, "precision");
4549 projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
4550 var center = project(λ, φ);
4551 δx = x - center[0] * k;
4552 δy = y + center[1] * k;
4553 return invalidate();
4556 function invalidate() {
4557 if (stream) stream.valid = false, stream = null;
4562 project = projectAt.apply(this, arguments);
4563 projection.invert = project.invert && invert;
4568 function d3_geo_projectionRadians(stream) {
4569 return d3_geo_transformPoint(stream, function(x, y) {
4570 stream.point(x * d3_radians, y * d3_radians);
4574 function d3_geo_mercator(λ, φ) {
4575 return [λ, Math.log(Math.tan(π / 4 + φ / 2))];
4578 d3_geo_mercator.invert = function(x, y) {
4579 return [x, 2 * Math.atan(Math.exp(y)) - halfπ];
4582 function d3_geo_mercatorProjection(project) {
4583 var m = d3_geo_projection(project),
4585 translate = m.translate,
4586 clipExtent = m.clipExtent,
4589 m.scale = function() {
4590 var v = scale.apply(m, arguments);
4591 return v === m ? (clipAuto ? m.clipExtent(null) : m) : v;
4594 m.translate = function() {
4595 var v = translate.apply(m, arguments);
4596 return v === m ? (clipAuto ? m.clipExtent(null) : m) : v;
4599 m.clipExtent = function(_) {
4600 var v = clipExtent.apply(m, arguments);
4602 if (clipAuto = _ == null) {
4603 var k = π * scale(), t = translate();
4604 clipExtent([[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]]);
4606 } else if (clipAuto) {
4612 return m.clipExtent(null);
4615 (d3.geo.mercator = function() {
4616 return d3_geo_mercatorProjection(d3_geo_mercator);
4617 }).raw = d3_geo_mercator;
4620 d3.geom.polygon = function(coordinates) {
4621 d3_subclass(coordinates, d3_geom_polygonPrototype);
4625 var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
4627 d3_geom_polygonPrototype.area = function() {
4637 area += a[1] * b[0] - a[0] * b[1];
4643 d3_geom_polygonPrototype.centroid = function(k) {
4652 if (!arguments.length) k = -1 / (6 * this.area());
4657 c = a[0] * b[1] - b[0] * a[1];
4658 x += (a[0] + b[0]) * c;
4659 y += (a[1] + b[1]) * c;
4662 return [x * k, y * k];
4665 // The Sutherland-Hodgman clipping algorithm.
4666 // Note: requires the clip polygon to be counterclockwise and convex.
4667 d3_geom_polygonPrototype.clip = function(subject) {
4669 closed = d3_geom_polygonClosed(subject),
4671 n = this.length - d3_geom_polygonClosed(this),
4680 input = subject.slice();
4683 c = input[(m = input.length - closed) - 1];
4687 if (d3_geom_polygonInside(d, a, b)) {
4688 if (!d3_geom_polygonInside(c, a, b)) {
4689 subject.push(d3_geom_polygonIntersect(c, d, a, b));
4692 } else if (d3_geom_polygonInside(c, a, b)) {
4693 subject.push(d3_geom_polygonIntersect(c, d, a, b));
4697 if (closed) subject.push(subject[0]);
4704 function d3_geom_polygonInside(p, a, b) {
4705 return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
4708 // Intersect two infinite lines cd and ab.
4709 function d3_geom_polygonIntersect(c, d, a, b) {
4710 var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3,
4711 y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3,
4712 ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);
4713 return [x1 + ua * x21, y1 + ua * y21];
4716 // Returns true if the polygon is closed.
4717 function d3_geom_polygonClosed(coordinates) {
4718 var a = coordinates[0],
4719 b = coordinates[coordinates.length - 1];
4720 return !(a[0] - b[0] || a[1] - b[1]);
4722 function d3_geom_pointX(d) {
4726 function d3_geom_pointY(d) {
4731 * Computes the 2D convex hull of a set of points using the monotone chain
4733 * http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain)
4735 * The runtime of this algorithm is O(n log n), where n is the number of input
4736 * points. However in practice it outperforms other O(n log n) hulls.
4738 * @param vertices [[x1, y1], [x2, y2], ...]
4739 * @returns polygon [[x1, y1], [x2, y2], ...]
4741 d3.geom.hull = function(vertices) {
4742 var x = d3_geom_pointX,
4745 if (arguments.length) return hull(vertices);
4747 function hull(data) {
4748 // Hull of < 3 points is not well-defined
4749 if (data.length < 3) return [];
4751 var fx = d3_functor(x),
4755 points = [], // of the form [[x0, y0, 0], ..., [xn, yn, n]]
4758 for (i = 0 ; i < n; i++) {
4759 points.push([+fx.call(this, data[i], i), +fy.call(this, data[i], i), i]);
4762 // sort ascending by x-coord first, y-coord second
4763 points.sort(d3_geom_hullOrder);
4765 // we flip bottommost points across y axis so we can use the upper hull routine on both
4766 for (i = 0; i < n; i++) flippedPoints.push([points[i][0], -points[i][1]]);
4768 var upper = d3_geom_hullUpper(points),
4769 lower = d3_geom_hullUpper(flippedPoints);
4771 // construct the polygon, removing possible duplicate endpoints
4772 var skipLeft = lower[0] === upper[0],
4773 skipRight = lower[lower.length - 1] === upper[upper.length - 1],
4776 // add upper hull in r->l order
4777 // then add lower hull in l->r order
4778 for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]);
4779 for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]);
4784 hull.x = function(_) {
4785 return arguments.length ? (x = _, hull) : x;
4788 hull.y = function(_) {
4789 return arguments.length ? (y = _, hull) : y;
4795 // finds the 'upper convex hull' (see wiki link above)
4796 // assumes points arg has >=3 elements, is sorted by x, unique in y
4797 // returns array of indices into points in left to right order
4798 function d3_geom_hullUpper(points) {
4799 var n = points.length,
4801 hs = 2; // hull size
4803 for (var i = 2; i < n; i++) {
4804 while (hs > 1 && d3_cross2d(points[hull[hs-2]], points[hull[hs-1]], points[i]) <= 0) --hs;
4808 // we slice to make sure that the points we 'popped' from hull don't stay behind
4809 return hull.slice(0, hs);
4812 // comparator for ascending sort by x-coord first, y-coord second
4813 function d3_geom_hullOrder(a, b) {
4814 return a[0] - b[0] || a[1] - b[1];
4816 // import "../transition/transition";
4818 d3_selectionPrototype.transition = function(name) {
4819 var id = d3_transitionInheritId || ++d3_transitionId,
4820 ns = d3_transitionNamespace(name),
4824 transition = d3_transitionInherit || {time: Date.now(), ease: d3_ease_cubicInOut, delay: 0, duration: 250};
4826 for (var j = -1, m = this.length; ++j < m;) {
4827 subgroups.push(subgroup = []);
4828 for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4829 if (node = group[i]) d3_transitionNode(node, i, ns, id, transition);
4830 subgroup.push(node);
4834 return d3_transition(subgroups, ns, id);
4836 // import "../transition/transition";
4838 // TODO Interrupt transitions for all namespaces?
4839 d3_selectionPrototype.interrupt = function(name) {
4840 return this.each(name == null
4841 ? d3_selection_interrupt
4842 : d3_selection_interruptNS(d3_transitionNamespace(name)));
4845 var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace());
4847 function d3_selection_interruptNS(ns) {
4850 if ((lock = this[ns]) && (active = lock[lock.active])) {
4851 if (--lock.count) delete lock[lock.active];
4852 else delete this[ns];
4854 active.event && active.event.interrupt.call(this, this.__data__, active.index);
4859 function d3_transition(groups, ns, id) {
4860 d3_subclass(groups, d3_transitionPrototype);
4863 groups.namespace = ns;
4869 var d3_transitionPrototype = [],
4870 d3_transitionId = 0,
4871 d3_transitionInheritId,
4872 d3_transitionInherit;
4874 d3_transitionPrototype.call = d3_selectionPrototype.call;
4875 d3_transitionPrototype.empty = d3_selectionPrototype.empty;
4876 d3_transitionPrototype.node = d3_selectionPrototype.node;
4877 d3_transitionPrototype.size = d3_selectionPrototype.size;
4879 d3.transition = function(selection, name) {
4880 return selection && selection.transition
4881 ? (d3_transitionInheritId ? selection.transition(name) : selection)
4882 : d3.selection().transition(selection);
4885 d3.transition.prototype = d3_transitionPrototype;
4888 d3_transitionPrototype.select = function(selector) {
4890 ns = this.namespace,
4896 selector = d3_selection_selector(selector);
4898 for (var j = -1, m = this.length; ++j < m;) {
4899 subgroups.push(subgroup = []);
4900 for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4901 if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
4902 if ("__data__" in node) subnode.__data__ = node.__data__;
4903 d3_transitionNode(subnode, i, ns, id, node[ns][id]);
4904 subgroup.push(subnode);
4906 subgroup.push(null);
4911 return d3_transition(subgroups, ns, id);
4914 d3_transitionPrototype.selectAll = function(selector) {
4916 ns = this.namespace,
4924 selector = d3_selection_selectorAll(selector);
4926 for (var j = -1, m = this.length; ++j < m;) {
4927 for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4928 if (node = group[i]) {
4929 transition = node[ns][id];
4930 subnodes = selector.call(node, node.__data__, i, j);
4931 subgroups.push(subgroup = []);
4932 for (var k = -1, o = subnodes.length; ++k < o;) {
4933 if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition);
4934 subgroup.push(subnode);
4940 return d3_transition(subgroups, ns, id);
4943 d3_transitionPrototype.filter = function(filter) {
4949 if (typeof filter !== "function") filter = d3_selection_filter(filter);
4951 for (var j = 0, m = this.length; j < m; j++) {
4952 subgroups.push(subgroup = []);
4953 for (var group = this[j], i = 0, n = group.length; i < n; i++) {
4954 if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
4955 subgroup.push(node);
4960 return d3_transition(subgroups, this.namespace, this.id);
4962 d3.color = d3_color;
4964 function d3_color() {}
4966 d3_color.prototype.toString = function() {
4967 return this.rgb() + "";
4972 function d3_hsl(h, s, l) {
4973 return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l)
4974 : arguments.length < 2 ? (h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l)
4975 : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl))
4976 : new d3_hsl(h, s, l);
4979 var d3_hslPrototype = d3_hsl.prototype = new d3_color;
4981 d3_hslPrototype.brighter = function(k) {
4982 k = Math.pow(0.7, arguments.length ? k : 1);
4983 return new d3_hsl(this.h, this.s, this.l / k);
4986 d3_hslPrototype.darker = function(k) {
4987 k = Math.pow(0.7, arguments.length ? k : 1);
4988 return new d3_hsl(this.h, this.s, k * this.l);
4991 d3_hslPrototype.rgb = function() {
4992 return d3_hsl_rgb(this.h, this.s, this.l);
4995 function d3_hsl_rgb(h, s, l) {
4999 /* Some simple corrections for h, s and l. */
5000 h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
5001 s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
5002 l = l < 0 ? 0 : l > 1 ? 1 : l;
5004 /* From FvD 13.37, CSS Color Module Level 3 */
5005 m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
5009 if (h > 360) h -= 360;
5010 else if (h < 0) h += 360;
5011 if (h < 60) return m1 + (m2 - m1) * h / 60;
5012 if (h < 180) return m2;
5013 if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
5018 return Math.round(v(h) * 255);
5021 return new d3_rgb(vv(h + 120), vv(h), vv(h - 120));
5026 function d3_hcl(h, c, l) {
5027 return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l)
5028 : arguments.length < 2 ? (h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l)
5029 : (h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b)
5030 : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b)))
5031 : new d3_hcl(h, c, l);
5034 var d3_hclPrototype = d3_hcl.prototype = new d3_color;
5036 d3_hclPrototype.brighter = function(k) {
5037 return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
5040 d3_hclPrototype.darker = function(k) {
5041 return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
5044 d3_hclPrototype.rgb = function() {
5045 return d3_hcl_lab(this.h, this.c, this.l).rgb();
5048 function d3_hcl_lab(h, c, l) {
5049 if (isNaN(h)) h = 0;
5050 if (isNaN(c)) c = 0;
5051 return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
5056 function d3_lab(l, a, b) {
5057 return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b)
5058 : arguments.length < 2 ? (l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b)
5059 : (l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l)
5060 : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b)))
5061 : new d3_lab(l, a, b);
5064 // Corresponds roughly to RGB brighter/darker
5067 // D65 standard referent
5068 var d3_lab_X = 0.950470,
5070 d3_lab_Z = 1.088830;
5072 var d3_labPrototype = d3_lab.prototype = new d3_color;
5074 d3_labPrototype.brighter = function(k) {
5075 return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
5078 d3_labPrototype.darker = function(k) {
5079 return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
5082 d3_labPrototype.rgb = function() {
5083 return d3_lab_rgb(this.l, this.a, this.b);
5086 function d3_lab_rgb(l, a, b) {
5087 var y = (l + 16) / 116,
5090 x = d3_lab_xyz(x) * d3_lab_X;
5091 y = d3_lab_xyz(y) * d3_lab_Y;
5092 z = d3_lab_xyz(z) * d3_lab_Z;
5094 d3_xyz_rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z),
5095 d3_xyz_rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z),
5096 d3_xyz_rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z)
5100 function d3_lab_hcl(l, a, b) {
5102 ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l)
5103 : new d3_hcl(NaN, NaN, l);
5106 function d3_lab_xyz(x) {
5107 return x > 0.206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
5109 function d3_xyz_lab(x) {
5110 return x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
5113 function d3_xyz_rgb(r) {
5114 return Math.round(255 * (r <= 0.00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - 0.055));
5119 function d3_rgb(r, g, b) {
5120 return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b)
5121 : arguments.length < 2 ? (r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b)
5122 : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb))
5123 : new d3_rgb(r, g, b);
5126 function d3_rgbNumber(value) {
5127 return new d3_rgb(value >> 16, value >> 8 & 0xff, value & 0xff);
5130 function d3_rgbString(value) {
5131 return d3_rgbNumber(value) + "";
5134 var d3_rgbPrototype = d3_rgb.prototype = new d3_color;
5136 d3_rgbPrototype.brighter = function(k) {
5137 k = Math.pow(0.7, arguments.length ? k : 1);
5142 if (!r && !g && !b) return new d3_rgb(i, i, i);
5143 if (r && r < i) r = i;
5144 if (g && g < i) g = i;
5145 if (b && b < i) b = i;
5146 return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k));
5149 d3_rgbPrototype.darker = function(k) {
5150 k = Math.pow(0.7, arguments.length ? k : 1);
5151 return new d3_rgb(k * this.r, k * this.g, k * this.b);
5154 d3_rgbPrototype.hsl = function() {
5155 return d3_rgb_hsl(this.r, this.g, this.b);
5158 d3_rgbPrototype.toString = function() {
5159 return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
5162 function d3_rgb_hex(v) {
5164 ? "0" + Math.max(0, v).toString(16)
5165 : Math.min(255, v).toString(16);
5168 function d3_rgb_parse(format, rgb, hsl) {
5169 var r = 0, // red channel; int in [0, 255]
5170 g = 0, // green channel; int in [0, 255]
5171 b = 0, // blue channel; int in [0, 255]
5172 m1, // CSS color specification match
5173 m2, // CSS color specification type (e.g., rgb)
5176 /* Handle hsl, rgb. */
5177 m1 = /([a-z]+)\((.*)\)/i.exec(format);
5179 m2 = m1[2].split(",");
5183 parseFloat(m2[0]), // degrees
5184 parseFloat(m2[1]) / 100, // percentage
5185 parseFloat(m2[2]) / 100 // percentage
5190 d3_rgb_parseNumber(m2[0]),
5191 d3_rgb_parseNumber(m2[1]),
5192 d3_rgb_parseNumber(m2[2])
5199 if (color = d3_rgb_names.get(format.toLowerCase())) {
5200 return rgb(color.r, color.g, color.b);
5203 /* Hexadecimal colors: #rgb and #rrggbb. */
5204 if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) {
5205 if (format.length === 4) {
5206 r = (color & 0xf00) >> 4; r = (r >> 4) | r;
5207 g = (color & 0xf0); g = (g >> 4) | g;
5208 b = (color & 0xf); b = (b << 4) | b;
5209 } else if (format.length === 7) {
5210 r = (color & 0xff0000) >> 16;
5211 g = (color & 0xff00) >> 8;
5216 return rgb(r, g, b);
5219 function d3_rgb_hsl(r, g, b) {
5220 var min = Math.min(r /= 255, g /= 255, b /= 255),
5221 max = Math.max(r, g, b),
5225 l = (max + min) / 2;
5227 s = l < .5 ? d / (max + min) : d / (2 - max - min);
5228 if (r == max) h = (g - b) / d + (g < b ? 6 : 0);
5229 else if (g == max) h = (b - r) / d + 2;
5230 else h = (r - g) / d + 4;
5234 s = l > 0 && l < 1 ? 0 : h;
5236 return new d3_hsl(h, s, l);
5239 function d3_rgb_lab(r, g, b) {
5243 var x = d3_xyz_lab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / d3_lab_X),
5244 y = d3_xyz_lab((0.2126729 * r + 0.7151522 * g + 0.0721750 * b) / d3_lab_Y),
5245 z = d3_xyz_lab((0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / d3_lab_Z);
5246 return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
5249 function d3_rgb_xyz(r) {
5250 return (r /= 255) <= 0.04045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
5253 function d3_rgb_parseNumber(c) { // either integer or percentage
5254 var f = parseFloat(c);
5255 return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
5258 var d3_rgb_names = d3.map({
5259 aliceblue: 0xf0f8ff,
5260 antiquewhite: 0xfaebd7,
5262 aquamarine: 0x7fffd4,
5267 blanchedalmond: 0xffebcd,
5269 blueviolet: 0x8a2be2,
5271 burlywood: 0xdeb887,
5272 cadetblue: 0x5f9ea0,
5273 chartreuse: 0x7fff00,
5274 chocolate: 0xd2691e,
5276 cornflowerblue: 0x6495ed,
5282 darkgoldenrod: 0xb8860b,
5284 darkgreen: 0x006400,
5286 darkkhaki: 0xbdb76b,
5287 darkmagenta: 0x8b008b,
5288 darkolivegreen: 0x556b2f,
5289 darkorange: 0xff8c00,
5290 darkorchid: 0x9932cc,
5292 darksalmon: 0xe9967a,
5293 darkseagreen: 0x8fbc8f,
5294 darkslateblue: 0x483d8b,
5295 darkslategray: 0x2f4f4f,
5296 darkslategrey: 0x2f4f4f,
5297 darkturquoise: 0x00ced1,
5298 darkviolet: 0x9400d3,
5300 deepskyblue: 0x00bfff,
5303 dodgerblue: 0x1e90ff,
5304 firebrick: 0xb22222,
5305 floralwhite: 0xfffaf0,
5306 forestgreen: 0x228b22,
5308 gainsboro: 0xdcdcdc,
5309 ghostwhite: 0xf8f8ff,
5311 goldenrod: 0xdaa520,
5314 greenyellow: 0xadff2f,
5318 indianred: 0xcd5c5c,
5323 lavenderblush: 0xfff0f5,
5324 lawngreen: 0x7cfc00,
5325 lemonchiffon: 0xfffacd,
5326 lightblue: 0xadd8e6,
5327 lightcoral: 0xf08080,
5328 lightcyan: 0xe0ffff,
5329 lightgoldenrodyellow: 0xfafad2,
5330 lightgray: 0xd3d3d3,
5331 lightgreen: 0x90ee90,
5332 lightgrey: 0xd3d3d3,
5333 lightpink: 0xffb6c1,
5334 lightsalmon: 0xffa07a,
5335 lightseagreen: 0x20b2aa,
5336 lightskyblue: 0x87cefa,
5337 lightslategray: 0x778899,
5338 lightslategrey: 0x778899,
5339 lightsteelblue: 0xb0c4de,
5340 lightyellow: 0xffffe0,
5342 limegreen: 0x32cd32,
5346 mediumaquamarine: 0x66cdaa,
5347 mediumblue: 0x0000cd,
5348 mediumorchid: 0xba55d3,
5349 mediumpurple: 0x9370db,
5350 mediumseagreen: 0x3cb371,
5351 mediumslateblue: 0x7b68ee,
5352 mediumspringgreen: 0x00fa9a,
5353 mediumturquoise: 0x48d1cc,
5354 mediumvioletred: 0xc71585,
5355 midnightblue: 0x191970,
5356 mintcream: 0xf5fffa,
5357 mistyrose: 0xffe4e1,
5359 navajowhite: 0xffdead,
5363 olivedrab: 0x6b8e23,
5365 orangered: 0xff4500,
5367 palegoldenrod: 0xeee8aa,
5368 palegreen: 0x98fb98,
5369 paleturquoise: 0xafeeee,
5370 palevioletred: 0xdb7093,
5371 papayawhip: 0xffefd5,
5372 peachpuff: 0xffdab9,
5376 powderblue: 0xb0e0e6,
5378 rebeccapurple: 0x663399,
5380 rosybrown: 0xbc8f8f,
5381 royalblue: 0x4169e1,
5382 saddlebrown: 0x8b4513,
5384 sandybrown: 0xf4a460,
5390 slateblue: 0x6a5acd,
5391 slategray: 0x708090,
5392 slategrey: 0x708090,
5394 springgreen: 0x00ff7f,
5395 steelblue: 0x4682b4,
5400 turquoise: 0x40e0d0,
5404 whitesmoke: 0xf5f5f5,
5406 yellowgreen: 0x9acd32
5409 d3_rgb_names.forEach(function(key, value) {
5410 d3_rgb_names.set(key, d3_rgbNumber(value));
5413 d3.interpolateRgb = d3_interpolateRgb;
5415 function d3_interpolateRgb(a, b) {
5424 return function(t) {
5426 + d3_rgb_hex(Math.round(ar + br * t))
5427 + d3_rgb_hex(Math.round(ag + bg * t))
5428 + d3_rgb_hex(Math.round(ab + bb * t));
5432 d3.interpolateObject = d3_interpolateObject;
5434 function d3_interpolateObject(a, b) {
5440 i[k] = d3_interpolate(a[k], b[k]);
5450 return function(t) {
5451 for (k in i) c[k] = i[k](t);
5456 d3.interpolateArray = d3_interpolateArray;
5458 function d3_interpolateArray(a, b) {
5463 n0 = Math.min(a.length, b.length),
5465 for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
5466 for (; i < na; ++i) c[i] = a[i];
5467 for (; i < nb; ++i) c[i] = b[i];
5468 return function(t) {
5469 for (i = 0; i < n0; ++i) c[i] = x[i](t);
5473 d3.interpolateNumber = d3_interpolateNumber;
5475 function d3_interpolateNumber(a, b) {
5477 return function(t) { return a * (1 - t) + b * t; };
5480 d3.interpolateString = d3_interpolateString;
5482 function d3_interpolateString(a, b) {
5483 var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, // scan index for next number in b
5484 am, // current match in a
5485 bm, // current match in b
5486 bs, // string preceding current number in b, if any
5487 i = -1, // index in s
5488 s = [], // string constants and placeholders
5489 q = []; // number interpolators
5491 // Coerce inputs to strings.
5492 a = a + "", b = b + "";
5494 // Interpolate pairs of numbers in a & b.
5495 while ((am = d3_interpolate_numberA.exec(a))
5496 && (bm = d3_interpolate_numberB.exec(b))) {
5497 if ((bs = bm.index) > bi) { // a string precedes the next number in b
5498 bs = b.slice(bi, bs);
5499 if (s[i]) s[i] += bs; // coalesce with previous string
5502 if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
5503 if (s[i]) s[i] += bm; // coalesce with previous string
5505 } else { // interpolate non-matching numbers
5507 q.push({i: i, x: d3_interpolateNumber(am, bm)});
5509 bi = d3_interpolate_numberB.lastIndex;
5512 // Add remains of b.
5513 if (bi < b.length) {
5515 if (s[i]) s[i] += bs; // coalesce with previous string
5519 // Special optimization for only a single match.
5520 // Otherwise, interpolate each of the numbers and rejoin the string.
5522 ? (q[0] ? (b = q[0].x, function(t) { return b(t) + ""; })
5523 : function() { return b; })
5524 : (b = q.length, function(t) {
5525 for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
5530 var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
5531 d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g");
5533 d3.interpolate = d3_interpolate;
5535 function d3_interpolate(a, b) {
5536 var i = d3.interpolators.length, f;
5537 while (--i >= 0 && !(f = d3.interpolators[i](a, b)));
5541 d3.interpolators = [
5544 return (t === "string" ? (d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString)
5545 : b instanceof d3_color ? d3_interpolateRgb
5546 : Array.isArray(b) ? d3_interpolateArray
5547 : t === "object" && isNaN(b) ? d3_interpolateObject
5548 : d3_interpolateNumber)(a, b);
5552 d3.transform = function(string) {
5553 var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
5554 return (d3.transform = function(string) {
5555 if (string != null) {
5556 g.setAttribute("transform", string);
5557 var t = g.transform.baseVal.consolidate();
5559 return new d3_transform(t ? t.matrix : d3_transformIdentity);
5563 // Compute x-scale and normalize the first row.
5564 // Compute shear and make second row orthogonal to first.
5565 // Compute y-scale and normalize the second row.
5566 // Finally, compute the rotation.
5567 function d3_transform(m) {
5568 var r0 = [m.a, m.b],
5570 kx = d3_transformNormalize(r0),
5571 kz = d3_transformDot(r0, r1),
5572 ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
5573 if (r0[0] * r1[1] < r1[0] * r0[1]) {
5579 this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
5580 this.translate = [m.e, m.f];
5581 this.scale = [kx, ky];
5582 this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
5585 d3_transform.prototype.toString = function() {
5586 return "translate(" + this.translate
5587 + ")rotate(" + this.rotate
5588 + ")skewX(" + this.skew
5589 + ")scale(" + this.scale
5593 function d3_transformDot(a, b) {
5594 return a[0] * b[0] + a[1] * b[1];
5597 function d3_transformNormalize(a) {
5598 var k = Math.sqrt(d3_transformDot(a, a));
5606 function d3_transformCombine(a, b, k) {
5612 var d3_transformIdentity = {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0};
5614 d3.interpolateTransform = d3_interpolateTransform;
5616 function d3_interpolateTransform(a, b) {
5617 var s = [], // string constants and placeholders
5618 q = [], // number interpolators
5620 A = d3.transform(a),
5621 B = d3.transform(b),
5631 if (ta[0] != tb[0] || ta[1] != tb[1]) {
5632 s.push("translate(", null, ",", null, ")");
5633 q.push({i: 1, x: d3_interpolateNumber(ta[0], tb[0])}, {i: 3, x: d3_interpolateNumber(ta[1], tb[1])});
5634 } else if (tb[0] || tb[1]) {
5635 s.push("translate(" + tb + ")");
5641 if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; // shortest path
5642 q.push({i: s.push(s.pop() + "rotate(", null, ")") - 2, x: d3_interpolateNumber(ra, rb)});
5644 s.push(s.pop() + "rotate(" + rb + ")");
5648 q.push({i: s.push(s.pop() + "skewX(", null, ")") - 2, x: d3_interpolateNumber(wa, wb)});
5650 s.push(s.pop() + "skewX(" + wb + ")");
5653 if (ka[0] != kb[0] || ka[1] != kb[1]) {
5654 n = s.push(s.pop() + "scale(", null, ",", null, ")");
5655 q.push({i: n - 4, x: d3_interpolateNumber(ka[0], kb[0])}, {i: n - 2, x: d3_interpolateNumber(ka[1], kb[1])});
5656 } else if (kb[0] != 1 || kb[1] != 1) {
5657 s.push(s.pop() + "scale(" + kb + ")");
5661 return function(t) {
5663 while (++i < n) s[(o = q[i]).i] = o.x(t);
5668 d3_transitionPrototype.tween = function(name, tween) {
5669 var id = this.id, ns = this.namespace;
5670 if (arguments.length < 2) return this.node()[ns][id].tween.get(name);
5671 return d3_selection_each(this, tween == null
5672 ? function(node) { node[ns][id].tween.remove(name); }
5673 : function(node) { node[ns][id].tween.set(name, tween); });
5676 function d3_transition_tween(groups, name, value, tween) {
5677 var id = groups.id, ns = groups.namespace;
5678 return d3_selection_each(groups, typeof value === "function"
5679 ? function(node, i, j) { node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j))); }
5680 : (value = tween(value), function(node) { node[ns][id].tween.set(name, value); }));
5683 d3_transitionPrototype.attr = function(nameNS, value) {
5684 if (arguments.length < 2) {
5686 // For attr(object), the object specifies the names and values of the
5687 // attributes to transition. The values may be functions that are
5688 // evaluated for each element.
5689 for (value in nameNS) this.attr(value, nameNS[value]);
5693 var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate,
5694 name = d3.ns.qualify(nameNS);
5696 // For attr(string, null), remove the attribute with the specified name.
5697 function attrNull() {
5698 this.removeAttribute(name);
5700 function attrNullNS() {
5701 this.removeAttributeNS(name.space, name.local);
5704 // For attr(string, string), set the attribute with the specified name.
5705 function attrTween(b) {
5706 return b == null ? attrNull : (b += "", function() {
5707 var a = this.getAttribute(name), i;
5708 return a !== b && (i = interpolate(a, b), function(t) { this.setAttribute(name, i(t)); });
5711 function attrTweenNS(b) {
5712 return b == null ? attrNullNS : (b += "", function() {
5713 var a = this.getAttributeNS(name.space, name.local), i;
5714 return a !== b && (i = interpolate(a, b), function(t) { this.setAttributeNS(name.space, name.local, i(t)); });
5718 return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
5721 d3_transitionPrototype.attrTween = function(nameNS, tween) {
5722 var name = d3.ns.qualify(nameNS);
5724 function attrTween(d, i) {
5725 var f = tween.call(this, d, i, this.getAttribute(name));
5726 return f && function(t) { this.setAttribute(name, f(t)); };
5728 function attrTweenNS(d, i) {
5729 var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
5730 return f && function(t) { this.setAttributeNS(name.space, name.local, f(t)); };
5733 return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
5736 d3_transitionPrototype.style = function(name, value, priority) {
5737 var n = arguments.length;
5740 // For style(object) or style(object, string), the object specifies the
5741 // names and values of the attributes to set or remove. The values may be
5742 // functions that are evaluated for each element. The optional string
5743 // specifies the priority.
5744 if (typeof name !== "string") {
5745 if (n < 2) value = "";
5746 for (priority in name) this.style(priority, name[priority], value);
5750 // For style(string, string) or style(string, function), use the default
5751 // priority. The priority is ignored for style(string, null).
5755 // For style(name, null) or style(name, null, priority), remove the style
5756 // property with the specified name. The priority is ignored.
5757 function styleNull() {
5758 this.style.removeProperty(name);
5761 // For style(name, string) or style(name, string, priority), set the style
5762 // property with the specified name, using the specified priority.
5763 // Otherwise, a name, value and priority are specified, and handled as below.
5764 function styleString(b) {
5765 return b == null ? styleNull : (b += "", function() {
5766 var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i;
5767 return a !== b && (i = d3_interpolate(a, b), function(t) { this.style.setProperty(name, i(t), priority); });
5771 return d3_transition_tween(this, "style." + name, value, styleString);
5774 d3_transitionPrototype.styleTween = function(name, tween, priority) {
5775 if (arguments.length < 3) priority = "";
5777 function styleTween(d, i) {
5778 var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name));
5779 return f && function(t) { this.style.setProperty(name, f(t), priority); };
5782 return this.tween("style." + name, styleTween);
5785 d3_transitionPrototype.text = function(value) {
5786 return d3_transition_tween(this, "text", value, d3_transition_text);
5789 function d3_transition_text(b) {
5790 if (b == null) b = "";
5791 return function() { this.textContent = b; };
5794 d3_transitionPrototype.remove = function() {
5795 var ns = this.namespace;
5796 return this.each("end.transition", function() {
5798 if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this);
5802 var d3_ease_default = function() { return d3_identity; };
5804 var d3_ease = d3.map({
5805 linear: d3_ease_default,
5807 quad: function() { return d3_ease_quad; },
5808 cubic: function() { return d3_ease_cubic; },
5809 sin: function() { return d3_ease_sin; },
5810 exp: function() { return d3_ease_exp; },
5811 circle: function() { return d3_ease_circle; },
5812 elastic: d3_ease_elastic,
5814 bounce: function() { return d3_ease_bounce; }
5817 var d3_ease_mode = d3.map({
5819 "out": d3_ease_reverse,
5820 "in-out": d3_ease_reflect,
5821 "out-in": function(f) { return d3_ease_reflect(d3_ease_reverse(f)); }
5824 d3.ease = function(name) {
5825 var i = name.indexOf("-"),
5826 t = i >= 0 ? name.slice(0, i) : name,
5827 m = i >= 0 ? name.slice(i + 1) : "in";
5828 t = d3_ease.get(t) || d3_ease_default;
5829 m = d3_ease_mode.get(m) || d3_identity;
5830 return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
5833 function d3_ease_clamp(f) {
5834 return function(t) {
5835 return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
5839 function d3_ease_reverse(f) {
5840 return function(t) {
5841 return 1 - f(1 - t);
5845 function d3_ease_reflect(f) {
5846 return function(t) {
5847 return .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t)));
5851 function d3_ease_quad(t) {
5855 function d3_ease_cubic(t) {
5859 // Optimized clamp(reflect(poly(3))).
5860 function d3_ease_cubicInOut(t) {
5861 if (t <= 0) return 0;
5862 if (t >= 1) return 1;
5863 var t2 = t * t, t3 = t2 * t;
5864 return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
5867 function d3_ease_poly(e) {
5868 return function(t) {
5869 return Math.pow(t, e);
5873 function d3_ease_sin(t) {
5874 return 1 - Math.cos(t * halfπ);
5877 function d3_ease_exp(t) {
5878 return Math.pow(2, 10 * (t - 1));
5881 function d3_ease_circle(t) {
5882 return 1 - Math.sqrt(1 - t * t);
5885 function d3_ease_elastic(a, p) {
5887 if (arguments.length < 2) p = 0.45;
5888 if (arguments.length) s = p / τ * Math.asin(1 / a);
5889 else a = 1, s = p / 4;
5890 return function(t) {
5891 return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p);
5895 function d3_ease_back(s) {
5896 if (!s) s = 1.70158;
5897 return function(t) {
5898 return t * t * ((s + 1) * t - s);
5902 function d3_ease_bounce(t) {
5903 return t < 1 / 2.75 ? 7.5625 * t * t
5904 : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75
5905 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375
5906 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
5909 d3_transitionPrototype.ease = function(value) {
5910 var id = this.id, ns = this.namespace;
5911 if (arguments.length < 1) return this.node()[ns][id].ease;
5912 if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
5913 return d3_selection_each(this, function(node) { node[ns][id].ease = value; });
5916 d3_transitionPrototype.delay = function(value) {
5917 var id = this.id, ns = this.namespace;
5918 if (arguments.length < 1) return this.node()[ns][id].delay;
5919 return d3_selection_each(this, typeof value === "function"
5920 ? function(node, i, j) { node[ns][id].delay = +value.call(node, node.__data__, i, j); }
5921 : (value = +value, function(node) { node[ns][id].delay = value; }));
5924 d3_transitionPrototype.duration = function(value) {
5925 var id = this.id, ns = this.namespace;
5926 if (arguments.length < 1) return this.node()[ns][id].duration;
5927 return d3_selection_each(this, typeof value === "function"
5928 ? function(node, i, j) { node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j)); }
5929 : (value = Math.max(1, value), function(node) { node[ns][id].duration = value; }));
5932 d3_transitionPrototype.each = function(type, listener) {
5933 var id = this.id, ns = this.namespace;
5934 if (arguments.length < 2) {
5935 var inherit = d3_transitionInherit,
5936 inheritId = d3_transitionInheritId;
5938 d3_transitionInheritId = id;
5939 d3_selection_each(this, function(node, i, j) {
5940 d3_transitionInherit = node[ns][id];
5941 type.call(node, node.__data__, i, j);
5944 d3_transitionInherit = inherit;
5945 d3_transitionInheritId = inheritId;
5948 d3_selection_each(this, function(node) {
5949 var transition = node[ns][id];
5950 (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener);
5956 d3_transitionPrototype.transition = function() {
5958 id1 = ++d3_transitionId,
5959 ns = this.namespace,
5966 for (var j = 0, m = this.length; j < m; j++) {
5967 subgroups.push(subgroup = []);
5968 for (var group = this[j], i = 0, n = group.length; i < n; i++) {
5969 if (node = group[i]) {
5970 transition = node[ns][id0];
5971 d3_transitionNode(node, i, ns, id1, {time: transition.time, ease: transition.ease, delay: transition.delay + transition.duration, duration: transition.duration});
5973 subgroup.push(node);
5977 return d3_transition(subgroups, ns, id1);
5980 function d3_transitionNamespace(name) {
5981 return name == null ? "__transition__" : "__transition_" + name + "__";
5984 function d3_transitionNode(node, i, ns, id, inherit) {
5985 var lock = node[ns] || (node[ns] = {active: 0, count: 0}),
5986 transition = lock[id];
5989 var time = inherit.time;
5991 transition = lock[id] = {
5994 delay: inherit.delay,
5995 duration: inherit.duration,
6000 inherit = null; // allow gc
6004 d3.timer(function(elapsed) {
6005 var delay = transition.delay,
6008 timer = d3_timer_active,
6011 timer.t = delay + time;
6012 if (delay <= elapsed) return start(elapsed - delay);
6015 function start(elapsed) {
6016 if (lock.active > id) return stop();
6018 var active = lock[lock.active];
6021 delete lock[lock.active];
6022 active.event && active.event.interrupt.call(node, node.__data__, active.index);
6027 transition.event && transition.event.start.call(node, node.__data__, i);
6029 transition.tween.forEach(function(key, value) {
6030 if (value = value.call(node, node.__data__, i)) {
6031 tweened.push(value);
6035 // Deferred capture to allow tweens to initialize ease & duration.
6036 ease = transition.ease;
6037 duration = transition.duration;
6039 d3.timer(function() { // defer to end of current frame
6040 timer.c = tick(elapsed || 1) ? d3_true : tick;
6045 function tick(elapsed) {
6046 if (lock.active !== id) return 1;
6048 var t = elapsed / duration,
6053 tweened[--n].call(node, e);
6057 transition.event && transition.event.end.call(node, node.__data__, i);
6063 if (--lock.count) delete lock[id];
6064 else delete node[ns];
6071 d3.xhr = d3_xhrType(d3_identity);
6073 function d3_xhrType(response) {
6074 return function(url, mimeType, callback) {
6075 if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, mimeType = null;
6076 return d3_xhr(url, mimeType, response, callback);
6080 function d3_xhr(url, mimeType, response, callback) {
6082 dispatch = d3.dispatch("beforesend", "progress", "load", "error"),
6084 request = new XMLHttpRequest,
6085 responseType = null;
6087 // If IE does not support CORS, use XDomainRequest.
6088 if (this.XDomainRequest
6089 && !("withCredentials" in request)
6090 && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest;
6093 ? request.onload = request.onerror = respond
6094 : request.onreadystatechange = function() { request.readyState > 3 && respond(); };
6096 function respond() {
6097 var status = request.status, result;
6098 if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) {
6100 result = response.call(xhr, request);
6102 dispatch.error.call(xhr, e);
6105 dispatch.load.call(xhr, result);
6107 dispatch.error.call(xhr, request);
6111 request.onprogress = function(event) {
6114 try { dispatch.progress.call(xhr, request); }
6115 finally { d3.event = o; }
6118 xhr.header = function(name, value) {
6119 name = (name + "").toLowerCase();
6120 if (arguments.length < 2) return headers[name];
6121 if (value == null) delete headers[name];
6122 else headers[name] = value + "";
6126 // If mimeType is non-null and no Accept header is set, a default is used.
6127 xhr.mimeType = function(value) {
6128 if (!arguments.length) return mimeType;
6129 mimeType = value == null ? null : value + "";
6133 // Specifies what type the response value should take;
6134 // for instance, arraybuffer, blob, document, or text.
6135 xhr.responseType = function(value) {
6136 if (!arguments.length) return responseType;
6137 responseType = value;
6141 // Specify how to convert the response content to a specific type;
6142 // changes the callback value on "load" events.
6143 xhr.response = function(value) {
6148 // Convenience methods.
6149 ["get", "post"].forEach(function(method) {
6150 xhr[method] = function() {
6151 return xhr.send.apply(xhr, [method].concat(d3_array(arguments)));
6155 // If callback is non-null, it will be used for error and load events.
6156 xhr.send = function(method, data, callback) {
6157 if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
6158 request.open(method, url, true);
6159 if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
6160 if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
6161 if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
6162 if (responseType != null) request.responseType = responseType;
6163 if (callback != null) xhr.on("error", callback).on("load", function(request) { callback(null, request); });
6164 dispatch.beforesend.call(xhr, request);
6165 request.send(data == null ? null : data);
6169 xhr.abort = function() {
6174 d3.rebind(xhr, dispatch, "on");
6176 return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
6179 function d3_xhr_fixCallback(callback) {
6180 return callback.length === 1
6181 ? function(error, request) { callback(error == null ? request : null); }
6185 function d3_xhrHasResponse(request) {
6186 var type = request.responseType;
6187 return type && type !== "text"
6188 ? request.response // null on error
6189 : request.responseText; // "" on error
6192 d3.text = d3_xhrType(function(request) {
6193 return request.responseText;
6196 d3.json = function(url, callback) {
6197 return d3_xhr(url, "application/json", d3_json, callback);
6200 function d3_json(request) {
6201 return JSON.parse(request.responseText);
6204 d3.html = function(url, callback) {
6205 return d3_xhr(url, "text/html", d3_html, callback);
6208 function d3_html(request) {
6209 var range = d3_document.createRange();
6210 range.selectNode(d3_document.body);
6211 return range.createContextualFragment(request.responseText);
6214 d3.xml = d3_xhrType(function(request) {
6215 return request.responseXML;
6217 if (typeof define === "function" && define.amd) define(d3);
6218 else if (typeof module === "object" && module.exports) module.exports = d3;
6221 d3.combobox = function() {
6222 var event = d3.dispatch('accept'),
6226 caseSensitive = false;
6228 var fetcher = function(val, cb) {
6229 cb(data.filter(function(d) {
6233 .indexOf(val.toLowerCase()) !== -1;
6237 var combobox = function(input, attachTo) {
6239 container = d3.select(document.body)
6240 .selectAll('div.combobox')
6241 .filter(function(d) { return d === input.node(); }),
6242 shown = !container.empty();
6245 .classed('combobox-input', true)
6246 .on('focus.typeahead', focus)
6247 .on('blur.typeahead', blur)
6248 .on('keydown.typeahead', keydown)
6249 .on('keyup.typeahead', keyup)
6250 .on('input.typeahead', change)
6252 var parent = this.parentNode,
6253 sibling = this.nextSibling;
6255 var caret = d3.select(parent).selectAll('.combobox-caret')
6256 .filter(function(d) { return d === input.node(); })
6257 .data([input.node()]);
6259 caret.enter().insert('div', function() { return sibling; })
6260 .attr('class', 'combobox-caret');
6263 .on('mousedown', function () {
6264 // prevent the form element from blurring. it blurs
6266 d3.event.stopPropagation();
6267 d3.event.preventDefault();
6269 input.node().focus();
6278 fetch(value(), render);
6282 window.setTimeout(hide, 150);
6287 container = d3.select(document.body)
6288 .insert('div', ':first-child')
6289 .datum(input.node())
6290 .attr('class', 'combobox')
6292 position: 'absolute',
6296 .on('mousedown', function () {
6297 // prevent moving focus out of the text field
6298 d3.event.preventDefault();
6301 d3.select(document.body)
6302 .on('scroll.combobox', render, true);
6313 d3.select(document.body)
6314 .on('scroll.combobox', null);
6320 function keydown() {
6321 switch (d3.event.keyCode) {
6322 // backspace, delete
6325 input.on('input.typeahead', function() {
6328 var start = input.property('selectionStart');
6329 input.node().setSelectionRange(start, start);
6330 input.on('input.typeahead', change);
6335 container.selectAll('a.selected').each(event.accept);
6339 d3.event.preventDefault();
6344 d3.event.preventDefault();
6349 d3.event.preventDefault();
6352 d3.event.stopPropagation();
6356 switch (d3.event.keyCode) {
6363 container.selectAll('a.selected').each(event.accept);
6370 fetch(value(), function() {
6377 if (!suggestions.length) return;
6378 idx = Math.max(Math.min(idx + dir, suggestions.length - 1), 0);
6379 input.property('value', suggestions[idx].value);
6385 var value = input.property('value'),
6386 start = input.property('selectionStart'),
6387 end = input.property('selectionEnd');
6390 value = value.substring(0, start);
6396 function fetch(v, cb) {
6397 fetcher.call(input, v, function(_) {
6403 function autocomplete() {
6404 var v = caseSensitive ? value() : value().toLowerCase();
6408 for (var i = 0; i < suggestions.length; i++) {
6409 var suggestion = suggestions[i].value,
6410 compare = caseSensitive ? suggestion : suggestion.toLowerCase();
6412 if (compare.indexOf(v) === 0) {
6414 input.property('value', suggestion);
6415 input.node().setSelectionRange(v.length, suggestion.length);
6422 if (suggestions.length >= minItems && document.activeElement === input.node()) {
6429 var options = container
6430 .selectAll('a.combobox-option')
6431 .data(suggestions, function(d) { return d.value; });
6433 options.enter().append('a')
6434 .attr('class', 'combobox-option')
6435 .text(function(d) { return d.value; });
6438 .attr('title', function(d) { return d.title; })
6439 .classed('selected', function(d, i) { return i == idx; })
6440 .on('mouseover', select)
6441 .on('click', accept)
6447 var node = attachTo ? attachTo.node() : input.node(),
6448 rect = node.getBoundingClientRect();
6451 'left': rect.left + 'px',
6452 'width': rect.width + 'px',
6453 'top': rect.height + rect.top + 'px'
6457 function select(d, i) {
6462 function ensureVisible() {
6463 var node = container.selectAll('a.selected').node();
6464 if (node) node.scrollIntoView();
6467 function accept(d) {
6470 .property('value', d.value)
6477 combobox.fetcher = function(_) {
6478 if (!arguments.length) return fetcher;
6483 combobox.data = function(_) {
6484 if (!arguments.length) return data;
6489 combobox.minItems = function(_) {
6490 if (!arguments.length) return minItems;
6495 combobox.caseSensitive = function(_) {
6496 if (!arguments.length) return caseSensitive;
6501 return d3.rebind(combobox, event, 'on');
6504 d3.combobox.off = function(input) {
6509 .on('focus.typeahead', null)
6510 .on('blur.typeahead', null)
6511 .on('keydown.typeahead', null)
6512 .on('keyup.typeahead', null)
6513 .on('input.typeahead', null)
6515 d3.select(this.parentNode).selectAll('.combobox-caret')
6516 .filter(function(d) { return d === input.node(); })
6517 .on('mousedown', null);
6520 d3.select(document.body)
6521 .on('scroll.combobox', null);
6523 d3.geo.tile = function() {
6524 var size = [960, 500],
6526 scaleExtent = [0, 20],
6527 translate = [size[0] / 2, size[1] / 2],
6531 return Math.min(scaleExtent[1], Math.max(scaleExtent[0], _));
6535 var z = Math.max(Math.log(scale) / Math.LN2 - 8, 0),
6536 z0 = bound(Math.round(z + zoomDelta)),
6537 k = Math.pow(2, z - z0 + 8),
6538 origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k],
6540 cols = d3.range(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))),
6541 rows = d3.range(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1])));
6543 rows.forEach(function(y) {
6544 cols.forEach(function(x) {
6545 tiles.push([x, y, z0]);
6549 tiles.translate = origin;
6555 tile.scaleExtent = function(_) {
6556 if (!arguments.length) return scaleExtent;
6561 tile.size = function(_) {
6562 if (!arguments.length) return size;
6567 tile.scale = function(_) {
6568 if (!arguments.length) return scale;
6573 tile.translate = function(_) {
6574 if (!arguments.length) return translate;
6579 tile.zoomDelta = function(_) {
6580 if (!arguments.length) return zoomDelta;
6587 d3.jsonp = function (url, callback) {
6589 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
6591 while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
6595 function create(url) {
6596 var e = url.match(/callback=d3.jsonp.(\w+)/),
6597 c = e ? e[1] : rand();
6598 d3.jsonp[c] = function(data) {
6603 return 'd3.jsonp.' + c;
6606 var cb = create(url),
6607 script = d3.select('head')
6609 .attr('type', 'text/javascript')
6610 .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
6613 * This code is licensed under the MIT license.
6615 * Copyright © 2013, iD authors.
6617 * Portions copyright © 2011, Keith Cirkel
6618 * See https://github.com/keithamus/jwerty
6621 d3.keybinding = function(namespace) {
6624 function matches(binding, event) {
6625 for (var p in binding.event) {
6626 if (event[p] != binding.event[p])
6630 return (!binding.capture) === (event.eventPhase !== Event.CAPTURING_PHASE);
6633 function capture() {
6634 for (var i = 0; i < bindings.length; i++) {
6635 var binding = bindings[i];
6636 if (matches(binding, d3.event)) {
6643 var tagName = d3.select(d3.event.target).node().tagName;
6644 if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
6650 function keybinding(selection) {
6651 selection = selection || d3.select(document);
6652 selection.on('keydown.capture' + namespace, capture, true);
6653 selection.on('keydown.bubble' + namespace, bubble, false);
6657 keybinding.off = function(selection) {
6658 selection = selection || d3.select(document);
6659 selection.on('keydown.capture' + namespace, null);
6660 selection.on('keydown.bubble' + namespace, null);
6664 keybinding.on = function(code, callback, capture) {
6677 code = code.toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
6679 for (var i = 0; i < code.length; i++) {
6680 // Normalise matching errors
6681 if (code[i] === '++') code[i] = '+';
6683 if (code[i] in d3.keybinding.modifierCodes) {
6684 binding.event[d3.keybinding.modifierProperties[d3.keybinding.modifierCodes[code[i]]]] = true;
6685 } else if (code[i] in d3.keybinding.keyCodes) {
6686 binding.event.keyCode = d3.keybinding.keyCodes[code[i]];
6690 bindings.push(binding);
6699 d3.keybinding.modifierCodes = {
6702 // CTRL key, on Mac: ⌃
6704 // ALT key, on Mac: ⌥ (Alt)
6705 '⌥': 18, alt: 18, option: 18,
6706 // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
6707 '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
6710 d3.keybinding.modifierProperties = {
6717 d3.keybinding.keyCodes = {
6718 // Backspace key, on Mac: ⌫ (Backspace)
6719 '⌫': 8, backspace: 8,
6720 // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
6721 '⇥': 9, '⇆': 9, tab: 9,
6723 '↩': 13, 'return': 13, enter: 13, '⌅': 13,
6725 'pause': 19, 'pause-break': 19,
6727 '⇪': 20, caps: 20, 'caps-lock': 20,
6728 // Escape key, on Mac: ⎋, on Windows: Esc
6729 '⎋': 27, escape: 27, esc: 27,
6732 // Page-Up key, or pgup, on Mac: ↖
6733 '↖': 33, pgup: 33, 'page-up': 33,
6734 // Page-Down key, or pgdown, on Mac: ↘
6735 '↘': 34, pgdown: 34, 'page-down': 34,
6736 // END key, on Mac: ⇟
6738 // HOME key, on Mac: ⇞
6740 // Insert key, or ins
6741 ins: 45, insert: 45,
6742 // Delete key, on Mac: ⌦ (Delete)
6743 '⌦': 46, del: 46, 'delete': 46,
6744 // Left Arrow Key, or ←
6745 '←': 37, left: 37, 'arrow-left': 37,
6746 // Up Arrow Key, or ↑
6747 '↑': 38, up: 38, 'arrow-up': 38,
6748 // Right Arrow Key, or →
6749 '→': 39, right: 39, 'arrow-right': 39,
6750 // Up Arrow Key, or ↓
6751 '↓': 40, down: 40, 'arrow-down': 40,
6752 // odities, printing characters that come out wrong:
6755 // Num-Multiply, or *
6756 '*': 106, star: 106, asterisk: 106, multiply: 106,
6758 '+': 107, 'plus': 107,
6759 // Num-Subtract, or -
6760 '-': 109, subtract: 109,
6766 ';': 186, semicolon: 186,
6768 '=': 187, 'equals': 187,
6770 ',': 188, comma: 188,
6772 // Period, or ., or full-stop
6773 '.': 190, period: 190, 'full-stop': 190,
6774 // Slash, or /, or forward-slash
6775 '/': 191, slash: 191, 'forward-slash': 191,
6776 // Tick, or `, or back-quote
6777 '`': 192, tick: 192, 'back-quote': 192,
6778 // Open bracket, or [
6779 '[': 219, 'open-bracket': 219,
6781 '\\': 220, 'back-slash': 220,
6782 // Close backet, or ]
6783 ']': 221, 'close-bracket': 221,
6784 // Apostrophe, or Quote, or '
6785 '\'': 222, quote: 222, apostrophe: 222
6791 d3.keybinding.keyCodes['num-' + n] = i;
6798 d3.keybinding.keyCodes[n] = i;
6805 d3.keybinding.keyCodes['f' + n] = i;
6812 d3.keybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
6815 d3.selection.prototype.one = function (type, listener, capture) {
6816 var target = this, typeOnce = type + ".once";
6818 target.on(typeOnce, null);
6819 listener.apply(this, arguments);
6821 target.on(typeOnce, one, capture);
6824 d3.selection.prototype.dimensions = function (dimensions) {
6825 var refresh = (function(node) {
6826 var cr = node.getBoundingClientRect();
6827 prop = [cr.width, cr.height];
6828 this.property('__dimensions__', prop);
6832 var node = this.node();
6834 if (!arguments.length) {
6835 if (!node) return [0,0];
6836 return this.property('__dimensions__') || refresh(node);
6838 if (dimensions === null) {
6839 if (!node) return [0,0];
6840 return refresh(node);
6844 .property('__dimensions__', [dimensions[0], dimensions[1]])
6845 .attr({width: dimensions[0], height: dimensions[1]});
6847 d3.selection.prototype.trigger = function (type) {
6848 this.each(function() {
6849 var evt = document.createEvent('HTMLEvents');
6850 evt.initEvent(type, true, true);
6851 this.dispatchEvent(evt);
6854 // Tooltips and svg mask used to highlight certain features
6855 d3.curtain = function() {
6857 var event = d3.dispatch(),
6862 function curtain(selection) {
6864 surface = selection.append('svg')
6865 .attr('id', 'curtain')
6868 'pointer-events': 'none',
6869 'position': 'absolute',
6874 darkness = surface.append('path')
6878 'class': 'curtain-darkness'
6881 d3.select(window).on('resize.curtain', resize);
6883 tooltip = selection.append('div')
6884 .attr('class', 'tooltip')
6885 .style('z-index', 1002);
6887 tooltip.append('div').attr('class', 'tooltip-arrow');
6888 tooltip.append('div').attr('class', 'tooltip-inner');
6894 width: window.innerWidth,
6895 height: window.innerHeight
6897 curtain.cut(darkness.datum());
6901 curtain.reveal = function(box, text, tooltipclass, duration) {
6902 if (typeof box === 'string') box = d3.select(box).node();
6903 if (box.getBoundingClientRect) box = box.getBoundingClientRect();
6905 curtain.cut(box, duration);
6908 // pseudo markdown bold text hack
6909 var parts = text.split('**');
6910 var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
6911 if (parts[1]) html += '<span class="bold">' + parts[1] + '</span>';
6913 var dimensions = tooltip.classed('in', true)
6914 .select('.tooltip-inner')
6920 var w = window.innerWidth,
6921 h = window.innerHeight;
6923 if (box.top + box.height < Math.min(100, box.width + box.left)) {
6925 pos = [box.left + box.width / 2 - dimensions[0]/ 2, box.top + box.height];
6927 } else if (box.left + box.width + 300 < window.innerWidth) {
6929 pos = [box.left + box.width, box.top + box.height / 2 - dimensions[1] / 2];
6931 } else if (box.left > 300) {
6933 pos = [box.left - 200, box.top + box.height / 2 - dimensions[1] / 2];
6936 pos = [box.left, box.top + box.height];
6940 Math.min(Math.max(10, pos[0]), w - dimensions[0] - 10),
6941 Math.min(Math.max(10, pos[1]), h - dimensions[1] - 10)
6945 if (duration !== 0 || !tooltip.classed(side)) tooltip.call(iD.ui.Toggle(true));
6948 .style('top', pos[1] + 'px')
6949 .style('left', pos[0] + 'px')
6950 .attr('class', 'curtain-tooltip tooltip in ' + side + ' ' + tooltipclass)
6951 .select('.tooltip-inner')
6955 tooltip.call(iD.ui.Toggle(false));
6959 curtain.cut = function(datum, duration) {
6960 darkness.datum(datum);
6962 (duration === 0 ? darkness : darkness.transition().duration(duration || 600))
6963 .attr('d', function(d) {
6964 var string = "M 0,0 L 0," + window.innerHeight + " L " +
6965 window.innerWidth + "," + window.innerHeight + "L" +
6966 window.innerWidth + ",0 Z";
6968 if (!d) return string;
6969 return string + 'M' +
6970 d.left + ',' + d.top + 'L' +
6971 d.left + ',' + (d.top + d.height) + 'L' +
6972 (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
6973 (d.left + d.width) + ',' + (d.top) + 'Z';
6978 curtain.remove = function() {
6983 return d3.rebind(curtain, event, 'on');
6985 // Like selection.property('value', ...), but avoids no-op value sets,
6986 // which can result in layout/repaint thrashing in some situations.
6987 d3.selection.prototype.value = function(value) {
6988 function d3_selection_value(value) {
6989 function valueNull() {
6993 function valueConstant() {
6994 if (this.value !== value) this.value = value;
6997 function valueFunction() {
6998 var x = value.apply(this, arguments);
6999 if (x == null) delete this.value;
7000 else if (this.value !== x) this.value = x;
7003 return value == null
7004 ? valueNull : (typeof value === "function"
7005 ? valueFunction : valueConstant);
7008 if (!arguments.length) return this.property('value');
7009 return this.each(d3_selection_value(value));
7011 // Copyright (c) 2006, 2008 Tony Garnock-Jones <tonyg@lshift.net>
7012 // Copyright (c) 2006, 2008 LShift Ltd. <query@lshift.net>
7014 // Permission is hereby granted, free of charge, to any person
7015 // obtaining a copy of this software and associated documentation files
7016 // (the "Software"), to deal in the Software without restriction,
7017 // including without limitation the rights to use, copy, modify, merge,
7018 // publish, distribute, sublicense, and/or sell copies of the Software,
7019 // and to permit persons to whom the Software is furnished to do so,
7020 // subject to the following conditions:
7022 // The above copyright notice and this permission notice shall be
7023 // included in all copies or substantial portions of the Software.
7025 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
7026 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
7027 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
7028 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
7029 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
7030 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7031 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7034 // source: https://bitbucket.org/lshift/synchrotron/src
7036 Diff3 = (function() {
7040 longest_common_subsequence: function(file1, file2) {
7041 /* Text diff algorithm following Hunt and McIlroy 1976.
7042 * J. W. Hunt and M. D. McIlroy, An algorithm for differential file
7043 * comparison, Bell Telephone Laboratories CSTR #41 (1976)
7044 * http://www.cs.dartmouth.edu/~doug/
7046 * Expects two arrays of strings.
7048 var equivalenceClasses;
7053 var c, i, j, jX, r, s;
7055 equivalenceClasses = {};
7056 for (j = 0; j < file2.length; j++) {
7058 if (equivalenceClasses[line]) {
7059 equivalenceClasses[line].push(j);
7061 equivalenceClasses[line] = [j];
7065 candidates = [{file1index: -1,
7069 for (i = 0; i < file1.length; i++) {
7071 file2indices = equivalenceClasses[line] || [];
7076 for (jX = 0; jX < file2indices.length; jX++) {
7077 j = file2indices[jX];
7079 for (s = 0; s < candidates.length; s++) {
7080 if ((candidates[s].file2index < j) &&
7081 ((s == candidates.length - 1) ||
7082 (candidates[s + 1].file2index > j)))
7086 if (s < candidates.length) {
7087 newCandidate = {file1index: i,
7089 chain: candidates[s]};
7090 if (r == candidates.length) {
7097 if (r == candidates.length) {
7098 break; // no point in examining further (j)s
7106 // At this point, we know the LCS: it's in the reverse of the
7107 // linked-list through .chain of
7108 // candidates[candidates.length - 1].
7110 return candidates[candidates.length - 1];
7113 diff_comm: function(file1, file2) {
7114 // We apply the LCS to build a "comm"-style picture of the
7115 // differences between file1 and file2.
7118 var tail1 = file1.length;
7119 var tail2 = file2.length;
7120 var common = {common: []};
7122 function processCommon() {
7123 if (common.common.length) {
7124 common.common.reverse();
7125 result.push(common);
7126 common = {common: []};
7130 for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7132 candidate = candidate.chain)
7134 var different = {file1: [], file2: []};
7136 while (--tail1 > candidate.file1index) {
7137 different.file1.push(file1[tail1]);
7140 while (--tail2 > candidate.file2index) {
7141 different.file2.push(file2[tail2]);
7144 if (different.file1.length || different.file2.length) {
7146 different.file1.reverse();
7147 different.file2.reverse();
7148 result.push(different);
7152 common.common.push(file1[tail1]);
7162 diff_patch: function(file1, file2) {
7163 // We apply the LCD to build a JSON representation of a
7164 // diff(1)-style patch.
7167 var tail1 = file1.length;
7168 var tail2 = file2.length;
7170 function chunkDescription(file, offset, length) {
7172 for (var i = 0; i < length; i++) {
7173 chunk.push(file[offset + i]);
7175 return {offset: offset,
7180 for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7182 candidate = candidate.chain)
7184 var mismatchLength1 = tail1 - candidate.file1index - 1;
7185 var mismatchLength2 = tail2 - candidate.file2index - 1;
7186 tail1 = candidate.file1index;
7187 tail2 = candidate.file2index;
7189 if (mismatchLength1 || mismatchLength2) {
7190 result.push({file1: chunkDescription(file1,
7191 candidate.file1index + 1,
7193 file2: chunkDescription(file2,
7194 candidate.file2index + 1,
7203 strip_patch: function(patch) {
7204 // Takes the output of Diff3.diff_patch(), and removes
7205 // information from it. It can still be used by patch(),
7206 // below, but can no longer be inverted.
7208 for (var i = 0; i < patch.length; i++) {
7209 var chunk = patch[i];
7210 newpatch.push({file1: {offset: chunk.file1.offset,
7211 length: chunk.file1.length},
7212 file2: {chunk: chunk.file2.chunk}});
7217 invert_patch: function(patch) {
7218 // Takes the output of Diff3.diff_patch(), and inverts the
7219 // sense of it, so that it can be applied to file2 to give
7220 // file1 rather than the other way around.
7222 for (var i = 0; i < patch.length; i++) {
7223 var chunk = patch[i];
7224 var tmp = chunk.file1;
7225 chunk.file1 = chunk.file2;
7230 patch: function (file, patch) {
7231 // Applies a patch to a file.
7233 // Given file1 and file2, Diff3.patch(file1,
7234 // Diff3.diff_patch(file1, file2)) should give file2.
7237 var commonOffset = 0;
7239 function copyCommon(targetOffset) {
7240 while (commonOffset < targetOffset) {
7241 result.push(file[commonOffset]);
7246 for (var chunkIndex = 0; chunkIndex < patch.length; chunkIndex++) {
7247 var chunk = patch[chunkIndex];
7248 copyCommon(chunk.file1.offset);
7249 for (var lineIndex = 0; lineIndex < chunk.file2.chunk.length; lineIndex++) {
7250 result.push(chunk.file2.chunk[lineIndex]);
7252 commonOffset += chunk.file1.length;
7255 copyCommon(file.length);
7259 diff_indices: function(file1, file2) {
7260 // We apply the LCS to give a simple representation of the
7261 // offsets and lengths of mismatched chunks in the input
7262 // files. This is used by diff3_merge_indices below.
7265 var tail1 = file1.length;
7266 var tail2 = file2.length;
7268 for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7270 candidate = candidate.chain)
7272 var mismatchLength1 = tail1 - candidate.file1index - 1;
7273 var mismatchLength2 = tail2 - candidate.file2index - 1;
7274 tail1 = candidate.file1index;
7275 tail2 = candidate.file2index;
7277 if (mismatchLength1 || mismatchLength2) {
7278 result.push({file1: [tail1 + 1, mismatchLength1],
7279 file2: [tail2 + 1, mismatchLength2]});
7287 diff3_merge_indices: function (a, o, b) {
7288 // Given three files, A, O, and B, where both A and B are
7289 // independently derived from O, returns a fairly complicated
7290 // internal representation of merge decisions it's taken. The
7291 // interested reader may wish to consult
7293 // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce. "A
7294 // Formal Investigation of Diff3." In Arvind and Prasad,
7295 // editors, Foundations of Software Technology and Theoretical
7296 // Computer Science (FSTTCS), December 2007.
7298 // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
7301 var m1 = Diff3.diff_indices(o, a);
7302 var m2 = Diff3.diff_indices(o, b);
7305 function addHunk(h, side) {
7306 hunks.push([h.file1[0], side, h.file1[1], h.file2[0], h.file2[1]]);
7308 for (i = 0; i < m1.length; i++) { addHunk(m1[i], 0); }
7309 for (i = 0; i < m2.length; i++) { addHunk(m2[i], 2); }
7313 var commonOffset = 0;
7314 function copyCommon(targetOffset) {
7315 if (targetOffset > commonOffset) {
7316 result.push([1, commonOffset, targetOffset - commonOffset]);
7317 commonOffset = targetOffset;
7321 for (var hunkIndex = 0; hunkIndex < hunks.length; hunkIndex++) {
7322 var firstHunkIndex = hunkIndex;
7323 var hunk = hunks[hunkIndex];
7324 var regionLhs = hunk[0];
7325 var regionRhs = regionLhs + hunk[2];
7326 while (hunkIndex < hunks.length - 1) {
7327 var maybeOverlapping = hunks[hunkIndex + 1];
7328 var maybeLhs = maybeOverlapping[0];
7329 if (maybeLhs > regionRhs) break;
7330 regionRhs = maybeLhs + maybeOverlapping[2];
7334 copyCommon(regionLhs);
7335 if (firstHunkIndex == hunkIndex) {
7336 // The "overlap" was only one hunk long, meaning that
7337 // there's no conflict here. Either a and o were the
7338 // same, or b and o were the same.
7340 result.push([hunk[1], hunk[3], hunk[4]]);
7343 // A proper conflict. Determine the extents of the
7344 // regions involved from a, o and b. Effectively merge
7345 // all the hunks on the left into one giant hunk, and
7346 // do the same for the right; then, correct for skew
7347 // in the regions of o that each side changed, and
7348 // report appropriate spans for the three sides.
7350 0: [a.length, -1, o.length, -1],
7351 2: [b.length, -1, o.length, -1]
7353 for (i = firstHunkIndex; i <= hunkIndex; i++) {
7356 var r = regions[side];
7358 var oRhs = oLhs + hunk[2];
7359 var abLhs = hunk[3];
7360 var abRhs = abLhs + hunk[4];
7361 r[0] = Math.min(abLhs, r[0]);
7362 r[1] = Math.max(abRhs, r[1]);
7363 r[2] = Math.min(oLhs, r[2]);
7364 r[3] = Math.max(oRhs, r[3]);
7366 var aLhs = regions[0][0] + (regionLhs - regions[0][2]);
7367 var aRhs = regions[0][1] + (regionRhs - regions[0][3]);
7368 var bLhs = regions[2][0] + (regionLhs - regions[2][2]);
7369 var bRhs = regions[2][1] + (regionRhs - regions[2][3]);
7372 regionLhs, regionRhs - regionLhs,
7373 bLhs, bRhs - bLhs]);
7375 commonOffset = regionRhs;
7378 copyCommon(o.length);
7382 diff3_merge: function (a, o, b, excludeFalseConflicts) {
7383 // Applies the output of Diff3.diff3_merge_indices to actually
7384 // construct the merged file; the returned result alternates
7385 // between "ok" and "conflict" blocks.
7388 var files = [a, o, b];
7389 var indices = Diff3.diff3_merge_indices(a, o, b);
7392 function flushOk() {
7393 if (okLines.length) {
7394 result.push({ok: okLines});
7398 function pushOk(xs) {
7399 for (var j = 0; j < xs.length; j++) {
7400 okLines.push(xs[j]);
7404 function isTrueConflict(rec) {
7405 if (rec[2] != rec[6]) return true;
7408 for (var j = 0; j < rec[2]; j++) {
7409 if (a[j + aoff] != b[j + boff]) return true;
7414 for (var i = 0; i < indices.length; i++) {
7418 if (excludeFalseConflicts && !isTrueConflict(x)) {
7419 pushOk(files[0].slice(x[1], x[1] + x[2]));
7422 result.push({conflict: {a: a.slice(x[1], x[1] + x[2]),
7424 o: o.slice(x[3], x[3] + x[4]),
7426 b: b.slice(x[5], x[5] + x[6]),
7430 pushOk(files[side].slice(x[1], x[1] + x[2]));
7441 if (typeof module !== 'undefined') module.exports = Diff3;
7442 var JXON = new (function () {
7444 sValueProp = "keyValue", sAttributesProp = "keyAttributes", sAttrPref = "@", /* you can customize these values */
7445 aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
7447 function parseText (sValue) {
7448 if (rIsNull.test(sValue)) { return null; }
7449 if (rIsBool.test(sValue)) { return sValue.toLowerCase() === "true"; }
7450 if (isFinite(sValue)) { return parseFloat(sValue); }
7451 if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
7455 function EmptyTree () { }
7456 EmptyTree.prototype.toString = function () { return "null"; };
7457 EmptyTree.prototype.valueOf = function () { return null; };
7459 function objectify (vValue) {
7460 return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
7463 function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
7465 nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
7466 bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
7469 sProp, vContent, nLength = 0, sCollectedTxt = "",
7470 vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
7473 for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
7474 oNode = oParentNode.childNodes.item(nItem);
7475 if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is "CDATASection" (4) */
7476 else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is "Text" (3) */
7477 else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is "Element" (1) */
7481 var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
7483 if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
7485 for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
7486 sProp = aCache[nElId].nodeName.toLowerCase();
7487 vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
7488 if (vResult.hasOwnProperty(sProp)) {
7489 if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
7490 vResult[sProp].push(vContent);
7492 vResult[sProp] = vContent;
7499 nAttrLen = oParentNode.attributes.length,
7500 sAPrefix = bNesteAttr ? "" : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
7502 for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
7503 oAttrib = oParentNode.attributes.item(nAttrib);
7504 oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
7508 if (bFreeze) { Object.freeze(oAttrParent); }
7509 vResult[sAttributesProp] = oAttrParent;
7510 nLength -= nAttrLen - 1;
7514 if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
7515 vResult[sValueProp] = vBuiltVal;
7516 } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
7517 vResult = vBuiltVal;
7520 if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
7522 aCache.length = nLevelStart;
7527 function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
7530 if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
7531 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
7532 } else if (oParentObj.constructor === Date) {
7533 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));
7536 for (var sName in oParentObj) {
7537 vValue = oParentObj[sName];
7538 if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
7539 if (sName === sValueProp) {
7540 if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
7541 } else if (sName === sAttributesProp) { /* verbosity level is 3 */
7542 for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
7543 } else if (sName.charAt(0) === sAttrPref) {
7544 oParentEl.setAttribute(sName.slice(1), vValue);
7545 } else if (vValue.constructor === Array) {
7546 for (var nItem = 0; nItem < vValue.length; nItem++) {
7547 oChild = oXMLDoc.createElement(sName);
7548 loadObjTree(oXMLDoc, oChild, vValue[nItem]);
7549 oParentEl.appendChild(oChild);
7552 oChild = oXMLDoc.createElement(sName);
7553 if (vValue instanceof Object) {
7554 loadObjTree(oXMLDoc, oChild, vValue);
7555 } else if (vValue !== null && vValue !== true) {
7556 oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
7558 oParentEl.appendChild(oChild);
7563 this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
7564 var _nVerb = arguments.length > 1 && typeof nVerbosity === "number" ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
7565 return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);
7568 this.unbuild = function (oObjTree) {
7569 var oNewDoc = document.implementation.createDocument("", "", null);
7570 loadObjTree(oNewDoc, oNewDoc, oObjTree);
7574 this.stringify = function (oObjTree) {
7575 return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
7578 // var myObject = JXON.build(doc);
7579 // we got our javascript object! try: alert(JSON.stringify(myObject));
7581 // var newDoc = JXON.unbuild(myObject);
7582 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
7585 * lodash 3.9.3 (Custom Build) <https://lodash.com/>
7586 * Build: `lodash --development --output js/lib/lodash.js include="any,assign,bind,chunk,clone,compact,contains,debounce,difference,each,every,extend,filter,find,first,forEach,forOwn,groupBy,indexOf,intersection,isEmpty,isEqual,isFunction,keys,last,map,omit,pairs,pluck,reject,some,throttle,union,uniq,unique,values,without,flatten,value,chain,cloneDeep,merge,pick,reduce" exports="global,node"`
7587 * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
7588 * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
7589 * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
7590 * Available under MIT license <https://lodash.com/license>
7594 /** Used as a safe reference for `undefined` in pre-ES5 environments. */
7597 /** Used as the semantic version number. */
7598 var VERSION = '3.9.3';
7600 /** Used to compose bitmasks for wrapper metadata. */
7603 CURRY_BOUND_FLAG = 4,
7605 CURRY_RIGHT_FLAG = 16,
7607 PARTIAL_RIGHT_FLAG = 64,
7611 /** Used to detect when a function becomes hot. */
7612 var HOT_COUNT = 150,
7615 /** Used to indicate the type of lazy iteratees. */
7616 var LAZY_DROP_WHILE_FLAG = 0,
7617 LAZY_FILTER_FLAG = 1,
7620 /** Used as the `TypeError` message for "Functions" methods. */
7621 var FUNC_ERROR_TEXT = 'Expected a function';
7623 /** Used as the internal argument placeholder. */
7624 var PLACEHOLDER = '__lodash_placeholder__';
7626 /** `Object#toString` result references. */
7627 var argsTag = '[object Arguments]',
7628 arrayTag = '[object Array]',
7629 boolTag = '[object Boolean]',
7630 dateTag = '[object Date]',
7631 errorTag = '[object Error]',
7632 funcTag = '[object Function]',
7633 mapTag = '[object Map]',
7634 numberTag = '[object Number]',
7635 objectTag = '[object Object]',
7636 regexpTag = '[object RegExp]',
7637 setTag = '[object Set]',
7638 stringTag = '[object String]',
7639 weakMapTag = '[object WeakMap]';
7641 var arrayBufferTag = '[object ArrayBuffer]',
7642 float32Tag = '[object Float32Array]',
7643 float64Tag = '[object Float64Array]',
7644 int8Tag = '[object Int8Array]',
7645 int16Tag = '[object Int16Array]',
7646 int32Tag = '[object Int32Array]',
7647 uint8Tag = '[object Uint8Array]',
7648 uint8ClampedTag = '[object Uint8ClampedArray]',
7649 uint16Tag = '[object Uint16Array]',
7650 uint32Tag = '[object Uint32Array]';
7652 /** Used to match property names within property paths. */
7653 var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,
7654 reIsPlainProp = /^\w*$/,
7655 rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g;
7658 * Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special).
7659 * In addition to special characters the forward slash is escaped to allow for
7660 * easier `eval` use and `Function` compilation.
7662 var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g,
7663 reHasRegExpChars = RegExp(reRegExpChars.source);
7665 /** Used to match backslashes in property paths. */
7666 var reEscapeChar = /\\(\\)?/g;
7668 /** Used to match `RegExp` flags from their coerced string values. */
7669 var reFlags = /\w*$/;
7671 /** Used to detect host constructors (Safari > 5). */
7672 var reIsHostCtor = /^\[object .+?Constructor\]$/;
7674 /** Used to detect unsigned integer values. */
7675 var reIsUint = /^\d+$/;
7677 /** Used to fix the JScript `[[DontEnum]]` bug. */
7679 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
7680 'toLocaleString', 'toString', 'valueOf'
7683 /** Used to identify `toStringTag` values of typed arrays. */
7684 var typedArrayTags = {};
7685 typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
7686 typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
7687 typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
7688 typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
7689 typedArrayTags[uint32Tag] = true;
7690 typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
7691 typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
7692 typedArrayTags[dateTag] = typedArrayTags[errorTag] =
7693 typedArrayTags[funcTag] = typedArrayTags[mapTag] =
7694 typedArrayTags[numberTag] = typedArrayTags[objectTag] =
7695 typedArrayTags[regexpTag] = typedArrayTags[setTag] =
7696 typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
7698 /** Used to identify `toStringTag` values supported by `_.clone`. */
7699 var cloneableTags = {};
7700 cloneableTags[argsTag] = cloneableTags[arrayTag] =
7701 cloneableTags[arrayBufferTag] = cloneableTags[boolTag] =
7702 cloneableTags[dateTag] = cloneableTags[float32Tag] =
7703 cloneableTags[float64Tag] = cloneableTags[int8Tag] =
7704 cloneableTags[int16Tag] = cloneableTags[int32Tag] =
7705 cloneableTags[numberTag] = cloneableTags[objectTag] =
7706 cloneableTags[regexpTag] = cloneableTags[stringTag] =
7707 cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
7708 cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
7709 cloneableTags[errorTag] = cloneableTags[funcTag] =
7710 cloneableTags[mapTag] = cloneableTags[setTag] =
7711 cloneableTags[weakMapTag] = false;
7713 /** Used as an internal `_.debounce` options object by `_.throttle`. */
7714 var debounceOptions = {
7720 /** Used to determine if values are of the language type `Object`. */
7726 /** Detect free variable `exports`. */
7727 var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
7729 /** Detect free variable `module`. */
7730 var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
7732 /** Detect free variable `global` from Node.js. */
7733 var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global;
7735 /** Detect free variable `self`. */
7736 var freeSelf = objectTypes[typeof self] && self && self.Object && self;
7738 /** Detect free variable `window`. */
7739 var freeWindow = objectTypes[typeof window] && window && window.Object && window;
7741 /** Detect the popular CommonJS extension `module.exports`. */
7742 var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
7745 * Used as a reference to the global object.
7747 * The `this` value is used if it's the global object to avoid Greasemonkey's
7748 * restricted `window` object, otherwise the `window` object is used.
7750 var root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this;
7752 /*--------------------------------------------------------------------------*/
7755 * The base implementation of `_.findIndex` and `_.findLastIndex` without
7756 * support for callback shorthands and `this` binding.
7759 * @param {Array} array The array to search.
7760 * @param {Function} predicate The function invoked per iteration.
7761 * @param {boolean} [fromRight] Specify iterating from right to left.
7762 * @returns {number} Returns the index of the matched value, else `-1`.
7764 function baseFindIndex(array, predicate, fromRight) {
7765 var length = array.length,
7766 index = fromRight ? length : -1;
7768 while ((fromRight ? index-- : ++index < length)) {
7769 if (predicate(array[index], index, array)) {
7777 * The base implementation of `_.indexOf` without support for binary searches.
7780 * @param {Array} array The array to search.
7781 * @param {*} value The value to search for.
7782 * @param {number} fromIndex The index to search from.
7783 * @returns {number} Returns the index of the matched value, else `-1`.
7785 function baseIndexOf(array, value, fromIndex) {
7786 if (value !== value) {
7787 return indexOfNaN(array, fromIndex);
7789 var index = fromIndex - 1,
7790 length = array.length;
7792 while (++index < length) {
7793 if (array[index] === value) {
7801 * The base implementation of `_.isFunction` without support for environments
7802 * with incorrect `typeof` results.
7805 * @param {*} value The value to check.
7806 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
7808 function baseIsFunction(value) {
7809 // Avoid a Chakra JIT bug in compatibility modes of IE 11.
7810 // See https://github.com/jashkenas/underscore/issues/1621 for more details.
7811 return typeof value == 'function' || false;
7815 * Converts `value` to a string if it's not one. An empty string is returned
7816 * for `null` or `undefined` values.
7819 * @param {*} value The value to process.
7820 * @returns {string} Returns the string.
7822 function baseToString(value) {
7823 if (typeof value == 'string') {
7826 return value == null ? '' : (value + '');
7830 * Gets the index at which the first occurrence of `NaN` is found in `array`.
7833 * @param {Array} array The array to search.
7834 * @param {number} fromIndex The index to search from.
7835 * @param {boolean} [fromRight] Specify iterating from right to left.
7836 * @returns {number} Returns the index of the matched `NaN`, else `-1`.
7838 function indexOfNaN(array, fromIndex, fromRight) {
7839 var length = array.length,
7840 index = fromIndex + (fromRight ? 0 : -1);
7842 while ((fromRight ? index-- : ++index < length)) {
7843 var other = array[index];
7844 if (other !== other) {
7852 * Checks if `value` is a host object in IE < 9.
7855 * @param {*} value The value to check.
7856 * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
7858 var isHostObject = (function() {
7860 Object({ 'toString': 0 } + '');
7862 return function() { return false; };
7864 return function(value) {
7865 // IE < 9 presents many host objects as `Object` objects that can coerce
7866 // to strings despite having improperly defined `toString` methods.
7867 return typeof value.toString != 'function' && typeof (value + '') == 'string';
7872 * Checks if `value` is object-like.
7875 * @param {*} value The value to check.
7876 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
7878 function isObjectLike(value) {
7879 return !!value && typeof value == 'object';
7883 * Replaces all `placeholder` elements in `array` with an internal placeholder
7884 * and returns an array of their indexes.
7887 * @param {Array} array The array to modify.
7888 * @param {*} placeholder The placeholder to replace.
7889 * @returns {Array} Returns the new array of placeholder indexes.
7891 function replaceHolders(array, placeholder) {
7893 length = array.length,
7897 while (++index < length) {
7898 if (array[index] === placeholder) {
7899 array[index] = PLACEHOLDER;
7900 result[++resIndex] = index;
7907 * An implementation of `_.uniq` optimized for sorted arrays without support
7908 * for callback shorthands and `this` binding.
7911 * @param {Array} array The array to inspect.
7912 * @param {Function} [iteratee] The function invoked per iteration.
7913 * @returns {Array} Returns the new duplicate-value-free array.
7915 function sortedUniq(array, iteratee) {
7918 length = array.length,
7922 while (++index < length) {
7923 var value = array[index],
7924 computed = iteratee ? iteratee(value, index, array) : value;
7926 if (!index || seen !== computed) {
7928 result[++resIndex] = value;
7934 /*--------------------------------------------------------------------------*/
7936 /** Used for native method references. */
7937 var arrayProto = Array.prototype,
7938 errorProto = Error.prototype,
7939 objectProto = Object.prototype,
7940 stringProto = String.prototype;
7942 /** Used to resolve the decompiled source of functions. */
7943 var fnToString = Function.prototype.toString;
7945 /** Used to check objects for own properties. */
7946 var hasOwnProperty = objectProto.hasOwnProperty;
7949 * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
7952 var objToString = objectProto.toString;
7954 /** Used to detect if a method is native. */
7955 var reIsNative = RegExp('^' +
7956 escapeRegExp(fnToString.call(hasOwnProperty))
7957 .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
7960 /** Native method references. */
7961 var ArrayBuffer = getNative(root, 'ArrayBuffer'),
7962 bufferSlice = getNative(ArrayBuffer && new ArrayBuffer(0), 'slice'),
7965 getPrototypeOf = getNative(Object, 'getPrototypeOf'),
7966 push = arrayProto.push,
7967 propertyIsEnumerable = objectProto.propertyIsEnumerable,
7968 Set = getNative(root, 'Set'),
7969 splice = arrayProto.splice,
7970 Uint8Array = getNative(root, 'Uint8Array'),
7971 WeakMap = getNative(root, 'WeakMap');
7973 /** Used to clone array buffers. */
7974 var Float64Array = (function() {
7975 // Safari 5 errors when using an array buffer to initialize a typed array
7976 // where the array buffer's `byteLength` is not a multiple of the typed
7977 // array's `BYTES_PER_ELEMENT`.
7979 var func = getNative(root, 'Float64Array'),
7980 result = new func(new ArrayBuffer(10), 0, 1) && func;
7982 return result || null;
7985 /* Native method references for those with the same name as other `lodash` methods. */
7986 var nativeCreate = getNative(Object, 'create'),
7987 nativeIsArray = getNative(Array, 'isArray'),
7988 nativeKeys = getNative(Object, 'keys'),
7989 nativeMax = Math.max,
7990 nativeMin = Math.min,
7991 nativeNow = getNative(Date, 'now');
7993 /** Used as references for `-Infinity` and `Infinity`. */
7994 var POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
7996 /** Used as references for the maximum length and index of an array. */
7997 var MAX_ARRAY_LENGTH = 4294967295,
7998 MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
7999 HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
8001 /** Used as the size, in bytes, of each `Float64Array` element. */
8002 var FLOAT64_BYTES_PER_ELEMENT = Float64Array ? Float64Array.BYTES_PER_ELEMENT : 0;
8005 * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer)
8006 * of an array-like value.
8008 var MAX_SAFE_INTEGER = 9007199254740991;
8010 /** Used to store function metadata. */
8011 var metaMap = WeakMap && new WeakMap;
8013 /** Used to lookup unminified function names. */
8016 /** Used to lookup a type array constructors by `toStringTag`. */
8018 ctorByTag[float32Tag] = root.Float32Array;
8019 ctorByTag[float64Tag] = root.Float64Array;
8020 ctorByTag[int8Tag] = root.Int8Array;
8021 ctorByTag[int16Tag] = root.Int16Array;
8022 ctorByTag[int32Tag] = root.Int32Array;
8023 ctorByTag[uint8Tag] = root.Uint8Array;
8024 ctorByTag[uint8ClampedTag] = root.Uint8ClampedArray;
8025 ctorByTag[uint16Tag] = root.Uint16Array;
8026 ctorByTag[uint32Tag] = root.Uint32Array;
8028 /** Used to avoid iterating over non-enumerable properties in IE < 9. */
8029 var nonEnumProps = {};
8030 nonEnumProps[arrayTag] = nonEnumProps[dateTag] = nonEnumProps[numberTag] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
8031 nonEnumProps[boolTag] = nonEnumProps[stringTag] = { 'constructor': true, 'toString': true, 'valueOf': true };
8032 nonEnumProps[errorTag] = nonEnumProps[funcTag] = nonEnumProps[regexpTag] = { 'constructor': true, 'toString': true };
8033 nonEnumProps[objectTag] = { 'constructor': true };
8035 arrayEach(shadowProps, function(key) {
8036 for (var tag in nonEnumProps) {
8037 if (hasOwnProperty.call(nonEnumProps, tag)) {
8038 var props = nonEnumProps[tag];
8039 props[key] = hasOwnProperty.call(props, key);
8044 /*------------------------------------------------------------------------*/
8047 * Creates a `lodash` object which wraps `value` to enable implicit chaining.
8048 * Methods that operate on and return arrays, collections, and functions can
8049 * be chained together. Methods that return a boolean or single value will
8050 * automatically end the chain returning the unwrapped value. Explicit chaining
8051 * may be enabled using `_.chain`. The execution of chained methods is lazy,
8052 * that is, execution is deferred until `_#value` is implicitly or explicitly
8055 * Lazy evaluation allows several methods to support shortcut fusion. Shortcut
8056 * fusion is an optimization that merges iteratees to avoid creating intermediate
8057 * arrays and reduce the number of iteratee executions.
8059 * Chaining is supported in custom builds as long as the `_#value` method is
8060 * directly or indirectly included in the build.
8062 * In addition to lodash methods, wrappers have `Array` and `String` methods.
8064 * The wrapper `Array` methods are:
8065 * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`,
8066 * `splice`, and `unshift`
8068 * The wrapper `String` methods are:
8069 * `replace` and `split`
8071 * The wrapper methods that support shortcut fusion are:
8072 * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`,
8073 * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`,
8074 * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`,
8077 * The chainable wrapper methods are:
8078 * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`,
8079 * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`,
8080 * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defer`, `delay`,
8081 * `difference`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `fill`,
8082 * `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, `forEach`,
8083 * `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `functions`,
8084 * `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, `invoke`, `keys`,
8085 * `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
8086 * `memoize`, `merge`, `method`, `methodOf`, `mixin`, `negate`, `omit`, `once`,
8087 * `pairs`, `partial`, `partialRight`, `partition`, `pick`, `plant`, `pluck`,
8088 * `property`, `propertyOf`, `pull`, `pullAt`, `push`, `range`, `rearg`,
8089 * `reject`, `remove`, `rest`, `restParam`, `reverse`, `set`, `shuffle`,
8090 * `slice`, `sort`, `sortBy`, `sortByAll`, `sortByOrder`, `splice`, `spread`,
8091 * `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `tap`, `throttle`,
8092 * `thru`, `times`, `toArray`, `toPlainObject`, `transform`, `union`, `uniq`,
8093 * `unshift`, `unzip`, `unzipWith`, `values`, `valuesIn`, `where`, `without`,
8094 * `wrap`, `xor`, `zip`, `zipObject`, `zipWith`
8096 * The wrapper methods that are **not** chainable by default are:
8097 * `add`, `attempt`, `camelCase`, `capitalize`, `clone`, `cloneDeep`, `deburr`,
8098 * `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`,
8099 * `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, `get`,
8100 * `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`, `inRange`, `isArguments`,
8101 * `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`,
8102 * `isFinite` `isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`,
8103 * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`,
8104 * `isTypedArray`, `join`, `kebabCase`, `last`, `lastIndexOf`, `lt`, `lte`,
8105 * `max`, `min`, `noConflict`, `noop`, `now`, `pad`, `padLeft`, `padRight`,
8106 * `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`,
8107 * `runInContext`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`,
8108 * `sortedLastIndex`, `startCase`, `startsWith`, `sum`, `template`, `trim`,
8109 * `trimLeft`, `trimRight`, `trunc`, `unescape`, `uniqueId`, `value`, and `words`
8111 * The wrapper method `sample` will return a wrapped value when `n` is provided,
8112 * otherwise an unwrapped value is returned.
8117 * @param {*} value The value to wrap in a `lodash` instance.
8118 * @returns {Object} Returns the new `lodash` wrapper instance.
8121 * var wrapped = _([1, 2, 3]);
8123 * // returns an unwrapped value
8124 * wrapped.reduce(function(total, n) {
8129 * // returns a wrapped value
8130 * var squares = wrapped.map(function(n) {
8134 * _.isArray(squares);
8137 * _.isArray(squares.value());
8140 function lodash(value) {
8141 if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
8142 if (value instanceof LodashWrapper) {
8145 if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) {
8146 return wrapperClone(value);
8149 return new LodashWrapper(value);
8153 * The function whose prototype all chaining wrappers inherit from.
8157 function baseLodash() {
8158 // No operation performed.
8162 * The base constructor for creating `lodash` wrapper objects.
8165 * @param {*} value The value to wrap.
8166 * @param {boolean} [chainAll] Enable chaining for all wrapper methods.
8167 * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value.
8169 function LodashWrapper(value, chainAll, actions) {
8170 this.__wrapped__ = value;
8171 this.__actions__ = actions || [];
8172 this.__chain__ = !!chainAll;
8176 * An object environment feature flags.
8182 var support = lodash.support = {};
8185 var Ctor = function() { this.x = x; },
8186 object = { '0': x, 'length': x },
8189 Ctor.prototype = { 'valueOf': x, 'y': x };
8190 for (var key in new Ctor) { props.push(key); }
8193 * Detect if the `toStringTag` of `arguments` objects is resolvable
8194 * (all but Firefox < 4, IE < 9).
8196 * @memberOf _.support
8199 support.argsTag = objToString.call(arguments) == argsTag;
8202 * Detect if `name` or `message` properties of `Error.prototype` are
8203 * enumerable by default (IE < 9, Safari < 5.1).
8205 * @memberOf _.support
8208 support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') ||
8209 propertyIsEnumerable.call(errorProto, 'name');
8212 * Detect if `prototype` properties are enumerable by default.
8214 * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
8215 * (if the prototype or a property on the prototype has been set)
8216 * incorrectly set the `[[Enumerable]]` value of a function's `prototype`
8217 * property to `true`.
8219 * @memberOf _.support
8222 support.enumPrototypes = propertyIsEnumerable.call(Ctor, 'prototype');
8225 * Detect if properties shadowing those on `Object.prototype` are non-enumerable.
8227 * In IE < 9 an object's own properties, shadowing non-enumerable ones,
8228 * are made non-enumerable as well (a.k.a the JScript `[[DontEnum]]` bug).
8230 * @memberOf _.support
8233 support.nonEnumShadows = !/valueOf/.test(props);
8236 * Detect if own properties are iterated after inherited properties (IE < 9).
8238 * @memberOf _.support
8241 support.ownLast = props[0] != 'x';
8244 * Detect if `Array#shift` and `Array#splice` augment array-like objects
8247 * Firefox < 10, compatibility modes of IE 8, and IE < 9 have buggy Array
8248 * `shift()` and `splice()` functions that fail to remove the last element,
8249 * `value[0]`, of array-like objects even though the "length" property is
8250 * set to `0`. The `shift()` method is buggy in compatibility modes of IE 8,
8251 * while `splice()` is buggy regardless of mode in IE < 9.
8253 * @memberOf _.support
8256 support.spliceObjects = (splice.call(object, 0, 1), !object[0]);
8259 * Detect lack of support for accessing string characters by index.
8261 * IE < 8 can't access characters by index. IE 8 can only access characters
8262 * by index on string literals, not string objects.
8264 * @memberOf _.support
8267 support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
8270 /*------------------------------------------------------------------------*/
8273 * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
8276 * @param {*} value The value to wrap.
8278 function LazyWrapper(value) {
8279 this.__wrapped__ = value;
8280 this.__actions__ = null;
8282 this.__dropCount__ = 0;
8283 this.__filtered__ = false;
8284 this.__iteratees__ = null;
8285 this.__takeCount__ = POSITIVE_INFINITY;
8286 this.__views__ = null;
8290 * Creates a clone of the lazy wrapper object.
8294 * @memberOf LazyWrapper
8295 * @returns {Object} Returns the cloned `LazyWrapper` object.
8297 function lazyClone() {
8298 var actions = this.__actions__,
8299 iteratees = this.__iteratees__,
8300 views = this.__views__,
8301 result = new LazyWrapper(this.__wrapped__);
8303 result.__actions__ = actions ? arrayCopy(actions) : null;
8304 result.__dir__ = this.__dir__;
8305 result.__filtered__ = this.__filtered__;
8306 result.__iteratees__ = iteratees ? arrayCopy(iteratees) : null;
8307 result.__takeCount__ = this.__takeCount__;
8308 result.__views__ = views ? arrayCopy(views) : null;
8313 * Reverses the direction of lazy iteration.
8317 * @memberOf LazyWrapper
8318 * @returns {Object} Returns the new reversed `LazyWrapper` object.
8320 function lazyReverse() {
8321 if (this.__filtered__) {
8322 var result = new LazyWrapper(this);
8323 result.__dir__ = -1;
8324 result.__filtered__ = true;
8326 result = this.clone();
8327 result.__dir__ *= -1;
8333 * Extracts the unwrapped value from its lazy wrapper.
8337 * @memberOf LazyWrapper
8338 * @returns {*} Returns the unwrapped value.
8340 function lazyValue() {
8341 var array = this.__wrapped__.value();
8342 if (!isArray(array)) {
8343 return baseWrapperValue(array, this.__actions__);
8345 var dir = this.__dir__,
8347 view = getView(0, array.length, this.__views__),
8350 length = end - start,
8351 index = isRight ? end : (start - 1),
8352 takeCount = nativeMin(length, this.__takeCount__),
8353 iteratees = this.__iteratees__,
8354 iterLength = iteratees ? iteratees.length : 0,
8359 while (length-- && resIndex < takeCount) {
8363 value = array[index];
8365 while (++iterIndex < iterLength) {
8366 var data = iteratees[iterIndex],
8367 iteratee = data.iteratee,
8370 if (type == LAZY_DROP_WHILE_FLAG) {
8371 if (data.done && (isRight ? (index > data.index) : (index < data.index))) {
8377 var limit = data.limit;
8378 if (!(data.done = limit > -1 ? (data.count++ >= limit) : !iteratee(value))) {
8383 var computed = iteratee(value);
8384 if (type == LAZY_MAP_FLAG) {
8386 } else if (!computed) {
8387 if (type == LAZY_FILTER_FLAG) {
8395 result[resIndex++] = value;
8400 /*------------------------------------------------------------------------*/
8404 * Creates a cache object to store unique values.
8407 * @param {Array} [values] The values to cache.
8409 function SetCache(values) {
8410 var length = values ? values.length : 0;
8412 this.data = { 'hash': nativeCreate(null), 'set': new Set };
8414 this.push(values[length]);
8419 * Checks if `value` is in `cache` mimicking the return signature of
8420 * `_.indexOf` by returning `0` if the value is found, else `-1`.
8423 * @param {Object} cache The cache to search.
8424 * @param {*} value The value to search for.
8425 * @returns {number} Returns `0` if `value` is found, else `-1`.
8427 function cacheIndexOf(cache, value) {
8428 var data = cache.data,
8429 result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value];
8431 return result ? 0 : -1;
8435 * Adds `value` to the cache.
8439 * @memberOf SetCache
8440 * @param {*} value The value to cache.
8442 function cachePush(value) {
8443 var data = this.data;
8444 if (typeof value == 'string' || isObject(value)) {
8445 data.set.add(value);
8447 data.hash[value] = true;
8451 /*------------------------------------------------------------------------*/
8454 * Copies the values of `source` to `array`.
8457 * @param {Array} source The array to copy values from.
8458 * @param {Array} [array=[]] The array to copy values to.
8459 * @returns {Array} Returns `array`.
8461 function arrayCopy(source, array) {
8463 length = source.length;
8465 array || (array = Array(length));
8466 while (++index < length) {
8467 array[index] = source[index];
8473 * A specialized version of `_.forEach` for arrays without support for callback
8474 * shorthands and `this` binding.
8477 * @param {Array} array The array to iterate over.
8478 * @param {Function} iteratee The function invoked per iteration.
8479 * @returns {Array} Returns `array`.
8481 function arrayEach(array, iteratee) {
8483 length = array.length;
8485 while (++index < length) {
8486 if (iteratee(array[index], index, array) === false) {
8494 * A specialized version of `_.every` for arrays without support for callback
8495 * shorthands and `this` binding.
8498 * @param {Array} array The array to iterate over.
8499 * @param {Function} predicate The function invoked per iteration.
8500 * @returns {boolean} Returns `true` if all elements pass the predicate check,
8503 function arrayEvery(array, predicate) {
8505 length = array.length;
8507 while (++index < length) {
8508 if (!predicate(array[index], index, array)) {
8516 * A specialized version of `_.filter` for arrays without support for callback
8517 * shorthands and `this` binding.
8520 * @param {Array} array The array to iterate over.
8521 * @param {Function} predicate The function invoked per iteration.
8522 * @returns {Array} Returns the new filtered array.
8524 function arrayFilter(array, predicate) {
8526 length = array.length,
8530 while (++index < length) {
8531 var value = array[index];
8532 if (predicate(value, index, array)) {
8533 result[++resIndex] = value;
8540 * A specialized version of `_.map` for arrays without support for callback
8541 * shorthands and `this` binding.
8544 * @param {Array} array The array to iterate over.
8545 * @param {Function} iteratee The function invoked per iteration.
8546 * @returns {Array} Returns the new mapped array.
8548 function arrayMap(array, iteratee) {
8550 length = array.length,
8551 result = Array(length);
8553 while (++index < length) {
8554 result[index] = iteratee(array[index], index, array);
8560 * A specialized version of `_.reduce` for arrays without support for callback
8561 * shorthands and `this` binding.
8564 * @param {Array} array The array to iterate over.
8565 * @param {Function} iteratee The function invoked per iteration.
8566 * @param {*} [accumulator] The initial value.
8567 * @param {boolean} [initFromArray] Specify using the first element of `array`
8568 * as the initial value.
8569 * @returns {*} Returns the accumulated value.
8571 function arrayReduce(array, iteratee, accumulator, initFromArray) {
8573 length = array.length;
8575 if (initFromArray && length) {
8576 accumulator = array[++index];
8578 while (++index < length) {
8579 accumulator = iteratee(accumulator, array[index], index, array);
8585 * A specialized version of `_.some` for arrays without support for callback
8586 * shorthands and `this` binding.
8589 * @param {Array} array The array to iterate over.
8590 * @param {Function} predicate The function invoked per iteration.
8591 * @returns {boolean} Returns `true` if any element passes the predicate check,
8594 function arraySome(array, predicate) {
8596 length = array.length;
8598 while (++index < length) {
8599 if (predicate(array[index], index, array)) {
8607 * A specialized version of `_.assign` for customizing assigned values without
8608 * support for argument juggling, multiple sources, and `this` binding `customizer`
8612 * @param {Object} object The destination object.
8613 * @param {Object} source The source object.
8614 * @param {Function} customizer The function to customize assigned values.
8615 * @returns {Object} Returns `object`.
8617 function assignWith(object, source, customizer) {
8619 props = keys(source),
8620 length = props.length;
8622 while (++index < length) {
8623 var key = props[index],
8624 value = object[key],
8625 result = customizer(value, source[key], key, object, source);
8627 if ((result === result ? (result !== value) : (value === value)) ||
8628 (value === undefined && !(key in object))) {
8629 object[key] = result;
8636 * The base implementation of `_.assign` without support for argument juggling,
8637 * multiple sources, and `customizer` functions.
8640 * @param {Object} object The destination object.
8641 * @param {Object} source The source object.
8642 * @returns {Object} Returns `object`.
8644 function baseAssign(object, source) {
8645 return source == null
8647 : baseCopy(source, keys(source), object);
8651 * Copies properties of `source` to `object`.
8654 * @param {Object} source The object to copy properties from.
8655 * @param {Array} props The property names to copy.
8656 * @param {Object} [object={}] The object to copy properties to.
8657 * @returns {Object} Returns `object`.
8659 function baseCopy(source, props, object) {
8660 object || (object = {});
8663 length = props.length;
8665 while (++index < length) {
8666 var key = props[index];
8667 object[key] = source[key];
8673 * The base implementation of `_.callback` which supports specifying the
8674 * number of arguments to provide to `func`.
8677 * @param {*} [func=_.identity] The value to convert to a callback.
8678 * @param {*} [thisArg] The `this` binding of `func`.
8679 * @param {number} [argCount] The number of arguments to provide to `func`.
8680 * @returns {Function} Returns the callback.
8682 function baseCallback(func, thisArg, argCount) {
8683 var type = typeof func;
8684 if (type == 'function') {
8685 return thisArg === undefined
8687 : bindCallback(func, thisArg, argCount);
8692 if (type == 'object') {
8693 return baseMatches(func);
8695 return thisArg === undefined
8697 : baseMatchesProperty(func, thisArg);
8701 * The base implementation of `_.clone` without support for argument juggling
8702 * and `this` binding `customizer` functions.
8705 * @param {*} value The value to clone.
8706 * @param {boolean} [isDeep] Specify a deep clone.
8707 * @param {Function} [customizer] The function to customize cloning values.
8708 * @param {string} [key] The key of `value`.
8709 * @param {Object} [object] The object `value` belongs to.
8710 * @param {Array} [stackA=[]] Tracks traversed source objects.
8711 * @param {Array} [stackB=[]] Associates clones with source counterparts.
8712 * @returns {*} Returns the cloned value.
8714 function baseClone(value, isDeep, customizer, key, object, stackA, stackB) {
8717 result = object ? customizer(value, key, object) : customizer(value);
8719 if (result !== undefined) {
8722 if (!isObject(value)) {
8725 var isArr = isArray(value);
8727 result = initCloneArray(value);
8729 return arrayCopy(value, result);
8732 var tag = objToString.call(value),
8733 isFunc = tag == funcTag;
8735 if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
8736 if (isHostObject(value)) {
8737 return object ? value : {};
8739 result = initCloneObject(isFunc ? {} : value);
8741 return baseAssign(result, value);
8744 return cloneableTags[tag]
8745 ? initCloneByTag(value, tag, isDeep)
8746 : (object ? value : {});
8749 // Check for circular references and return corresponding clone.
8750 stackA || (stackA = []);
8751 stackB || (stackB = []);
8753 var length = stackA.length;
8755 if (stackA[length] == value) {
8756 return stackB[length];
8759 // Add the source value to the stack of traversed objects and associate it with its clone.
8761 stackB.push(result);
8763 // Recursively populate clone (susceptible to call stack limits).
8764 (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) {
8765 result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB);
8771 * The base implementation of `_.create` without support for assigning
8772 * properties to the created object.
8775 * @param {Object} prototype The object to inherit from.
8776 * @returns {Object} Returns the new object.
8778 var baseCreate = (function() {
8779 function object() {}
8780 return function(prototype) {
8781 if (isObject(prototype)) {
8782 object.prototype = prototype;
8783 var result = new object;
8784 object.prototype = null;
8786 return result || {};
8791 * The base implementation of `_.difference` which accepts a single array
8792 * of values to exclude.
8795 * @param {Array} array The array to inspect.
8796 * @param {Array} values The values to exclude.
8797 * @returns {Array} Returns the new array of filtered values.
8799 function baseDifference(array, values) {
8800 var length = array ? array.length : 0,
8807 indexOf = getIndexOf(),
8808 isCommon = indexOf == baseIndexOf,
8809 cache = (isCommon && values.length >= 200) ? createCache(values) : null,
8810 valuesLength = values.length;
8813 indexOf = cacheIndexOf;
8818 while (++index < length) {
8819 var value = array[index];
8821 if (isCommon && value === value) {
8822 var valuesIndex = valuesLength;
8823 while (valuesIndex--) {
8824 if (values[valuesIndex] === value) {
8830 else if (indexOf(values, value, 0) < 0) {
8838 * The base implementation of `_.forEach` without support for callback
8839 * shorthands and `this` binding.
8842 * @param {Array|Object|string} collection The collection to iterate over.
8843 * @param {Function} iteratee The function invoked per iteration.
8844 * @returns {Array|Object|string} Returns `collection`.
8846 var baseEach = createBaseEach(baseForOwn);
8849 * The base implementation of `_.every` without support for callback
8850 * shorthands and `this` binding.
8853 * @param {Array|Object|string} collection The collection to iterate over.
8854 * @param {Function} predicate The function invoked per iteration.
8855 * @returns {boolean} Returns `true` if all elements pass the predicate check,
8858 function baseEvery(collection, predicate) {
8860 baseEach(collection, function(value, index, collection) {
8861 result = !!predicate(value, index, collection);
8868 * The base implementation of `_.filter` without support for callback
8869 * shorthands and `this` binding.
8872 * @param {Array|Object|string} collection The collection to iterate over.
8873 * @param {Function} predicate The function invoked per iteration.
8874 * @returns {Array} Returns the new filtered array.
8876 function baseFilter(collection, predicate) {
8878 baseEach(collection, function(value, index, collection) {
8879 if (predicate(value, index, collection)) {
8887 * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`,
8888 * without support for callback shorthands and `this` binding, which iterates
8889 * over `collection` using the provided `eachFunc`.
8892 * @param {Array|Object|string} collection The collection to search.
8893 * @param {Function} predicate The function invoked per iteration.
8894 * @param {Function} eachFunc The function to iterate over `collection`.
8895 * @param {boolean} [retKey] Specify returning the key of the found element
8896 * instead of the element itself.
8897 * @returns {*} Returns the found element or its key, else `undefined`.
8899 function baseFind(collection, predicate, eachFunc, retKey) {
8901 eachFunc(collection, function(value, key, collection) {
8902 if (predicate(value, key, collection)) {
8903 result = retKey ? key : value;
8911 * The base implementation of `_.flatten` with added support for restricting
8912 * flattening and specifying the start index.
8915 * @param {Array} array The array to flatten.
8916 * @param {boolean} [isDeep] Specify a deep flatten.
8917 * @param {boolean} [isStrict] Restrict flattening to arrays-like objects.
8918 * @returns {Array} Returns the new flattened array.
8920 function baseFlatten(array, isDeep, isStrict) {
8922 length = array.length,
8926 while (++index < length) {
8927 var value = array[index];
8928 if (isObjectLike(value) && isArrayLike(value) &&
8929 (isStrict || isArray(value) || isArguments(value))) {
8931 // Recursively flatten arrays (susceptible to call stack limits).
8932 value = baseFlatten(value, isDeep, isStrict);
8935 valLength = value.length;
8937 while (++valIndex < valLength) {
8938 result[++resIndex] = value[valIndex];
8940 } else if (!isStrict) {
8941 result[++resIndex] = value;
8948 * The base implementation of `baseForIn` and `baseForOwn` which iterates
8949 * over `object` properties returned by `keysFunc` invoking `iteratee` for
8950 * each property. Iteratee functions may exit iteration early by explicitly
8951 * returning `false`.
8954 * @param {Object} object The object to iterate over.
8955 * @param {Function} iteratee The function invoked per iteration.
8956 * @param {Function} keysFunc The function to get the keys of `object`.
8957 * @returns {Object} Returns `object`.
8959 var baseFor = createBaseFor();
8962 * The base implementation of `_.forIn` without support for callback
8963 * shorthands and `this` binding.
8966 * @param {Object} object The object to iterate over.
8967 * @param {Function} iteratee The function invoked per iteration.
8968 * @returns {Object} Returns `object`.
8970 function baseForIn(object, iteratee) {
8971 return baseFor(object, iteratee, keysIn);
8975 * The base implementation of `_.forOwn` without support for callback
8976 * shorthands and `this` binding.
8979 * @param {Object} object The object to iterate over.
8980 * @param {Function} iteratee The function invoked per iteration.
8981 * @returns {Object} Returns `object`.
8983 function baseForOwn(object, iteratee) {
8984 return baseFor(object, iteratee, keys);
8988 * The base implementation of `_.functions` which creates an array of
8989 * `object` function property names filtered from those provided.
8992 * @param {Object} object The object to inspect.
8993 * @param {Array} props The property names to filter.
8994 * @returns {Array} Returns the new array of filtered property names.
8996 function baseFunctions(object, props) {
8998 length = props.length,
9002 while (++index < length) {
9003 var key = props[index];
9004 if (isFunction(object[key])) {
9005 result[++resIndex] = key;
9012 * The base implementation of `get` without support for string paths
9013 * and default values.
9016 * @param {Object} object The object to query.
9017 * @param {Array} path The path of the property to get.
9018 * @param {string} [pathKey] The key representation of path.
9019 * @returns {*} Returns the resolved value.
9021 function baseGet(object, path, pathKey) {
9022 if (object == null) {
9025 object = toObject(object);
9026 if (pathKey !== undefined && pathKey in object) {
9030 length = path.length;
9032 while (object != null && index < length) {
9033 object = toObject(object)[path[index++]];
9035 return (index && index == length) ? object : undefined;
9039 * The base implementation of `_.isEqual` without support for `this` binding
9040 * `customizer` functions.
9043 * @param {*} value The value to compare.
9044 * @param {*} other The other value to compare.
9045 * @param {Function} [customizer] The function to customize comparing values.
9046 * @param {boolean} [isLoose] Specify performing partial comparisons.
9047 * @param {Array} [stackA] Tracks traversed `value` objects.
9048 * @param {Array} [stackB] Tracks traversed `other` objects.
9049 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
9051 function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) {
9052 if (value === other) {
9055 if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
9056 return value !== value && other !== other;
9058 return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB);
9062 * A specialized version of `baseIsEqual` for arrays and objects which performs
9063 * deep comparisons and tracks traversed objects enabling objects with circular
9064 * references to be compared.
9067 * @param {Object} object The object to compare.
9068 * @param {Object} other The other object to compare.
9069 * @param {Function} equalFunc The function to determine equivalents of values.
9070 * @param {Function} [customizer] The function to customize comparing objects.
9071 * @param {boolean} [isLoose] Specify performing partial comparisons.
9072 * @param {Array} [stackA=[]] Tracks traversed `value` objects.
9073 * @param {Array} [stackB=[]] Tracks traversed `other` objects.
9074 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9076 function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
9077 var objIsArr = isArray(object),
9078 othIsArr = isArray(other),
9083 objTag = objToString.call(object);
9084 if (objTag == argsTag) {
9086 } else if (objTag != objectTag) {
9087 objIsArr = isTypedArray(object);
9091 othTag = objToString.call(other);
9092 if (othTag == argsTag) {
9094 } else if (othTag != objectTag) {
9095 othIsArr = isTypedArray(other);
9098 var objIsObj = objTag == objectTag && !isHostObject(object),
9099 othIsObj = othTag == objectTag && !isHostObject(other),
9100 isSameTag = objTag == othTag;
9102 if (isSameTag && !(objIsArr || objIsObj)) {
9103 return equalByTag(object, other, objTag);
9106 var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
9107 othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
9109 if (objIsWrapped || othIsWrapped) {
9110 return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB);
9116 // Assume cyclic values are equal.
9117 // For more information on detecting circular references see https://es5.github.io/#JO.
9118 stackA || (stackA = []);
9119 stackB || (stackB = []);
9121 var length = stackA.length;
9123 if (stackA[length] == object) {
9124 return stackB[length] == other;
9127 // Add `object` and `other` to the stack of traversed objects.
9128 stackA.push(object);
9131 var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB);
9140 * The base implementation of `_.isMatch` without support for callback
9141 * shorthands and `this` binding.
9144 * @param {Object} object The object to inspect.
9145 * @param {Array} matchData The propery names, values, and compare flags to match.
9146 * @param {Function} [customizer] The function to customize comparing objects.
9147 * @returns {boolean} Returns `true` if `object` is a match, else `false`.
9149 function baseIsMatch(object, matchData, customizer) {
9150 var index = matchData.length,
9152 noCustomizer = !customizer;
9154 if (object == null) {
9157 object = toObject(object);
9159 var data = matchData[index];
9160 if ((noCustomizer && data[2])
9161 ? data[1] !== object[data[0]]
9162 : !(data[0] in object)
9167 while (++index < length) {
9168 data = matchData[index];
9170 objValue = object[key],
9173 if (noCustomizer && data[2]) {
9174 if (objValue === undefined && !(key in object)) {
9178 var result = customizer ? customizer(objValue, srcValue, key) : undefined;
9179 if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, true) : result)) {
9188 * The base implementation of `_.map` without support for callback shorthands
9189 * and `this` binding.
9192 * @param {Array|Object|string} collection The collection to iterate over.
9193 * @param {Function} iteratee The function invoked per iteration.
9194 * @returns {Array} Returns the new mapped array.
9196 function baseMap(collection, iteratee) {
9198 result = isArrayLike(collection) ? Array(collection.length) : [];
9200 baseEach(collection, function(value, key, collection) {
9201 result[++index] = iteratee(value, key, collection);
9207 * The base implementation of `_.matches` which does not clone `source`.
9210 * @param {Object} source The object of property values to match.
9211 * @returns {Function} Returns the new function.
9213 function baseMatches(source) {
9214 var matchData = getMatchData(source);
9215 if (matchData.length == 1 && matchData[0][2]) {
9216 var key = matchData[0][0],
9217 value = matchData[0][1];
9219 return function(object) {
9220 if (object == null) {
9223 object = toObject(object);
9224 return object[key] === value && (value !== undefined || (key in object));
9227 return function(object) {
9228 return baseIsMatch(object, matchData);
9233 * The base implementation of `_.matchesProperty` which does not clone `srcValue`.
9236 * @param {string} path The path of the property to get.
9237 * @param {*} srcValue The value to compare.
9238 * @returns {Function} Returns the new function.
9240 function baseMatchesProperty(path, srcValue) {
9241 var isArr = isArray(path),
9242 isCommon = isKey(path) && isStrictComparable(srcValue),
9243 pathKey = (path + '');
9245 path = toPath(path);
9246 return function(object) {
9247 if (object == null) {
9251 object = toObject(object);
9252 if ((isArr || !isCommon) && !(key in object)) {
9253 object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
9254 if (object == null) {
9258 object = toObject(object);
9260 return object[key] === srcValue
9261 ? (srcValue !== undefined || (key in object))
9262 : baseIsEqual(srcValue, object[key], undefined, true);
9267 * The base implementation of `_.merge` without support for argument juggling,
9268 * multiple sources, and `this` binding `customizer` functions.
9271 * @param {Object} object The destination object.
9272 * @param {Object} source The source object.
9273 * @param {Function} [customizer] The function to customize merging properties.
9274 * @param {Array} [stackA=[]] Tracks traversed source objects.
9275 * @param {Array} [stackB=[]] Associates values with source counterparts.
9276 * @returns {Object} Returns `object`.
9278 function baseMerge(object, source, customizer, stackA, stackB) {
9279 if (!isObject(object)) {
9282 var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)),
9283 props = isSrcArr ? null : keys(source);
9285 arrayEach(props || source, function(srcValue, key) {
9288 srcValue = source[key];
9290 if (isObjectLike(srcValue)) {
9291 stackA || (stackA = []);
9292 stackB || (stackB = []);
9293 baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
9296 var value = object[key],
9297 result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
9298 isCommon = result === undefined;
9303 if ((result !== undefined || (isSrcArr && !(key in object))) &&
9304 (isCommon || (result === result ? (result !== value) : (value === value)))) {
9305 object[key] = result;
9313 * A specialized version of `baseMerge` for arrays and objects which performs
9314 * deep merges and tracks traversed objects enabling objects with circular
9315 * references to be merged.
9318 * @param {Object} object The destination object.
9319 * @param {Object} source The source object.
9320 * @param {string} key The key of the value to merge.
9321 * @param {Function} mergeFunc The function to merge values.
9322 * @param {Function} [customizer] The function to customize merging properties.
9323 * @param {Array} [stackA=[]] Tracks traversed source objects.
9324 * @param {Array} [stackB=[]] Associates values with source counterparts.
9325 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9327 function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) {
9328 var length = stackA.length,
9329 srcValue = source[key];
9332 if (stackA[length] == srcValue) {
9333 object[key] = stackB[length];
9337 var value = object[key],
9338 result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
9339 isCommon = result === undefined;
9343 if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) {
9344 result = isArray(value)
9346 : (isArrayLike(value) ? arrayCopy(value) : []);
9348 else if (isPlainObject(srcValue) || isArguments(srcValue)) {
9349 result = isArguments(value)
9350 ? toPlainObject(value)
9351 : (isPlainObject(value) ? value : {});
9357 // Add the source value to the stack of traversed objects and associate
9358 // it with its merged value.
9359 stackA.push(srcValue);
9360 stackB.push(result);
9363 // Recursively merge objects and arrays (susceptible to call stack limits).
9364 object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB);
9365 } else if (result === result ? (result !== value) : (value === value)) {
9366 object[key] = result;
9371 * The base implementation of `_.property` without support for deep paths.
9374 * @param {string} key The key of the property to get.
9375 * @returns {Function} Returns the new function.
9377 function baseProperty(key) {
9378 return function(object) {
9379 return object == null ? undefined : toObject(object)[key];
9384 * A specialized version of `baseProperty` which supports deep paths.
9387 * @param {Array|string} path The path of the property to get.
9388 * @returns {Function} Returns the new function.
9390 function basePropertyDeep(path) {
9391 var pathKey = (path + '');
9392 path = toPath(path);
9393 return function(object) {
9394 return baseGet(object, path, pathKey);
9399 * The base implementation of `_.reduce` and `_.reduceRight` without support
9400 * for callback shorthands and `this` binding, which iterates over `collection`
9401 * using the provided `eachFunc`.
9404 * @param {Array|Object|string} collection The collection to iterate over.
9405 * @param {Function} iteratee The function invoked per iteration.
9406 * @param {*} accumulator The initial value.
9407 * @param {boolean} initFromCollection Specify using the first or last element
9408 * of `collection` as the initial value.
9409 * @param {Function} eachFunc The function to iterate over `collection`.
9410 * @returns {*} Returns the accumulated value.
9412 function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) {
9413 eachFunc(collection, function(value, index, collection) {
9414 accumulator = initFromCollection
9415 ? (initFromCollection = false, value)
9416 : iteratee(accumulator, value, index, collection);
9422 * The base implementation of `setData` without support for hot loop detection.
9425 * @param {Function} func The function to associate metadata with.
9426 * @param {*} data The metadata.
9427 * @returns {Function} Returns `func`.
9429 var baseSetData = !metaMap ? identity : function(func, data) {
9430 metaMap.set(func, data);
9435 * The base implementation of `_.slice` without an iteratee call guard.
9438 * @param {Array} array The array to slice.
9439 * @param {number} [start=0] The start position.
9440 * @param {number} [end=array.length] The end position.
9441 * @returns {Array} Returns the slice of `array`.
9443 function baseSlice(array, start, end) {
9445 length = array.length;
9447 start = start == null ? 0 : (+start || 0);
9449 start = -start > length ? 0 : (length + start);
9451 end = (end === undefined || end > length) ? length : (+end || 0);
9455 length = start > end ? 0 : ((end - start) >>> 0);
9458 var result = Array(length);
9459 while (++index < length) {
9460 result[index] = array[index + start];
9466 * The base implementation of `_.some` without support for callback shorthands
9467 * and `this` binding.
9470 * @param {Array|Object|string} collection The collection to iterate over.
9471 * @param {Function} predicate The function invoked per iteration.
9472 * @returns {boolean} Returns `true` if any element passes the predicate check,
9475 function baseSome(collection, predicate) {
9478 baseEach(collection, function(value, index, collection) {
9479 result = predicate(value, index, collection);
9486 * The base implementation of `_.uniq` without support for callback shorthands
9487 * and `this` binding.
9490 * @param {Array} array The array to inspect.
9491 * @param {Function} [iteratee] The function invoked per iteration.
9492 * @returns {Array} Returns the new duplicate-value-free array.
9494 function baseUniq(array, iteratee) {
9496 indexOf = getIndexOf(),
9497 length = array.length,
9498 isCommon = indexOf == baseIndexOf,
9499 isLarge = isCommon && length >= 200,
9500 seen = isLarge ? createCache() : null,
9504 indexOf = cacheIndexOf;
9508 seen = iteratee ? [] : result;
9511 while (++index < length) {
9512 var value = array[index],
9513 computed = iteratee ? iteratee(value, index, array) : value;
9515 if (isCommon && value === value) {
9516 var seenIndex = seen.length;
9517 while (seenIndex--) {
9518 if (seen[seenIndex] === computed) {
9523 seen.push(computed);
9527 else if (indexOf(seen, computed, 0) < 0) {
9528 if (iteratee || isLarge) {
9529 seen.push(computed);
9538 * The base implementation of `_.values` and `_.valuesIn` which creates an
9539 * array of `object` property values corresponding to the property names
9543 * @param {Object} object The object to query.
9544 * @param {Array} props The property names to get values for.
9545 * @returns {Object} Returns the array of property values.
9547 function baseValues(object, props) {
9549 length = props.length,
9550 result = Array(length);
9552 while (++index < length) {
9553 result[index] = object[props[index]];
9559 * The base implementation of `wrapperValue` which returns the result of
9560 * performing a sequence of actions on the unwrapped `value`, where each
9561 * successive action is supplied the return value of the previous.
9564 * @param {*} value The unwrapped value.
9565 * @param {Array} actions Actions to peform to resolve the unwrapped value.
9566 * @returns {*} Returns the resolved value.
9568 function baseWrapperValue(value, actions) {
9570 if (result instanceof LazyWrapper) {
9571 result = result.value();
9574 length = actions.length;
9576 while (++index < length) {
9577 var args = [result],
9578 action = actions[index];
9580 push.apply(args, action.args);
9581 result = action.func.apply(action.thisArg, args);
9587 * Performs a binary search of `array` to determine the index at which `value`
9588 * should be inserted into `array` in order to maintain its sort order.
9591 * @param {Array} array The sorted array to inspect.
9592 * @param {*} value The value to evaluate.
9593 * @param {boolean} [retHighest] Specify returning the highest qualified index.
9594 * @returns {number} Returns the index at which `value` should be inserted
9597 function binaryIndex(array, value, retHighest) {
9599 high = array ? array.length : low;
9601 if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
9602 while (low < high) {
9603 var mid = (low + high) >>> 1,
9604 computed = array[mid];
9606 if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) {
9614 return binaryIndexBy(array, value, identity, retHighest);
9618 * This function is like `binaryIndex` except that it invokes `iteratee` for
9619 * `value` and each element of `array` to compute their sort ranking. The
9620 * iteratee is invoked with one argument; (value).
9623 * @param {Array} array The sorted array to inspect.
9624 * @param {*} value The value to evaluate.
9625 * @param {Function} iteratee The function invoked per iteration.
9626 * @param {boolean} [retHighest] Specify returning the highest qualified index.
9627 * @returns {number} Returns the index at which `value` should be inserted
9630 function binaryIndexBy(array, value, iteratee, retHighest) {
9631 value = iteratee(value);
9634 high = array ? array.length : 0,
9635 valIsNaN = value !== value,
9636 valIsNull = value === null,
9637 valIsUndef = value === undefined;
9639 while (low < high) {
9640 var mid = floor((low + high) / 2),
9641 computed = iteratee(array[mid]),
9642 isDef = computed !== undefined,
9643 isReflexive = computed === computed;
9646 var setLow = isReflexive || retHighest;
9647 } else if (valIsNull) {
9648 setLow = isReflexive && isDef && (retHighest || computed != null);
9649 } else if (valIsUndef) {
9650 setLow = isReflexive && (retHighest || isDef);
9651 } else if (computed == null) {
9654 setLow = retHighest ? (computed <= value) : (computed < value);
9662 return nativeMin(high, MAX_ARRAY_INDEX);
9666 * A specialized version of `baseCallback` which only supports `this` binding
9667 * and specifying the number of arguments to provide to `func`.
9670 * @param {Function} func The function to bind.
9671 * @param {*} thisArg The `this` binding of `func`.
9672 * @param {number} [argCount] The number of arguments to provide to `func`.
9673 * @returns {Function} Returns the callback.
9675 function bindCallback(func, thisArg, argCount) {
9676 if (typeof func != 'function') {
9679 if (thisArg === undefined) {
9683 case 1: return function(value) {
9684 return func.call(thisArg, value);
9686 case 3: return function(value, index, collection) {
9687 return func.call(thisArg, value, index, collection);
9689 case 4: return function(accumulator, value, index, collection) {
9690 return func.call(thisArg, accumulator, value, index, collection);
9692 case 5: return function(value, other, key, object, source) {
9693 return func.call(thisArg, value, other, key, object, source);
9697 return func.apply(thisArg, arguments);
9702 * Creates a clone of the given array buffer.
9705 * @param {ArrayBuffer} buffer The array buffer to clone.
9706 * @returns {ArrayBuffer} Returns the cloned array buffer.
9708 function bufferClone(buffer) {
9709 return bufferSlice.call(buffer, 0);
9712 // PhantomJS has `ArrayBuffer` and `Uint8Array` but not `Float64Array`.
9713 bufferClone = !(ArrayBuffer && Uint8Array) ? constant(null) : function(buffer) {
9714 var byteLength = buffer.byteLength,
9715 floatLength = Float64Array ? floor(byteLength / FLOAT64_BYTES_PER_ELEMENT) : 0,
9716 offset = floatLength * FLOAT64_BYTES_PER_ELEMENT,
9717 result = new ArrayBuffer(byteLength);
9720 var view = new Float64Array(result, 0, floatLength);
9721 view.set(new Float64Array(buffer, 0, floatLength));
9723 if (byteLength != offset) {
9724 view = new Uint8Array(result, offset);
9725 view.set(new Uint8Array(buffer, offset));
9732 * Creates an array that is the composition of partially applied arguments,
9733 * placeholders, and provided arguments into a single array of arguments.
9736 * @param {Array|Object} args The provided arguments.
9737 * @param {Array} partials The arguments to prepend to those provided.
9738 * @param {Array} holders The `partials` placeholder indexes.
9739 * @returns {Array} Returns the new array of composed arguments.
9741 function composeArgs(args, partials, holders) {
9742 var holdersLength = holders.length,
9744 argsLength = nativeMax(args.length - holdersLength, 0),
9746 leftLength = partials.length,
9747 result = Array(argsLength + leftLength);
9749 while (++leftIndex < leftLength) {
9750 result[leftIndex] = partials[leftIndex];
9752 while (++argsIndex < holdersLength) {
9753 result[holders[argsIndex]] = args[argsIndex];
9755 while (argsLength--) {
9756 result[leftIndex++] = args[argsIndex++];
9762 * This function is like `composeArgs` except that the arguments composition
9763 * is tailored for `_.partialRight`.
9766 * @param {Array|Object} args The provided arguments.
9767 * @param {Array} partials The arguments to append to those provided.
9768 * @param {Array} holders The `partials` placeholder indexes.
9769 * @returns {Array} Returns the new array of composed arguments.
9771 function composeArgsRight(args, partials, holders) {
9772 var holdersIndex = -1,
9773 holdersLength = holders.length,
9775 argsLength = nativeMax(args.length - holdersLength, 0),
9777 rightLength = partials.length,
9778 result = Array(argsLength + rightLength);
9780 while (++argsIndex < argsLength) {
9781 result[argsIndex] = args[argsIndex];
9783 var offset = argsIndex;
9784 while (++rightIndex < rightLength) {
9785 result[offset + rightIndex] = partials[rightIndex];
9787 while (++holdersIndex < holdersLength) {
9788 result[offset + holders[holdersIndex]] = args[argsIndex++];
9794 * Creates a function that aggregates a collection, creating an accumulator
9795 * object composed from the results of running each element in the collection
9796 * through an iteratee.
9798 * **Note:** This function is used to create `_.countBy`, `_.groupBy`, `_.indexBy`,
9799 * and `_.partition`.
9802 * @param {Function} setter The function to set keys and values of the accumulator object.
9803 * @param {Function} [initializer] The function to initialize the accumulator object.
9804 * @returns {Function} Returns the new aggregator function.
9806 function createAggregator(setter, initializer) {
9807 return function(collection, iteratee, thisArg) {
9808 var result = initializer ? initializer() : {};
9809 iteratee = getCallback(iteratee, thisArg, 3);
9811 if (isArray(collection)) {
9813 length = collection.length;
9815 while (++index < length) {
9816 var value = collection[index];
9817 setter(result, value, iteratee(value, index, collection), collection);
9820 baseEach(collection, function(value, key, collection) {
9821 setter(result, value, iteratee(value, key, collection), collection);
9829 * Creates a function that assigns properties of source object(s) to a given
9830 * destination object.
9832 * **Note:** This function is used to create `_.assign`, `_.defaults`, and `_.merge`.
9835 * @param {Function} assigner The function to assign values.
9836 * @returns {Function} Returns the new assigner function.
9838 function createAssigner(assigner) {
9839 return restParam(function(object, sources) {
9841 length = object == null ? 0 : sources.length,
9842 customizer = length > 2 ? sources[length - 2] : undefined,
9843 guard = length > 2 ? sources[2] : undefined,
9844 thisArg = length > 1 ? sources[length - 1] : undefined;
9846 if (typeof customizer == 'function') {
9847 customizer = bindCallback(customizer, thisArg, 5);
9850 customizer = typeof thisArg == 'function' ? thisArg : undefined;
9851 length -= (customizer ? 1 : 0);
9853 if (guard && isIterateeCall(sources[0], sources[1], guard)) {
9854 customizer = length < 3 ? undefined : customizer;
9857 while (++index < length) {
9858 var source = sources[index];
9860 assigner(object, source, customizer);
9868 * Creates a `baseEach` or `baseEachRight` function.
9871 * @param {Function} eachFunc The function to iterate over a collection.
9872 * @param {boolean} [fromRight] Specify iterating from right to left.
9873 * @returns {Function} Returns the new base function.
9875 function createBaseEach(eachFunc, fromRight) {
9876 return function(collection, iteratee) {
9877 var length = collection ? getLength(collection) : 0;
9878 if (!isLength(length)) {
9879 return eachFunc(collection, iteratee);
9881 var index = fromRight ? length : -1,
9882 iterable = toObject(collection);
9884 while ((fromRight ? index-- : ++index < length)) {
9885 if (iteratee(iterable[index], index, iterable) === false) {
9894 * Creates a base function for `_.forIn` or `_.forInRight`.
9897 * @param {boolean} [fromRight] Specify iterating from right to left.
9898 * @returns {Function} Returns the new base function.
9900 function createBaseFor(fromRight) {
9901 return function(object, iteratee, keysFunc) {
9902 var iterable = toObject(object),
9903 props = keysFunc(object),
9904 length = props.length,
9905 index = fromRight ? length : -1;
9907 while ((fromRight ? index-- : ++index < length)) {
9908 var key = props[index];
9909 if (iteratee(iterable[key], key, iterable) === false) {
9918 * Creates a function that wraps `func` and invokes it with the `this`
9919 * binding of `thisArg`.
9922 * @param {Function} func The function to bind.
9923 * @param {*} [thisArg] The `this` binding of `func`.
9924 * @returns {Function} Returns the new bound function.
9926 function createBindWrapper(func, thisArg) {
9927 var Ctor = createCtorWrapper(func);
9929 function wrapper() {
9930 var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
9931 return fn.apply(thisArg, arguments);
9937 * Creates a `Set` cache object to optimize linear searches of large arrays.
9940 * @param {Array} [values] The values to cache.
9941 * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`.
9943 var createCache = !(nativeCreate && Set) ? constant(null) : function(values) {
9944 return new SetCache(values);
9948 * Creates a function that produces an instance of `Ctor` regardless of
9949 * whether it was invoked as part of a `new` expression or by `call` or `apply`.
9952 * @param {Function} Ctor The constructor to wrap.
9953 * @returns {Function} Returns the new wrapped function.
9955 function createCtorWrapper(Ctor) {
9957 // Use a `switch` statement to work with class constructors.
9958 // See https://people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-function-objects-call-thisargument-argumentslist
9959 // for more details.
9960 var args = arguments;
9961 switch (args.length) {
9962 case 0: return new Ctor;
9963 case 1: return new Ctor(args[0]);
9964 case 2: return new Ctor(args[0], args[1]);
9965 case 3: return new Ctor(args[0], args[1], args[2]);
9966 case 4: return new Ctor(args[0], args[1], args[2], args[3]);
9967 case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
9969 var thisBinding = baseCreate(Ctor.prototype),
9970 result = Ctor.apply(thisBinding, args);
9972 // Mimic the constructor's `return` behavior.
9973 // See https://es5.github.io/#x13.2.2 for more details.
9974 return isObject(result) ? result : thisBinding;
9979 * Creates a `_.find` or `_.findLast` function.
9982 * @param {Function} eachFunc The function to iterate over a collection.
9983 * @param {boolean} [fromRight] Specify iterating from right to left.
9984 * @returns {Function} Returns the new find function.
9986 function createFind(eachFunc, fromRight) {
9987 return function(collection, predicate, thisArg) {
9988 predicate = getCallback(predicate, thisArg, 3);
9989 if (isArray(collection)) {
9990 var index = baseFindIndex(collection, predicate, fromRight);
9991 return index > -1 ? collection[index] : undefined;
9993 return baseFind(collection, predicate, eachFunc);
9998 * Creates a function for `_.forEach` or `_.forEachRight`.
10001 * @param {Function} arrayFunc The function to iterate over an array.
10002 * @param {Function} eachFunc The function to iterate over a collection.
10003 * @returns {Function} Returns the new each function.
10005 function createForEach(arrayFunc, eachFunc) {
10006 return function(collection, iteratee, thisArg) {
10007 return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
10008 ? arrayFunc(collection, iteratee)
10009 : eachFunc(collection, bindCallback(iteratee, thisArg, 3));
10014 * Creates a function for `_.forOwn` or `_.forOwnRight`.
10017 * @param {Function} objectFunc The function to iterate over an object.
10018 * @returns {Function} Returns the new each function.
10020 function createForOwn(objectFunc) {
10021 return function(object, iteratee, thisArg) {
10022 if (typeof iteratee != 'function' || thisArg !== undefined) {
10023 iteratee = bindCallback(iteratee, thisArg, 3);
10025 return objectFunc(object, iteratee);
10030 * Creates a function for `_.reduce` or `_.reduceRight`.
10033 * @param {Function} arrayFunc The function to iterate over an array.
10034 * @param {Function} eachFunc The function to iterate over a collection.
10035 * @returns {Function} Returns the new each function.
10037 function createReduce(arrayFunc, eachFunc) {
10038 return function(collection, iteratee, accumulator, thisArg) {
10039 var initFromArray = arguments.length < 3;
10040 return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
10041 ? arrayFunc(collection, iteratee, accumulator, initFromArray)
10042 : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc);
10047 * Creates a function that wraps `func` and invokes it with optional `this`
10048 * binding of, partial application, and currying.
10051 * @param {Function|string} func The function or method name to reference.
10052 * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
10053 * @param {*} [thisArg] The `this` binding of `func`.
10054 * @param {Array} [partials] The arguments to prepend to those provided to the new function.
10055 * @param {Array} [holders] The `partials` placeholder indexes.
10056 * @param {Array} [partialsRight] The arguments to append to those provided to the new function.
10057 * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
10058 * @param {Array} [argPos] The argument positions of the new function.
10059 * @param {number} [ary] The arity cap of `func`.
10060 * @param {number} [arity] The arity of `func`.
10061 * @returns {Function} Returns the new wrapped function.
10063 function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
10064 var isAry = bitmask & ARY_FLAG,
10065 isBind = bitmask & BIND_FLAG,
10066 isBindKey = bitmask & BIND_KEY_FLAG,
10067 isCurry = bitmask & CURRY_FLAG,
10068 isCurryBound = bitmask & CURRY_BOUND_FLAG,
10069 isCurryRight = bitmask & CURRY_RIGHT_FLAG,
10070 Ctor = isBindKey ? null : createCtorWrapper(func);
10072 function wrapper() {
10073 // Avoid `arguments` object use disqualifying optimizations by
10074 // converting it to an array before providing it to other functions.
10075 var length = arguments.length,
10077 args = Array(length);
10080 args[index] = arguments[index];
10083 args = composeArgs(args, partials, holders);
10085 if (partialsRight) {
10086 args = composeArgsRight(args, partialsRight, holdersRight);
10088 if (isCurry || isCurryRight) {
10089 var placeholder = wrapper.placeholder,
10090 argsHolders = replaceHolders(args, placeholder);
10092 length -= argsHolders.length;
10093 if (length < arity) {
10094 var newArgPos = argPos ? arrayCopy(argPos) : null,
10095 newArity = nativeMax(arity - length, 0),
10096 newsHolders = isCurry ? argsHolders : null,
10097 newHoldersRight = isCurry ? null : argsHolders,
10098 newPartials = isCurry ? args : null,
10099 newPartialsRight = isCurry ? null : args;
10101 bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
10102 bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
10104 if (!isCurryBound) {
10105 bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
10107 var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity],
10108 result = createHybridWrapper.apply(undefined, newData);
10110 if (isLaziable(func)) {
10111 setData(result, newData);
10113 result.placeholder = placeholder;
10117 var thisBinding = isBind ? thisArg : this,
10118 fn = isBindKey ? thisBinding[func] : func;
10121 args = reorder(args, argPos);
10123 if (isAry && ary < args.length) {
10126 if (this && this !== root && this instanceof wrapper) {
10127 fn = Ctor || createCtorWrapper(func);
10129 return fn.apply(thisBinding, args);
10135 * Creates a function that wraps `func` and invokes it with the optional `this`
10136 * binding of `thisArg` and the `partials` prepended to those provided to
10140 * @param {Function} func The function to partially apply arguments to.
10141 * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
10142 * @param {*} thisArg The `this` binding of `func`.
10143 * @param {Array} partials The arguments to prepend to those provided to the new function.
10144 * @returns {Function} Returns the new bound function.
10146 function createPartialWrapper(func, bitmask, thisArg, partials) {
10147 var isBind = bitmask & BIND_FLAG,
10148 Ctor = createCtorWrapper(func);
10150 function wrapper() {
10151 // Avoid `arguments` object use disqualifying optimizations by
10152 // converting it to an array before providing it `func`.
10153 var argsIndex = -1,
10154 argsLength = arguments.length,
10156 leftLength = partials.length,
10157 args = Array(argsLength + leftLength);
10159 while (++leftIndex < leftLength) {
10160 args[leftIndex] = partials[leftIndex];
10162 while (argsLength--) {
10163 args[leftIndex++] = arguments[++argsIndex];
10165 var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10166 return fn.apply(isBind ? thisArg : this, args);
10172 * Creates a function that either curries or invokes `func` with optional
10173 * `this` binding and partially applied arguments.
10176 * @param {Function|string} func The function or method name to reference.
10177 * @param {number} bitmask The bitmask of flags.
10178 * The bitmask may be composed of the following flags:
10181 * 4 - `_.curry` or `_.curryRight` of a bound function
10183 * 16 - `_.curryRight`
10185 * 64 - `_.partialRight`
10188 * @param {*} [thisArg] The `this` binding of `func`.
10189 * @param {Array} [partials] The arguments to be partially applied.
10190 * @param {Array} [holders] The `partials` placeholder indexes.
10191 * @param {Array} [argPos] The argument positions of the new function.
10192 * @param {number} [ary] The arity cap of `func`.
10193 * @param {number} [arity] The arity of `func`.
10194 * @returns {Function} Returns the new wrapped function.
10196 function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
10197 var isBindKey = bitmask & BIND_KEY_FLAG;
10198 if (!isBindKey && typeof func != 'function') {
10199 throw new TypeError(FUNC_ERROR_TEXT);
10201 var length = partials ? partials.length : 0;
10203 bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
10204 partials = holders = null;
10206 length -= (holders ? holders.length : 0);
10207 if (bitmask & PARTIAL_RIGHT_FLAG) {
10208 var partialsRight = partials,
10209 holdersRight = holders;
10211 partials = holders = null;
10213 var data = isBindKey ? null : getData(func),
10214 newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
10217 mergeData(newData, data);
10218 bitmask = newData[1];
10219 arity = newData[9];
10221 newData[9] = arity == null
10222 ? (isBindKey ? 0 : func.length)
10223 : (nativeMax(arity - length, 0) || 0);
10225 if (bitmask == BIND_FLAG) {
10226 var result = createBindWrapper(newData[0], newData[2]);
10227 } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) {
10228 result = createPartialWrapper.apply(undefined, newData);
10230 result = createHybridWrapper.apply(undefined, newData);
10232 var setter = data ? baseSetData : setData;
10233 return setter(result, newData);
10237 * A specialized version of `baseIsEqualDeep` for arrays with support for
10238 * partial deep comparisons.
10241 * @param {Array} array The array to compare.
10242 * @param {Array} other The other array to compare.
10243 * @param {Function} equalFunc The function to determine equivalents of values.
10244 * @param {Function} [customizer] The function to customize comparing arrays.
10245 * @param {boolean} [isLoose] Specify performing partial comparisons.
10246 * @param {Array} [stackA] Tracks traversed `value` objects.
10247 * @param {Array} [stackB] Tracks traversed `other` objects.
10248 * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
10250 function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) {
10252 arrLength = array.length,
10253 othLength = other.length;
10255 if (arrLength != othLength && !(isLoose && othLength > arrLength)) {
10258 // Ignore non-index properties.
10259 while (++index < arrLength) {
10260 var arrValue = array[index],
10261 othValue = other[index],
10262 result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined;
10264 if (result !== undefined) {
10270 // Recursively compare arrays (susceptible to call stack limits).
10272 if (!arraySome(other, function(othValue) {
10273 return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB);
10277 } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) {
10285 * A specialized version of `baseIsEqualDeep` for comparing objects of
10286 * the same `toStringTag`.
10288 * **Note:** This function only supports comparing values with tags of
10289 * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
10292 * @param {Object} value The object to compare.
10293 * @param {Object} other The other object to compare.
10294 * @param {string} tag The `toStringTag` of the objects to compare.
10295 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
10297 function equalByTag(object, other, tag) {
10301 // Coerce dates and booleans to numbers, dates to milliseconds and booleans
10302 // to `1` or `0` treating invalid dates coerced to `NaN` as not equal.
10303 return +object == +other;
10306 return object.name == other.name && object.message == other.message;
10309 // Treat `NaN` vs. `NaN` as equal.
10310 return (object != +object)
10312 : object == +other;
10316 // Coerce regexes to strings and treat strings primitives and string
10317 // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details.
10318 return object == (other + '');
10324 * A specialized version of `baseIsEqualDeep` for objects with support for
10325 * partial deep comparisons.
10328 * @param {Object} object The object to compare.
10329 * @param {Object} other The other object to compare.
10330 * @param {Function} equalFunc The function to determine equivalents of values.
10331 * @param {Function} [customizer] The function to customize comparing values.
10332 * @param {boolean} [isLoose] Specify performing partial comparisons.
10333 * @param {Array} [stackA] Tracks traversed `value` objects.
10334 * @param {Array} [stackB] Tracks traversed `other` objects.
10335 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
10337 function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
10338 var objProps = keys(object),
10339 objLength = objProps.length,
10340 othProps = keys(other),
10341 othLength = othProps.length;
10343 if (objLength != othLength && !isLoose) {
10346 var index = objLength;
10348 var key = objProps[index];
10349 if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) {
10353 var skipCtor = isLoose;
10354 while (++index < objLength) {
10355 key = objProps[index];
10356 var objValue = object[key],
10357 othValue = other[key],
10358 result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined;
10360 // Recursively compare objects (susceptible to call stack limits).
10361 if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) {
10364 skipCtor || (skipCtor = key == 'constructor');
10367 var objCtor = object.constructor,
10368 othCtor = other.constructor;
10370 // Non `Object` object instances with different constructors are not equal.
10371 if (objCtor != othCtor &&
10372 ('constructor' in object && 'constructor' in other) &&
10373 !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
10374 typeof othCtor == 'function' && othCtor instanceof othCtor)) {
10382 * Gets the appropriate "callback" function. If the `_.callback` method is
10383 * customized this function returns the custom method, otherwise it returns
10384 * the `baseCallback` function. If arguments are provided the chosen function
10385 * is invoked with them and its result is returned.
10388 * @returns {Function} Returns the chosen function or its result.
10390 function getCallback(func, thisArg, argCount) {
10391 var result = lodash.callback || callback;
10392 result = result === callback ? baseCallback : result;
10393 return argCount ? result(func, thisArg, argCount) : result;
10397 * Gets metadata for `func`.
10400 * @param {Function} func The function to query.
10401 * @returns {*} Returns the metadata for `func`.
10403 var getData = !metaMap ? noop : function(func) {
10404 return metaMap.get(func);
10408 * Gets the name of `func`.
10411 * @param {Function} func The function to query.
10412 * @returns {string} Returns the function name.
10414 function getFuncName(func) {
10415 var result = func.name,
10416 array = realNames[result],
10417 length = array ? array.length : 0;
10420 var data = array[length],
10421 otherFunc = data.func;
10422 if (otherFunc == null || otherFunc == func) {
10430 * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
10431 * customized this function returns the custom method, otherwise it returns
10432 * the `baseIndexOf` function. If arguments are provided the chosen function
10433 * is invoked with them and its result is returned.
10436 * @returns {Function|number} Returns the chosen function or its result.
10438 function getIndexOf(collection, target, fromIndex) {
10439 var result = lodash.indexOf || indexOf;
10440 result = result === indexOf ? baseIndexOf : result;
10441 return collection ? result(collection, target, fromIndex) : result;
10445 * Gets the "length" property value of `object`.
10447 * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792)
10448 * that affects Safari on at least iOS 8.1-8.3 ARM64.
10451 * @param {Object} object The object to query.
10452 * @returns {*} Returns the "length" value.
10454 var getLength = baseProperty('length');
10457 * Gets the propery names, values, and compare flags of `object`.
10460 * @param {Object} object The object to query.
10461 * @returns {Array} Returns the match data of `object`.
10463 function getMatchData(object) {
10464 var result = pairs(object),
10465 length = result.length;
10468 result[length][2] = isStrictComparable(result[length][1]);
10474 * Gets the native function at `key` of `object`.
10477 * @param {Object} object The object to query.
10478 * @param {string} key The key of the method to get.
10479 * @returns {*} Returns the function if it's native, else `undefined`.
10481 function getNative(object, key) {
10482 var value = object == null ? undefined : object[key];
10483 return isNative(value) ? value : undefined;
10487 * Gets the view, applying any `transforms` to the `start` and `end` positions.
10490 * @param {number} start The start of the view.
10491 * @param {number} end The end of the view.
10492 * @param {Array} [transforms] The transformations to apply to the view.
10493 * @returns {Object} Returns an object containing the `start` and `end`
10494 * positions of the view.
10496 function getView(start, end, transforms) {
10498 length = transforms ? transforms.length : 0;
10500 while (++index < length) {
10501 var data = transforms[index],
10504 switch (data.type) {
10505 case 'drop': start += size; break;
10506 case 'dropRight': end -= size; break;
10507 case 'take': end = nativeMin(end, start + size); break;
10508 case 'takeRight': start = nativeMax(start, end - size); break;
10511 return { 'start': start, 'end': end };
10515 * Initializes an array clone.
10518 * @param {Array} array The array to clone.
10519 * @returns {Array} Returns the initialized clone.
10521 function initCloneArray(array) {
10522 var length = array.length,
10523 result = new array.constructor(length);
10525 // Add array properties assigned by `RegExp#exec`.
10526 if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
10527 result.index = array.index;
10528 result.input = array.input;
10534 * Initializes an object clone.
10537 * @param {Object} object The object to clone.
10538 * @returns {Object} Returns the initialized clone.
10540 function initCloneObject(object) {
10541 var Ctor = object.constructor;
10542 if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) {
10549 * Initializes an object clone based on its `toStringTag`.
10551 * **Note:** This function only supports cloning values with tags of
10552 * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
10555 * @param {Object} object The object to clone.
10556 * @param {string} tag The `toStringTag` of the object to clone.
10557 * @param {boolean} [isDeep] Specify a deep clone.
10558 * @returns {Object} Returns the initialized clone.
10560 function initCloneByTag(object, tag, isDeep) {
10561 var Ctor = object.constructor;
10563 case arrayBufferTag:
10564 return bufferClone(object);
10568 return new Ctor(+object);
10570 case float32Tag: case float64Tag:
10571 case int8Tag: case int16Tag: case int32Tag:
10572 case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
10573 // Safari 5 mobile incorrectly has `Object` as the constructor of typed arrays.
10574 if (Ctor instanceof Ctor) {
10575 Ctor = ctorByTag[tag];
10577 var buffer = object.buffer;
10578 return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length);
10582 return new Ctor(object);
10585 var result = new Ctor(object.source, reFlags.exec(object));
10586 result.lastIndex = object.lastIndex;
10592 * Checks if `value` is array-like.
10595 * @param {*} value The value to check.
10596 * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
10598 function isArrayLike(value) {
10599 return value != null && isLength(getLength(value));
10603 * Checks if `value` is a valid array-like index.
10606 * @param {*} value The value to check.
10607 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
10608 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
10610 function isIndex(value, length) {
10611 value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1;
10612 length = length == null ? MAX_SAFE_INTEGER : length;
10613 return value > -1 && value % 1 == 0 && value < length;
10617 * Checks if the provided arguments are from an iteratee call.
10620 * @param {*} value The potential iteratee value argument.
10621 * @param {*} index The potential iteratee index or key argument.
10622 * @param {*} object The potential iteratee object argument.
10623 * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`.
10625 function isIterateeCall(value, index, object) {
10626 if (!isObject(object)) {
10629 var type = typeof index;
10630 if (type == 'number'
10631 ? (isArrayLike(object) && isIndex(index, object.length))
10632 : (type == 'string' && index in object)) {
10633 var other = object[index];
10634 return value === value ? (value === other) : (other !== other);
10640 * Checks if `value` is a property name and not a property path.
10643 * @param {*} value The value to check.
10644 * @param {Object} [object] The object to query keys on.
10645 * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
10647 function isKey(value, object) {
10648 var type = typeof value;
10649 if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') {
10652 if (isArray(value)) {
10655 var result = !reIsDeepProp.test(value);
10656 return result || (object != null && value in toObject(object));
10660 * Checks if `func` has a lazy counterpart.
10663 * @param {Function} func The function to check.
10664 * @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`.
10666 function isLaziable(func) {
10667 var funcName = getFuncName(func);
10668 if (!(funcName in LazyWrapper.prototype)) {
10671 var other = lodash[funcName];
10672 if (func === other) {
10675 var data = getData(other);
10676 return !!data && func === data[0];
10680 * Checks if `value` is a valid array-like length.
10682 * **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength).
10685 * @param {*} value The value to check.
10686 * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
10688 function isLength(value) {
10689 return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
10693 * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
10696 * @param {*} value The value to check.
10697 * @returns {boolean} Returns `true` if `value` if suitable for strict
10698 * equality comparisons, else `false`.
10700 function isStrictComparable(value) {
10701 return value === value && !isObject(value);
10705 * Merges the function metadata of `source` into `data`.
10707 * Merging metadata reduces the number of wrappers required to invoke a function.
10708 * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
10709 * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg`
10710 * augment function arguments, making the order in which they are executed important,
10711 * preventing the merging of metadata. However, we make an exception for a safe
10712 * common case where curried functions have `_.ary` and or `_.rearg` applied.
10715 * @param {Array} data The destination metadata.
10716 * @param {Array} source The source metadata.
10717 * @returns {Array} Returns `data`.
10719 function mergeData(data, source) {
10720 var bitmask = data[1],
10721 srcBitmask = source[1],
10722 newBitmask = bitmask | srcBitmask,
10723 isCommon = newBitmask < ARY_FLAG;
10726 (srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) ||
10727 (srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) ||
10728 (srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG);
10730 // Exit early if metadata can't be merged.
10731 if (!(isCommon || isCombo)) {
10734 // Use source `thisArg` if available.
10735 if (srcBitmask & BIND_FLAG) {
10736 data[2] = source[2];
10737 // Set when currying a bound function.
10738 newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG;
10740 // Compose partial arguments.
10741 var value = source[3];
10743 var partials = data[3];
10744 data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value);
10745 data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]);
10747 // Compose partial right arguments.
10750 partials = data[5];
10751 data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value);
10752 data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]);
10754 // Use source `argPos` if available.
10757 data[7] = arrayCopy(value);
10759 // Use source `ary` if it's smaller.
10760 if (srcBitmask & ARY_FLAG) {
10761 data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
10763 // Use source `arity` if one is not provided.
10764 if (data[9] == null) {
10765 data[9] = source[9];
10767 // Use source `func` and merge bitmasks.
10768 data[0] = source[0];
10769 data[1] = newBitmask;
10775 * A specialized version of `_.pick` which picks `object` properties specified
10779 * @param {Object} object The source object.
10780 * @param {string[]} props The property names to pick.
10781 * @returns {Object} Returns the new object.
10783 function pickByArray(object, props) {
10784 object = toObject(object);
10787 length = props.length,
10790 while (++index < length) {
10791 var key = props[index];
10792 if (key in object) {
10793 result[key] = object[key];
10800 * A specialized version of `_.pick` which picks `object` properties `predicate`
10801 * returns truthy for.
10804 * @param {Object} object The source object.
10805 * @param {Function} predicate The function invoked per iteration.
10806 * @returns {Object} Returns the new object.
10808 function pickByCallback(object, predicate) {
10810 baseForIn(object, function(value, key, object) {
10811 if (predicate(value, key, object)) {
10812 result[key] = value;
10819 * Reorder `array` according to the specified indexes where the element at
10820 * the first index is assigned as the first element, the element at
10821 * the second index is assigned as the second element, and so on.
10824 * @param {Array} array The array to reorder.
10825 * @param {Array} indexes The arranged array indexes.
10826 * @returns {Array} Returns `array`.
10828 function reorder(array, indexes) {
10829 var arrLength = array.length,
10830 length = nativeMin(indexes.length, arrLength),
10831 oldArray = arrayCopy(array);
10834 var index = indexes[length];
10835 array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
10841 * Sets metadata for `func`.
10843 * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
10844 * period of time, it will trip its breaker and transition to an identity function
10845 * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070)
10846 * for more details.
10849 * @param {Function} func The function to associate metadata with.
10850 * @param {*} data The metadata.
10851 * @returns {Function} Returns `func`.
10853 var setData = (function() {
10857 return function(key, value) {
10859 remaining = HOT_SPAN - (stamp - lastCalled);
10861 lastCalled = stamp;
10862 if (remaining > 0) {
10863 if (++count >= HOT_COUNT) {
10869 return baseSetData(key, value);
10874 * A fallback implementation of `_.isPlainObject` which checks if `value`
10875 * is an object created by the `Object` constructor or has a `[[Prototype]]`
10879 * @param {*} value The value to check.
10880 * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
10882 function shimIsPlainObject(value) {
10884 support = lodash.support;
10886 // Exit early for non `Object` objects.
10887 if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isHostObject(value)) ||
10888 (!hasOwnProperty.call(value, 'constructor') &&
10889 (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor))) ||
10890 (!support.argsTag && isArguments(value))) {
10893 // IE < 9 iterates inherited properties before own properties. If the first
10894 // iterated property is an object's own property then there are no inherited
10895 // enumerable properties.
10897 if (support.ownLast) {
10898 baseForIn(value, function(subValue, key, object) {
10899 result = hasOwnProperty.call(object, key);
10902 return result !== false;
10904 // In most environments an object's own properties are iterated before
10905 // its inherited properties. If the last iterated property is an object's
10906 // own property then there are no inherited enumerable properties.
10907 baseForIn(value, function(subValue, key) {
10910 return result === undefined || hasOwnProperty.call(value, result);
10914 * A fallback implementation of `Object.keys` which creates an array of the
10915 * own enumerable property names of `object`.
10918 * @param {Object} object The object to query.
10919 * @returns {Array} Returns the array of property names.
10921 function shimKeys(object) {
10922 var props = keysIn(object),
10923 propsLength = props.length,
10924 length = propsLength && object.length;
10926 var allowIndexes = !!length && isLength(length) &&
10927 (isArray(object) || isArguments(object) || isString(object));
10932 while (++index < propsLength) {
10933 var key = props[index];
10934 if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) {
10942 * Converts `value` to an object if it's not one.
10945 * @param {*} value The value to process.
10946 * @returns {Object} Returns the object.
10948 function toObject(value) {
10949 if (lodash.support.unindexedChars && isString(value)) {
10951 length = value.length,
10952 result = Object(value);
10954 while (++index < length) {
10955 result[index] = value.charAt(index);
10959 return isObject(value) ? value : Object(value);
10963 * Converts `value` to property path array if it's not one.
10966 * @param {*} value The value to process.
10967 * @returns {Array} Returns the property path array.
10969 function toPath(value) {
10970 if (isArray(value)) {
10974 baseToString(value).replace(rePropName, function(match, number, quote, string) {
10975 result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
10981 * Creates a clone of `wrapper`.
10984 * @param {Object} wrapper The wrapper to clone.
10985 * @returns {Object} Returns the cloned wrapper.
10987 function wrapperClone(wrapper) {
10988 return wrapper instanceof LazyWrapper
10990 : new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__, arrayCopy(wrapper.__actions__));
10993 /*------------------------------------------------------------------------*/
10996 * Creates an array of elements split into groups the length of `size`.
10997 * If `collection` can't be split evenly, the final chunk will be the remaining
11003 * @param {Array} array The array to process.
11004 * @param {number} [size=1] The length of each chunk.
11005 * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
11006 * @returns {Array} Returns the new array containing chunks.
11009 * _.chunk(['a', 'b', 'c', 'd'], 2);
11010 * // => [['a', 'b'], ['c', 'd']]
11012 * _.chunk(['a', 'b', 'c', 'd'], 3);
11013 * // => [['a', 'b', 'c'], ['d']]
11015 function chunk(array, size, guard) {
11016 if (guard ? isIterateeCall(array, size, guard) : size == null) {
11019 size = nativeMax(+size || 1, 1);
11022 length = array ? array.length : 0,
11024 result = Array(ceil(length / size));
11026 while (index < length) {
11027 result[++resIndex] = baseSlice(array, index, (index += size));
11033 * Creates an array with all falsey values removed. The values `false`, `null`,
11034 * `0`, `""`, `undefined`, and `NaN` are falsey.
11039 * @param {Array} array The array to compact.
11040 * @returns {Array} Returns the new array of filtered values.
11043 * _.compact([0, 1, false, 2, '', 3]);
11046 function compact(array) {
11048 length = array ? array.length : 0,
11052 while (++index < length) {
11053 var value = array[index];
11055 result[++resIndex] = value;
11062 * Creates an array of unique `array` values not included in the other
11063 * provided arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11064 * for equality comparisons.
11069 * @param {Array} array The array to inspect.
11070 * @param {...Array} [values] The arrays of values to exclude.
11071 * @returns {Array} Returns the new array of filtered values.
11074 * _.difference([1, 2, 3], [4, 2]);
11077 var difference = restParam(function(array, values) {
11078 return isArrayLike(array)
11079 ? baseDifference(array, baseFlatten(values, false, true))
11084 * Gets the first element of `array`.
11090 * @param {Array} array The array to query.
11091 * @returns {*} Returns the first element of `array`.
11094 * _.first([1, 2, 3]);
11100 function first(array) {
11101 return array ? array[0] : undefined;
11105 * Flattens a nested array. If `isDeep` is `true` the array is recursively
11106 * flattened, otherwise it is only flattened a single level.
11111 * @param {Array} array The array to flatten.
11112 * @param {boolean} [isDeep] Specify a deep flatten.
11113 * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
11114 * @returns {Array} Returns the new flattened array.
11117 * _.flatten([1, [2, 3, [4]]]);
11118 * // => [1, 2, 3, [4]]
11120 * // using `isDeep`
11121 * _.flatten([1, [2, 3, [4]]], true);
11122 * // => [1, 2, 3, 4]
11124 function flatten(array, isDeep, guard) {
11125 var length = array ? array.length : 0;
11126 if (guard && isIterateeCall(array, isDeep, guard)) {
11129 return length ? baseFlatten(array, isDeep) : [];
11133 * Gets the index at which the first occurrence of `value` is found in `array`
11134 * using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11135 * for equality comparisons. If `fromIndex` is negative, it is used as the offset
11136 * from the end of `array`. If `array` is sorted providing `true` for `fromIndex`
11137 * performs a faster binary search.
11142 * @param {Array} array The array to search.
11143 * @param {*} value The value to search for.
11144 * @param {boolean|number} [fromIndex=0] The index to search from or `true`
11145 * to perform a binary search on a sorted array.
11146 * @returns {number} Returns the index of the matched value, else `-1`.
11149 * _.indexOf([1, 2, 1, 2], 2);
11152 * // using `fromIndex`
11153 * _.indexOf([1, 2, 1, 2], 2, 2);
11156 * // performing a binary search
11157 * _.indexOf([1, 1, 2, 2], 2, true);
11160 function indexOf(array, value, fromIndex) {
11161 var length = array ? array.length : 0;
11165 if (typeof fromIndex == 'number') {
11166 fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex;
11167 } else if (fromIndex) {
11168 var index = binaryIndex(array, value),
11169 other = array[index];
11171 if (value === value ? (value === other) : (other !== other)) {
11176 return baseIndexOf(array, value, fromIndex || 0);
11180 * Creates an array of unique values that are included in all of the provided
11181 * arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11182 * for equality comparisons.
11187 * @param {...Array} [arrays] The arrays to inspect.
11188 * @returns {Array} Returns the new array of shared values.
11190 * _.intersection([1, 2], [4, 2], [2, 1]);
11193 var intersection = restParam(function(arrays) {
11194 var othLength = arrays.length,
11195 othIndex = othLength,
11196 caches = Array(length),
11197 indexOf = getIndexOf(),
11198 isCommon = indexOf == baseIndexOf,
11201 while (othIndex--) {
11202 var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : [];
11203 caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null;
11205 var array = arrays[0],
11207 length = array ? array.length : 0,
11211 while (++index < length) {
11212 value = array[index];
11213 if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) {
11214 var othIndex = othLength;
11215 while (--othIndex) {
11216 var cache = caches[othIndex];
11217 if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) {
11224 result.push(value);
11231 * Gets the last element of `array`.
11236 * @param {Array} array The array to query.
11237 * @returns {*} Returns the last element of `array`.
11240 * _.last([1, 2, 3]);
11243 function last(array) {
11244 var length = array ? array.length : 0;
11245 return length ? array[length - 1] : undefined;
11249 * Creates an array of unique values, in order, from all of the provided arrays
11250 * using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11251 * for equality comparisons.
11256 * @param {...Array} [arrays] The arrays to inspect.
11257 * @returns {Array} Returns the new array of combined values.
11260 * _.union([1, 2], [4, 2], [2, 1]);
11263 var union = restParam(function(arrays) {
11264 return baseUniq(baseFlatten(arrays, false, true));
11268 * Creates a duplicate-free version of an array, using
11269 * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11270 * for equality comparisons, in which only the first occurence of each element
11271 * is kept. Providing `true` for `isSorted` performs a faster search algorithm
11272 * for sorted arrays. If an iteratee function is provided it is invoked for
11273 * each element in the array to generate the criterion by which uniqueness
11274 * is computed. The `iteratee` is bound to `thisArg` and invoked with three
11275 * arguments: (value, index, array).
11277 * If a property name is provided for `iteratee` the created `_.property`
11278 * style callback returns the property value of the given element.
11280 * If a value is also provided for `thisArg` the created `_.matchesProperty`
11281 * style callback returns `true` for elements that have a matching property
11282 * value, else `false`.
11284 * If an object is provided for `iteratee` the created `_.matches` style
11285 * callback returns `true` for elements that have the properties of the given
11286 * object, else `false`.
11292 * @param {Array} array The array to inspect.
11293 * @param {boolean} [isSorted] Specify the array is sorted.
11294 * @param {Function|Object|string} [iteratee] The function invoked per iteration.
11295 * @param {*} [thisArg] The `this` binding of `iteratee`.
11296 * @returns {Array} Returns the new duplicate-value-free array.
11299 * _.uniq([2, 1, 2]);
11302 * // using `isSorted`
11303 * _.uniq([1, 1, 2], true);
11306 * // using an iteratee function
11307 * _.uniq([1, 2.5, 1.5, 2], function(n) {
11308 * return this.floor(n);
11312 * // using the `_.property` callback shorthand
11313 * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
11314 * // => [{ 'x': 1 }, { 'x': 2 }]
11316 function uniq(array, isSorted, iteratee, thisArg) {
11317 var length = array ? array.length : 0;
11321 if (isSorted != null && typeof isSorted != 'boolean') {
11322 thisArg = iteratee;
11323 iteratee = isIterateeCall(array, isSorted, thisArg) ? null : isSorted;
11326 var callback = getCallback();
11327 if (!(iteratee == null && callback === baseCallback)) {
11328 iteratee = callback(iteratee, thisArg, 3);
11330 return (isSorted && getIndexOf() == baseIndexOf)
11331 ? sortedUniq(array, iteratee)
11332 : baseUniq(array, iteratee);
11336 * Creates an array excluding all provided values using
11337 * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11338 * for equality comparisons.
11343 * @param {Array} array The array to filter.
11344 * @param {...*} [values] The values to exclude.
11345 * @returns {Array} Returns the new array of filtered values.
11348 * _.without([1, 2, 1, 3], 1, 2);
11351 var without = restParam(function(array, values) {
11352 return isArrayLike(array)
11353 ? baseDifference(array, values)
11357 /*------------------------------------------------------------------------*/
11360 * Creates a `lodash` object that wraps `value` with explicit method
11361 * chaining enabled.
11366 * @param {*} value The value to wrap.
11367 * @returns {Object} Returns the new `lodash` wrapper instance.
11371 * { 'user': 'barney', 'age': 36 },
11372 * { 'user': 'fred', 'age': 40 },
11373 * { 'user': 'pebbles', 'age': 1 }
11376 * var youngest = _.chain(users)
11378 * .map(function(chr) {
11379 * return chr.user + ' is ' + chr.age;
11383 * // => 'pebbles is 1'
11385 function chain(value) {
11386 var result = lodash(value);
11387 result.__chain__ = true;
11392 * This method invokes `interceptor` and returns `value`. The interceptor is
11393 * bound to `thisArg` and invoked with one argument; (value). The purpose of
11394 * this method is to "tap into" a method chain in order to perform operations
11395 * on intermediate results within the chain.
11400 * @param {*} value The value to provide to `interceptor`.
11401 * @param {Function} interceptor The function to invoke.
11402 * @param {*} [thisArg] The `this` binding of `interceptor`.
11403 * @returns {*} Returns `value`.
11407 * .tap(function(array) {
11414 function tap(value, interceptor, thisArg) {
11415 interceptor.call(thisArg, value);
11420 * This method is like `_.tap` except that it returns the result of `interceptor`.
11425 * @param {*} value The value to provide to `interceptor`.
11426 * @param {Function} interceptor The function to invoke.
11427 * @param {*} [thisArg] The `this` binding of `interceptor`.
11428 * @returns {*} Returns the result of `interceptor`.
11434 * .thru(function(value) {
11440 function thru(value, interceptor, thisArg) {
11441 return interceptor.call(thisArg, value);
11445 * Enables explicit method chaining on the wrapper object.
11450 * @returns {Object} Returns the new `lodash` wrapper instance.
11454 * { 'user': 'barney', 'age': 36 },
11455 * { 'user': 'fred', 'age': 40 }
11458 * // without explicit chaining
11459 * _(users).first();
11460 * // => { 'user': 'barney', 'age': 36 }
11462 * // with explicit chaining
11467 * // => { 'user': 'barney' }
11469 function wrapperChain() {
11470 return chain(this);
11474 * Executes the chained sequence and returns the wrapped result.
11479 * @returns {Object} Returns the new `lodash` wrapper instance.
11482 * var array = [1, 2];
11483 * var wrapper = _(array).push(3);
11485 * console.log(array);
11488 * wrapper = wrapper.commit();
11489 * console.log(array);
11495 * console.log(array);
11498 function wrapperCommit() {
11499 return new LodashWrapper(this.value(), this.__chain__);
11503 * Creates a clone of the chained sequence planting `value` as the wrapped value.
11508 * @returns {Object} Returns the new `lodash` wrapper instance.
11511 * var array = [1, 2];
11512 * var wrapper = _(array).map(function(value) {
11513 * return Math.pow(value, 2);
11516 * var other = [3, 4];
11517 * var otherWrapper = wrapper.plant(other);
11519 * otherWrapper.value();
11525 function wrapperPlant(value) {
11529 while (parent instanceof baseLodash) {
11530 var clone = wrapperClone(parent);
11532 previous.__wrapped__ = clone;
11536 var previous = clone;
11537 parent = parent.__wrapped__;
11539 previous.__wrapped__ = value;
11544 * Reverses the wrapped array so the first element becomes the last, the
11545 * second element becomes the second to last, and so on.
11547 * **Note:** This method mutates the wrapped array.
11552 * @returns {Object} Returns the new reversed `lodash` wrapper instance.
11555 * var array = [1, 2, 3];
11557 * _(array).reverse().value()
11560 * console.log(array);
11563 function wrapperReverse() {
11564 var value = this.__wrapped__;
11565 if (value instanceof LazyWrapper) {
11566 if (this.__actions__.length) {
11567 value = new LazyWrapper(this);
11569 return new LodashWrapper(value.reverse(), this.__chain__);
11571 return this.thru(function(value) {
11572 return value.reverse();
11577 * Produces the result of coercing the unwrapped value to a string.
11582 * @returns {string} Returns the coerced string value.
11585 * _([1, 2, 3]).toString();
11588 function wrapperToString() {
11589 return (this.value() + '');
11593 * Executes the chained sequence to extract the unwrapped value.
11597 * @alias run, toJSON, valueOf
11599 * @returns {*} Returns the resolved unwrapped value.
11602 * _([1, 2, 3]).value();
11605 function wrapperValue() {
11606 return baseWrapperValue(this.__wrapped__, this.__actions__);
11609 /*------------------------------------------------------------------------*/
11612 * Checks if `predicate` returns truthy for **all** elements of `collection`.
11613 * The predicate is bound to `thisArg` and invoked with three arguments:
11614 * (value, index|key, collection).
11616 * If a property name is provided for `predicate` the created `_.property`
11617 * style callback returns the property value of the given element.
11619 * If a value is also provided for `thisArg` the created `_.matchesProperty`
11620 * style callback returns `true` for elements that have a matching property
11621 * value, else `false`.
11623 * If an object is provided for `predicate` the created `_.matches` style
11624 * callback returns `true` for elements that have the properties of the given
11625 * object, else `false`.
11630 * @category Collection
11631 * @param {Array|Object|string} collection The collection to iterate over.
11632 * @param {Function|Object|string} [predicate=_.identity] The function invoked
11634 * @param {*} [thisArg] The `this` binding of `predicate`.
11635 * @returns {boolean} Returns `true` if all elements pass the predicate check,
11639 * _.every([true, 1, null, 'yes'], Boolean);
11643 * { 'user': 'barney', 'active': false },
11644 * { 'user': 'fred', 'active': false }
11647 * // using the `_.matches` callback shorthand
11648 * _.every(users, { 'user': 'barney', 'active': false });
11651 * // using the `_.matchesProperty` callback shorthand
11652 * _.every(users, 'active', false);
11655 * // using the `_.property` callback shorthand
11656 * _.every(users, 'active');
11659 function every(collection, predicate, thisArg) {
11660 var func = isArray(collection) ? arrayEvery : baseEvery;
11661 if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
11664 if (typeof predicate != 'function' || thisArg !== undefined) {
11665 predicate = getCallback(predicate, thisArg, 3);
11667 return func(collection, predicate);
11671 * Iterates over elements of `collection`, returning an array of all elements
11672 * `predicate` returns truthy for. The predicate is bound to `thisArg` and
11673 * invoked with three arguments: (value, index|key, collection).
11675 * If a property name is provided for `predicate` the created `_.property`
11676 * style callback returns the property value of the given element.
11678 * If a value is also provided for `thisArg` the created `_.matchesProperty`
11679 * style callback returns `true` for elements that have a matching property
11680 * value, else `false`.
11682 * If an object is provided for `predicate` the created `_.matches` style
11683 * callback returns `true` for elements that have the properties of the given
11684 * object, else `false`.
11689 * @category Collection
11690 * @param {Array|Object|string} collection The collection to iterate over.
11691 * @param {Function|Object|string} [predicate=_.identity] The function invoked
11693 * @param {*} [thisArg] The `this` binding of `predicate`.
11694 * @returns {Array} Returns the new filtered array.
11697 * _.filter([4, 5, 6], function(n) {
11698 * return n % 2 == 0;
11703 * { 'user': 'barney', 'age': 36, 'active': true },
11704 * { 'user': 'fred', 'age': 40, 'active': false }
11707 * // using the `_.matches` callback shorthand
11708 * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user');
11711 * // using the `_.matchesProperty` callback shorthand
11712 * _.pluck(_.filter(users, 'active', false), 'user');
11715 * // using the `_.property` callback shorthand
11716 * _.pluck(_.filter(users, 'active'), 'user');
11719 function filter(collection, predicate, thisArg) {
11720 var func = isArray(collection) ? arrayFilter : baseFilter;
11721 predicate = getCallback(predicate, thisArg, 3);
11722 return func(collection, predicate);
11726 * Iterates over elements of `collection`, returning the first element
11727 * `predicate` returns truthy for. The predicate is bound to `thisArg` and
11728 * invoked with three arguments: (value, index|key, collection).
11730 * If a property name is provided for `predicate` the created `_.property`
11731 * style callback returns the property value of the given element.
11733 * If a value is also provided for `thisArg` the created `_.matchesProperty`
11734 * style callback returns `true` for elements that have a matching property
11735 * value, else `false`.
11737 * If an object is provided for `predicate` the created `_.matches` style
11738 * callback returns `true` for elements that have the properties of the given
11739 * object, else `false`.
11744 * @category Collection
11745 * @param {Array|Object|string} collection The collection to search.
11746 * @param {Function|Object|string} [predicate=_.identity] The function invoked
11748 * @param {*} [thisArg] The `this` binding of `predicate`.
11749 * @returns {*} Returns the matched element, else `undefined`.
11753 * { 'user': 'barney', 'age': 36, 'active': true },
11754 * { 'user': 'fred', 'age': 40, 'active': false },
11755 * { 'user': 'pebbles', 'age': 1, 'active': true }
11758 * _.result(_.find(users, function(chr) {
11759 * return chr.age < 40;
11763 * // using the `_.matches` callback shorthand
11764 * _.result(_.find(users, { 'age': 1, 'active': true }), 'user');
11767 * // using the `_.matchesProperty` callback shorthand
11768 * _.result(_.find(users, 'active', false), 'user');
11771 * // using the `_.property` callback shorthand
11772 * _.result(_.find(users, 'active'), 'user');
11775 var find = createFind(baseEach);
11778 * Iterates over elements of `collection` invoking `iteratee` for each element.
11779 * The `iteratee` is bound to `thisArg` and invoked with three arguments:
11780 * (value, index|key, collection). Iteratee functions may exit iteration early
11781 * by explicitly returning `false`.
11783 * **Note:** As with other "Collections" methods, objects with a "length" property
11784 * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
11785 * may be used for object iteration.
11790 * @category Collection
11791 * @param {Array|Object|string} collection The collection to iterate over.
11792 * @param {Function} [iteratee=_.identity] The function invoked per iteration.
11793 * @param {*} [thisArg] The `this` binding of `iteratee`.
11794 * @returns {Array|Object|string} Returns `collection`.
11797 * _([1, 2]).forEach(function(n) {
11800 * // => logs each value from left to right and returns the array
11802 * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) {
11803 * console.log(n, key);
11805 * // => logs each value-key pair and returns the object (iteration order is not guaranteed)
11807 var forEach = createForEach(arrayEach, baseEach);
11810 * Creates an object composed of keys generated from the results of running
11811 * each element of `collection` through `iteratee`. The corresponding value
11812 * of each key is an array of the elements responsible for generating the key.
11813 * The `iteratee` is bound to `thisArg` and invoked with three arguments:
11814 * (value, index|key, collection).
11816 * If a property name is provided for `iteratee` the created `_.property`
11817 * style callback returns the property value of the given element.
11819 * If a value is also provided for `thisArg` the created `_.matchesProperty`
11820 * style callback returns `true` for elements that have a matching property
11821 * value, else `false`.
11823 * If an object is provided for `iteratee` the created `_.matches` style
11824 * callback returns `true` for elements that have the properties of the given
11825 * object, else `false`.
11829 * @category Collection
11830 * @param {Array|Object|string} collection The collection to iterate over.
11831 * @param {Function|Object|string} [iteratee=_.identity] The function invoked
11833 * @param {*} [thisArg] The `this` binding of `iteratee`.
11834 * @returns {Object} Returns the composed aggregate object.
11837 * _.groupBy([4.2, 6.1, 6.4], function(n) {
11838 * return Math.floor(n);
11840 * // => { '4': [4.2], '6': [6.1, 6.4] }
11842 * _.groupBy([4.2, 6.1, 6.4], function(n) {
11843 * return this.floor(n);
11845 * // => { '4': [4.2], '6': [6.1, 6.4] }
11847 * // using the `_.property` callback shorthand
11848 * _.groupBy(['one', 'two', 'three'], 'length');
11849 * // => { '3': ['one', 'two'], '5': ['three'] }
11851 var groupBy = createAggregator(function(result, value, key) {
11852 if (hasOwnProperty.call(result, key)) {
11853 result[key].push(value);
11855 result[key] = [value];
11860 * Checks if `value` is in `collection` using
11861 * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11862 * for equality comparisons. If `fromIndex` is negative, it is used as the offset
11863 * from the end of `collection`.
11867 * @alias contains, include
11868 * @category Collection
11869 * @param {Array|Object|string} collection The collection to search.
11870 * @param {*} target The value to search for.
11871 * @param {number} [fromIndex=0] The index to search from.
11872 * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`.
11873 * @returns {boolean} Returns `true` if a matching element is found, else `false`.
11876 * _.includes([1, 2, 3], 1);
11879 * _.includes([1, 2, 3], 1, 2);
11882 * _.includes({ 'user': 'fred', 'age': 40 }, 'fred');
11885 * _.includes('pebbles', 'eb');
11888 function includes(collection, target, fromIndex, guard) {
11889 var length = collection ? getLength(collection) : 0;
11890 if (!isLength(length)) {
11891 collection = values(collection);
11892 length = collection.length;
11897 if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) {
11900 fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
11902 return (typeof collection == 'string' || !isArray(collection) && isString(collection))
11903 ? (fromIndex < length && collection.indexOf(target, fromIndex) > -1)
11904 : (getIndexOf(collection, target, fromIndex) > -1);
11908 * Creates an array of values by running each element in `collection` through
11909 * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three
11910 * arguments: (value, index|key, collection).
11912 * If a property name is provided for `iteratee` the created `_.property`
11913 * style callback returns the property value of the given element.
11915 * If a value is also provided for `thisArg` the created `_.matchesProperty`
11916 * style callback returns `true` for elements that have a matching property
11917 * value, else `false`.
11919 * If an object is provided for `iteratee` the created `_.matches` style
11920 * callback returns `true` for elements that have the properties of the given
11921 * object, else `false`.
11923 * Many lodash methods are guarded to work as iteratees for methods like
11924 * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
11926 * The guarded methods are:
11927 * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`,
11928 * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`,
11929 * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`,
11930 * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`,
11931 * `sum`, `uniq`, and `words`
11936 * @category Collection
11937 * @param {Array|Object|string} collection The collection to iterate over.
11938 * @param {Function|Object|string} [iteratee=_.identity] The function invoked
11940 * @param {*} [thisArg] The `this` binding of `iteratee`.
11941 * @returns {Array} Returns the new mapped array.
11944 * function timesThree(n) {
11948 * _.map([1, 2], timesThree);
11951 * _.map({ 'a': 1, 'b': 2 }, timesThree);
11952 * // => [3, 6] (iteration order is not guaranteed)
11955 * { 'user': 'barney' },
11956 * { 'user': 'fred' }
11959 * // using the `_.property` callback shorthand
11960 * _.map(users, 'user');
11961 * // => ['barney', 'fred']
11963 function map(collection, iteratee, thisArg) {
11964 var func = isArray(collection) ? arrayMap : baseMap;
11965 iteratee = getCallback(iteratee, thisArg, 3);
11966 return func(collection, iteratee);
11970 * Gets the property value of `path` from all elements in `collection`.
11974 * @category Collection
11975 * @param {Array|Object|string} collection The collection to iterate over.
11976 * @param {Array|string} path The path of the property to pluck.
11977 * @returns {Array} Returns the property values.
11981 * { 'user': 'barney', 'age': 36 },
11982 * { 'user': 'fred', 'age': 40 }
11985 * _.pluck(users, 'user');
11986 * // => ['barney', 'fred']
11988 * var userIndex = _.indexBy(users, 'user');
11989 * _.pluck(userIndex, 'age');
11990 * // => [36, 40] (iteration order is not guaranteed)
11992 function pluck(collection, path) {
11993 return map(collection, property(path));
11997 * Reduces `collection` to a value which is the accumulated result of running
11998 * each element in `collection` through `iteratee`, where each successive
11999 * invocation is supplied the return value of the previous. If `accumulator`
12000 * is not provided the first element of `collection` is used as the initial
12001 * value. The `iteratee` is bound to `thisArg` and invoked with four arguments:
12002 * (accumulator, value, index|key, collection).
12004 * Many lodash methods are guarded to work as iteratees for methods like
12005 * `_.reduce`, `_.reduceRight`, and `_.transform`.
12007 * The guarded methods are:
12008 * `assign`, `defaults`, `includes`, `merge`, `sortByAll`, and `sortByOrder`
12012 * @alias foldl, inject
12013 * @category Collection
12014 * @param {Array|Object|string} collection The collection to iterate over.
12015 * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12016 * @param {*} [accumulator] The initial value.
12017 * @param {*} [thisArg] The `this` binding of `iteratee`.
12018 * @returns {*} Returns the accumulated value.
12021 * _.reduce([1, 2], function(total, n) {
12022 * return total + n;
12026 * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) {
12027 * result[key] = n * 3;
12030 * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed)
12032 var reduce = createReduce(arrayReduce, baseEach);
12035 * The opposite of `_.filter`; this method returns the elements of `collection`
12036 * that `predicate` does **not** return truthy for.
12040 * @category Collection
12041 * @param {Array|Object|string} collection The collection to iterate over.
12042 * @param {Function|Object|string} [predicate=_.identity] The function invoked
12044 * @param {*} [thisArg] The `this` binding of `predicate`.
12045 * @returns {Array} Returns the new filtered array.
12048 * _.reject([1, 2, 3, 4], function(n) {
12049 * return n % 2 == 0;
12054 * { 'user': 'barney', 'age': 36, 'active': false },
12055 * { 'user': 'fred', 'age': 40, 'active': true }
12058 * // using the `_.matches` callback shorthand
12059 * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user');
12062 * // using the `_.matchesProperty` callback shorthand
12063 * _.pluck(_.reject(users, 'active', false), 'user');
12066 * // using the `_.property` callback shorthand
12067 * _.pluck(_.reject(users, 'active'), 'user');
12070 function reject(collection, predicate, thisArg) {
12071 var func = isArray(collection) ? arrayFilter : baseFilter;
12072 predicate = getCallback(predicate, thisArg, 3);
12073 return func(collection, function(value, index, collection) {
12074 return !predicate(value, index, collection);
12079 * Checks if `predicate` returns truthy for **any** element of `collection`.
12080 * The function returns as soon as it finds a passing value and does not iterate
12081 * over the entire collection. The predicate is bound to `thisArg` and invoked
12082 * with three arguments: (value, index|key, collection).
12084 * If a property name is provided for `predicate` the created `_.property`
12085 * style callback returns the property value of the given element.
12087 * If a value is also provided for `thisArg` the created `_.matchesProperty`
12088 * style callback returns `true` for elements that have a matching property
12089 * value, else `false`.
12091 * If an object is provided for `predicate` the created `_.matches` style
12092 * callback returns `true` for elements that have the properties of the given
12093 * object, else `false`.
12098 * @category Collection
12099 * @param {Array|Object|string} collection The collection to iterate over.
12100 * @param {Function|Object|string} [predicate=_.identity] The function invoked
12102 * @param {*} [thisArg] The `this` binding of `predicate`.
12103 * @returns {boolean} Returns `true` if any element passes the predicate check,
12107 * _.some([null, 0, 'yes', false], Boolean);
12111 * { 'user': 'barney', 'active': true },
12112 * { 'user': 'fred', 'active': false }
12115 * // using the `_.matches` callback shorthand
12116 * _.some(users, { 'user': 'barney', 'active': false });
12119 * // using the `_.matchesProperty` callback shorthand
12120 * _.some(users, 'active', false);
12123 * // using the `_.property` callback shorthand
12124 * _.some(users, 'active');
12127 function some(collection, predicate, thisArg) {
12128 var func = isArray(collection) ? arraySome : baseSome;
12129 if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
12132 if (typeof predicate != 'function' || thisArg !== undefined) {
12133 predicate = getCallback(predicate, thisArg, 3);
12135 return func(collection, predicate);
12138 /*------------------------------------------------------------------------*/
12141 * Gets the number of milliseconds that have elapsed since the Unix epoch
12142 * (1 January 1970 00:00:00 UTC).
12149 * _.defer(function(stamp) {
12150 * console.log(_.now() - stamp);
12152 * // => logs the number of milliseconds it took for the deferred function to be invoked
12154 var now = nativeNow || function() {
12155 return new Date().getTime();
12158 /*------------------------------------------------------------------------*/
12161 * Creates a function that invokes `func` with the `this` binding of `thisArg`
12162 * and prepends any additional `_.bind` arguments to those provided to the
12165 * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
12166 * may be used as a placeholder for partially applied arguments.
12168 * **Note:** Unlike native `Function#bind` this method does not set the "length"
12169 * property of bound functions.
12173 * @category Function
12174 * @param {Function} func The function to bind.
12175 * @param {*} thisArg The `this` binding of `func`.
12176 * @param {...*} [partials] The arguments to be partially applied.
12177 * @returns {Function} Returns the new bound function.
12180 * var greet = function(greeting, punctuation) {
12181 * return greeting + ' ' + this.user + punctuation;
12184 * var object = { 'user': 'fred' };
12186 * var bound = _.bind(greet, object, 'hi');
12190 * // using placeholders
12191 * var bound = _.bind(greet, object, _, '!');
12195 var bind = restParam(function(func, thisArg, partials) {
12196 var bitmask = BIND_FLAG;
12197 if (partials.length) {
12198 var holders = replaceHolders(partials, bind.placeholder);
12199 bitmask |= PARTIAL_FLAG;
12201 return createWrapper(func, bitmask, thisArg, partials, holders);
12205 * Creates a debounced function that delays invoking `func` until after `wait`
12206 * milliseconds have elapsed since the last time the debounced function was
12207 * invoked. The debounced function comes with a `cancel` method to cancel
12208 * delayed invocations. Provide an options object to indicate that `func`
12209 * should be invoked on the leading and/or trailing edge of the `wait` timeout.
12210 * Subsequent calls to the debounced function return the result of the last
12211 * `func` invocation.
12213 * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
12214 * on the trailing edge of the timeout only if the the debounced function is
12215 * invoked more than once during the `wait` timeout.
12217 * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
12218 * for details over the differences between `_.debounce` and `_.throttle`.
12222 * @category Function
12223 * @param {Function} func The function to debounce.
12224 * @param {number} [wait=0] The number of milliseconds to delay.
12225 * @param {Object} [options] The options object.
12226 * @param {boolean} [options.leading=false] Specify invoking on the leading
12227 * edge of the timeout.
12228 * @param {number} [options.maxWait] The maximum time `func` is allowed to be
12229 * delayed before it is invoked.
12230 * @param {boolean} [options.trailing=true] Specify invoking on the trailing
12231 * edge of the timeout.
12232 * @returns {Function} Returns the new debounced function.
12235 * // avoid costly calculations while the window size is in flux
12236 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
12238 * // invoke `sendMail` when the click event is fired, debouncing subsequent calls
12239 * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
12241 * 'trailing': false
12244 * // ensure `batchLog` is invoked once after 1 second of debounced calls
12245 * var source = new EventSource('/stream');
12246 * jQuery(source).on('message', _.debounce(batchLog, 250, {
12250 * // cancel a debounced call
12251 * var todoChanges = _.debounce(batchLog, 1000);
12252 * Object.observe(models.todo, todoChanges);
12254 * Object.observe(models, function(changes) {
12255 * if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
12256 * todoChanges.cancel();
12260 * // ...at some point `models.todo` is changed
12261 * models.todo.completed = true;
12263 * // ...before 1 second has passed `models.todo` is deleted
12264 * // which cancels the debounced `todoChanges` call
12265 * delete models.todo;
12267 function debounce(func, wait, options) {
12279 if (typeof func != 'function') {
12280 throw new TypeError(FUNC_ERROR_TEXT);
12282 wait = wait < 0 ? 0 : (+wait || 0);
12283 if (options === true) {
12284 var leading = true;
12286 } else if (isObject(options)) {
12287 leading = options.leading;
12288 maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
12289 trailing = 'trailing' in options ? options.trailing : trailing;
12292 function cancel() {
12294 clearTimeout(timeoutId);
12296 if (maxTimeoutId) {
12297 clearTimeout(maxTimeoutId);
12299 maxTimeoutId = timeoutId = trailingCall = undefined;
12302 function delayed() {
12303 var remaining = wait - (now() - stamp);
12304 if (remaining <= 0 || remaining > wait) {
12305 if (maxTimeoutId) {
12306 clearTimeout(maxTimeoutId);
12308 var isCalled = trailingCall;
12309 maxTimeoutId = timeoutId = trailingCall = undefined;
12311 lastCalled = now();
12312 result = func.apply(thisArg, args);
12313 if (!timeoutId && !maxTimeoutId) {
12314 args = thisArg = null;
12318 timeoutId = setTimeout(delayed, remaining);
12322 function maxDelayed() {
12324 clearTimeout(timeoutId);
12326 maxTimeoutId = timeoutId = trailingCall = undefined;
12327 if (trailing || (maxWait !== wait)) {
12328 lastCalled = now();
12329 result = func.apply(thisArg, args);
12330 if (!timeoutId && !maxTimeoutId) {
12331 args = thisArg = null;
12336 function debounced() {
12340 trailingCall = trailing && (timeoutId || !leading);
12342 if (maxWait === false) {
12343 var leadingCall = leading && !timeoutId;
12345 if (!maxTimeoutId && !leading) {
12346 lastCalled = stamp;
12348 var remaining = maxWait - (stamp - lastCalled),
12349 isCalled = remaining <= 0 || remaining > maxWait;
12352 if (maxTimeoutId) {
12353 maxTimeoutId = clearTimeout(maxTimeoutId);
12355 lastCalled = stamp;
12356 result = func.apply(thisArg, args);
12358 else if (!maxTimeoutId) {
12359 maxTimeoutId = setTimeout(maxDelayed, remaining);
12362 if (isCalled && timeoutId) {
12363 timeoutId = clearTimeout(timeoutId);
12365 else if (!timeoutId && wait !== maxWait) {
12366 timeoutId = setTimeout(delayed, wait);
12370 result = func.apply(thisArg, args);
12372 if (isCalled && !timeoutId && !maxTimeoutId) {
12373 args = thisArg = null;
12377 debounced.cancel = cancel;
12382 * Creates a function that invokes `func` with the `this` binding of the
12383 * created function and arguments from `start` and beyond provided as an array.
12385 * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
12389 * @category Function
12390 * @param {Function} func The function to apply a rest parameter to.
12391 * @param {number} [start=func.length-1] The start position of the rest parameter.
12392 * @returns {Function} Returns the new function.
12395 * var say = _.restParam(function(what, names) {
12396 * return what + ' ' + _.initial(names).join(', ') +
12397 * (_.size(names) > 1 ? ', & ' : '') + _.last(names);
12400 * say('hello', 'fred', 'barney', 'pebbles');
12401 * // => 'hello fred, barney, & pebbles'
12403 function restParam(func, start) {
12404 if (typeof func != 'function') {
12405 throw new TypeError(FUNC_ERROR_TEXT);
12407 start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0);
12408 return function() {
12409 var args = arguments,
12411 length = nativeMax(args.length - start, 0),
12412 rest = Array(length);
12414 while (++index < length) {
12415 rest[index] = args[start + index];
12418 case 0: return func.call(this, rest);
12419 case 1: return func.call(this, args[0], rest);
12420 case 2: return func.call(this, args[0], args[1], rest);
12422 var otherArgs = Array(start + 1);
12424 while (++index < start) {
12425 otherArgs[index] = args[index];
12427 otherArgs[start] = rest;
12428 return func.apply(this, otherArgs);
12433 * Creates a throttled function that only invokes `func` at most once per
12434 * every `wait` milliseconds. The throttled function comes with a `cancel`
12435 * method to cancel delayed invocations. Provide an options object to indicate
12436 * that `func` should be invoked on the leading and/or trailing edge of the
12437 * `wait` timeout. Subsequent calls to the throttled function return the
12438 * result of the last `func` call.
12440 * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
12441 * on the trailing edge of the timeout only if the the throttled function is
12442 * invoked more than once during the `wait` timeout.
12444 * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
12445 * for details over the differences between `_.throttle` and `_.debounce`.
12449 * @category Function
12450 * @param {Function} func The function to throttle.
12451 * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
12452 * @param {Object} [options] The options object.
12453 * @param {boolean} [options.leading=true] Specify invoking on the leading
12454 * edge of the timeout.
12455 * @param {boolean} [options.trailing=true] Specify invoking on the trailing
12456 * edge of the timeout.
12457 * @returns {Function} Returns the new throttled function.
12460 * // avoid excessively updating the position while scrolling
12461 * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
12463 * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes
12464 * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
12465 * 'trailing': false
12468 * // cancel a trailing throttled call
12469 * jQuery(window).on('popstate', throttled.cancel);
12471 function throttle(func, wait, options) {
12472 var leading = true,
12475 if (typeof func != 'function') {
12476 throw new TypeError(FUNC_ERROR_TEXT);
12478 if (options === false) {
12480 } else if (isObject(options)) {
12481 leading = 'leading' in options ? !!options.leading : leading;
12482 trailing = 'trailing' in options ? !!options.trailing : trailing;
12484 debounceOptions.leading = leading;
12485 debounceOptions.maxWait = +wait;
12486 debounceOptions.trailing = trailing;
12487 return debounce(func, wait, debounceOptions);
12490 /*------------------------------------------------------------------------*/
12493 * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned,
12494 * otherwise they are assigned by reference. If `customizer` is provided it is
12495 * invoked to produce the cloned values. If `customizer` returns `undefined`
12496 * cloning is handled by the method instead. The `customizer` is bound to
12497 * `thisArg` and invoked with two argument; (value [, index|key, object]).
12499 * **Note:** This method is loosely based on the
12500 * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
12501 * The enumerable properties of `arguments` objects and objects created by
12502 * constructors other than `Object` are cloned to plain `Object` objects. An
12503 * empty object is returned for uncloneable values such as functions, DOM nodes,
12504 * Maps, Sets, and WeakMaps.
12509 * @param {*} value The value to clone.
12510 * @param {boolean} [isDeep] Specify a deep clone.
12511 * @param {Function} [customizer] The function to customize cloning values.
12512 * @param {*} [thisArg] The `this` binding of `customizer`.
12513 * @returns {*} Returns the cloned value.
12517 * { 'user': 'barney' },
12518 * { 'user': 'fred' }
12521 * var shallow = _.clone(users);
12522 * shallow[0] === users[0];
12525 * var deep = _.clone(users, true);
12526 * deep[0] === users[0];
12529 * // using a customizer callback
12530 * var el = _.clone(document.body, function(value) {
12531 * if (_.isElement(value)) {
12532 * return value.cloneNode(false);
12536 * el === document.body
12540 * el.childNodes.length;
12543 function clone(value, isDeep, customizer, thisArg) {
12544 if (isDeep && typeof isDeep != 'boolean' && isIterateeCall(value, isDeep, customizer)) {
12547 else if (typeof isDeep == 'function') {
12548 thisArg = customizer;
12549 customizer = isDeep;
12552 return typeof customizer == 'function'
12553 ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 1))
12554 : baseClone(value, isDeep);
12558 * Creates a deep clone of `value`. If `customizer` is provided it is invoked
12559 * to produce the cloned values. If `customizer` returns `undefined` cloning
12560 * is handled by the method instead. The `customizer` is bound to `thisArg`
12561 * and invoked with two argument; (value [, index|key, object]).
12563 * **Note:** This method is loosely based on the
12564 * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
12565 * The enumerable properties of `arguments` objects and objects created by
12566 * constructors other than `Object` are cloned to plain `Object` objects. An
12567 * empty object is returned for uncloneable values such as functions, DOM nodes,
12568 * Maps, Sets, and WeakMaps.
12573 * @param {*} value The value to deep clone.
12574 * @param {Function} [customizer] The function to customize cloning values.
12575 * @param {*} [thisArg] The `this` binding of `customizer`.
12576 * @returns {*} Returns the deep cloned value.
12580 * { 'user': 'barney' },
12581 * { 'user': 'fred' }
12584 * var deep = _.cloneDeep(users);
12585 * deep[0] === users[0];
12588 * // using a customizer callback
12589 * var el = _.cloneDeep(document.body, function(value) {
12590 * if (_.isElement(value)) {
12591 * return value.cloneNode(true);
12595 * el === document.body
12599 * el.childNodes.length;
12602 function cloneDeep(value, customizer, thisArg) {
12603 return typeof customizer == 'function'
12604 ? baseClone(value, true, bindCallback(customizer, thisArg, 1))
12605 : baseClone(value, true);
12609 * Checks if `value` is classified as an `arguments` object.
12614 * @param {*} value The value to check.
12615 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12618 * _.isArguments(function() { return arguments; }());
12621 * _.isArguments([1, 2, 3]);
12624 function isArguments(value) {
12625 return isObjectLike(value) && isArrayLike(value) && objToString.call(value) == argsTag;
12627 // Fallback for environments without a `toStringTag` for `arguments` objects.
12628 if (!support.argsTag) {
12629 isArguments = function(value) {
12630 return isObjectLike(value) && isArrayLike(value) &&
12631 hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');
12636 * Checks if `value` is classified as an `Array` object.
12641 * @param {*} value The value to check.
12642 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12645 * _.isArray([1, 2, 3]);
12648 * _.isArray(function() { return arguments; }());
12651 var isArray = nativeIsArray || function(value) {
12652 return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag;
12656 * Checks if `value` is empty. A value is considered empty unless it is an
12657 * `arguments` object, array, string, or jQuery-like collection with a length
12658 * greater than `0` or an object with own enumerable properties.
12663 * @param {Array|Object|string} value The value to inspect.
12664 * @returns {boolean} Returns `true` if `value` is empty, else `false`.
12676 * _.isEmpty([1, 2, 3]);
12679 * _.isEmpty({ 'a': 1 });
12682 function isEmpty(value) {
12683 if (value == null) {
12686 if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) ||
12687 (isObjectLike(value) && isFunction(value.splice)))) {
12688 return !value.length;
12690 return !keys(value).length;
12694 * Performs a deep comparison between two values to determine if they are
12695 * equivalent. If `customizer` is provided it is invoked to compare values.
12696 * If `customizer` returns `undefined` comparisons are handled by the method
12697 * instead. The `customizer` is bound to `thisArg` and invoked with three
12698 * arguments: (value, other [, index|key]).
12700 * **Note:** This method supports comparing arrays, booleans, `Date` objects,
12701 * numbers, `Object` objects, regexes, and strings. Objects are compared by
12702 * their own, not inherited, enumerable properties. Functions and DOM nodes
12703 * are **not** supported. Provide a customizer function to extend support
12704 * for comparing other values.
12710 * @param {*} value The value to compare.
12711 * @param {*} other The other value to compare.
12712 * @param {Function} [customizer] The function to customize value comparisons.
12713 * @param {*} [thisArg] The `this` binding of `customizer`.
12714 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
12717 * var object = { 'user': 'fred' };
12718 * var other = { 'user': 'fred' };
12723 * _.isEqual(object, other);
12726 * // using a customizer callback
12727 * var array = ['hello', 'goodbye'];
12728 * var other = ['hi', 'goodbye'];
12730 * _.isEqual(array, other, function(value, other) {
12731 * if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) {
12737 function isEqual(value, other, customizer, thisArg) {
12738 customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined;
12739 var result = customizer ? customizer(value, other) : undefined;
12740 return result === undefined ? baseIsEqual(value, other, customizer) : !!result;
12744 * Checks if `value` is classified as a `Function` object.
12749 * @param {*} value The value to check.
12750 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12756 * _.isFunction(/abc/);
12759 var isFunction = !(baseIsFunction(/x/) || (Uint8Array && !baseIsFunction(Uint8Array))) ? baseIsFunction : function(value) {
12760 // The use of `Object#toString` avoids issues with the `typeof` operator
12761 // in older versions of Chrome and Safari which return 'function' for regexes
12762 // and Safari 8 equivalents which return 'object' for typed array constructors.
12763 return objToString.call(value) == funcTag;
12767 * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
12768 * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
12773 * @param {*} value The value to check.
12774 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
12780 * _.isObject([1, 2, 3]);
12786 function isObject(value) {
12787 // Avoid a V8 JIT bug in Chrome 19-20.
12788 // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
12789 var type = typeof value;
12790 return !!value && (type == 'object' || type == 'function');
12794 * Checks if `value` is a native function.
12799 * @param {*} value The value to check.
12800 * @returns {boolean} Returns `true` if `value` is a native function, else `false`.
12803 * _.isNative(Array.prototype.push);
12809 function isNative(value) {
12810 if (value == null) {
12813 if (objToString.call(value) == funcTag) {
12814 return reIsNative.test(fnToString.call(value));
12816 return isObjectLike(value) && (isHostObject(value) ? reIsNative : reIsHostCtor).test(value);
12820 * Checks if `value` is a plain object, that is, an object created by the
12821 * `Object` constructor or one with a `[[Prototype]]` of `null`.
12823 * **Note:** This method assumes objects created by the `Object` constructor
12824 * have no inherited enumerable properties.
12829 * @param {*} value The value to check.
12830 * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
12837 * _.isPlainObject(new Foo);
12840 * _.isPlainObject([1, 2, 3]);
12843 * _.isPlainObject({ 'x': 0, 'y': 0 });
12846 * _.isPlainObject(Object.create(null));
12849 var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
12850 if (!(value && objToString.call(value) == objectTag) || (!lodash.support.argsTag && isArguments(value))) {
12853 var valueOf = getNative(value, 'valueOf'),
12854 objProto = valueOf && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
12857 ? (value == objProto || getPrototypeOf(value) == objProto)
12858 : shimIsPlainObject(value);
12862 * Checks if `value` is classified as a `String` primitive or object.
12867 * @param {*} value The value to check.
12868 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12871 * _.isString('abc');
12877 function isString(value) {
12878 return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag);
12882 * Checks if `value` is classified as a typed array.
12887 * @param {*} value The value to check.
12888 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12891 * _.isTypedArray(new Uint8Array);
12894 * _.isTypedArray([]);
12897 function isTypedArray(value) {
12898 return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
12902 * Converts `value` to a plain object flattening inherited enumerable
12903 * properties of `value` to own properties of the plain object.
12908 * @param {*} value The value to convert.
12909 * @returns {Object} Returns the converted plain object.
12916 * Foo.prototype.c = 3;
12918 * _.assign({ 'a': 1 }, new Foo);
12919 * // => { 'a': 1, 'b': 2 }
12921 * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
12922 * // => { 'a': 1, 'b': 2, 'c': 3 }
12924 function toPlainObject(value) {
12925 return baseCopy(value, keysIn(value));
12928 /*------------------------------------------------------------------------*/
12931 * Assigns own enumerable properties of source object(s) to the destination
12932 * object. Subsequent sources overwrite property assignments of previous sources.
12933 * If `customizer` is provided it is invoked to produce the assigned values.
12934 * The `customizer` is bound to `thisArg` and invoked with five arguments:
12935 * (objectValue, sourceValue, key, object, source).
12937 * **Note:** This method mutates `object` and is based on
12938 * [`Object.assign`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign).
12944 * @param {Object} object The destination object.
12945 * @param {...Object} [sources] The source objects.
12946 * @param {Function} [customizer] The function to customize assigned values.
12947 * @param {*} [thisArg] The `this` binding of `customizer`.
12948 * @returns {Object} Returns `object`.
12951 * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' });
12952 * // => { 'user': 'fred', 'age': 40 }
12954 * // using a customizer callback
12955 * var defaults = _.partialRight(_.assign, function(value, other) {
12956 * return _.isUndefined(value) ? other : value;
12959 * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
12960 * // => { 'user': 'barney', 'age': 36 }
12962 var assign = createAssigner(function(object, source, customizer) {
12964 ? assignWith(object, source, customizer)
12965 : baseAssign(object, source);
12969 * Iterates over own enumerable properties of an object invoking `iteratee`
12970 * for each property. The `iteratee` is bound to `thisArg` and invoked with
12971 * three arguments: (value, key, object). Iteratee functions may exit iteration
12972 * early by explicitly returning `false`.
12977 * @param {Object} object The object to iterate over.
12978 * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12979 * @param {*} [thisArg] The `this` binding of `iteratee`.
12980 * @returns {Object} Returns `object`.
12988 * Foo.prototype.c = 3;
12990 * _.forOwn(new Foo, function(value, key) {
12991 * console.log(key);
12993 * // => logs 'a' and 'b' (iteration order is not guaranteed)
12995 var forOwn = createForOwn(baseForOwn);
12998 * Creates an array of the own enumerable property names of `object`.
13000 * **Note:** Non-object values are coerced to objects. See the
13001 * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys)
13002 * for more details.
13007 * @param {Object} object The object to query.
13008 * @returns {Array} Returns the array of property names.
13016 * Foo.prototype.c = 3;
13019 * // => ['a', 'b'] (iteration order is not guaranteed)
13024 var keys = !nativeKeys ? shimKeys : function(object) {
13025 var Ctor = object == null ? null : object.constructor;
13026 if ((typeof Ctor == 'function' && Ctor.prototype === object) ||
13027 (typeof object == 'function' ? lodash.support.enumPrototypes : isArrayLike(object))) {
13028 return shimKeys(object);
13030 return isObject(object) ? nativeKeys(object) : [];
13034 * Creates an array of the own and inherited enumerable property names of `object`.
13036 * **Note:** Non-object values are coerced to objects.
13041 * @param {Object} object The object to query.
13042 * @returns {Array} Returns the array of property names.
13050 * Foo.prototype.c = 3;
13052 * _.keysIn(new Foo);
13053 * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
13055 function keysIn(object) {
13056 if (object == null) {
13059 if (!isObject(object)) {
13060 object = Object(object);
13062 var length = object.length,
13063 support = lodash.support;
13065 length = (length && isLength(length) &&
13066 (isArray(object) || isArguments(object) || isString(object)) && length) || 0;
13068 var Ctor = object.constructor,
13070 proto = (isFunction(Ctor) && Ctor.prototype) || objectProto,
13071 isProto = proto === object,
13072 result = Array(length),
13073 skipIndexes = length > 0,
13074 skipErrorProps = support.enumErrorProps && (object === errorProto || object instanceof Error),
13075 skipProto = support.enumPrototypes && isFunction(object);
13077 while (++index < length) {
13078 result[index] = (index + '');
13080 // lodash skips the `constructor` property when it infers it is iterating
13081 // over a `prototype` object because IE < 9 can't set the `[[Enumerable]]`
13082 // attribute of an existing property and the `constructor` property of a
13083 // prototype defaults to non-enumerable.
13084 for (var key in object) {
13085 if (!(skipProto && key == 'prototype') &&
13086 !(skipErrorProps && (key == 'message' || key == 'name')) &&
13087 !(skipIndexes && isIndex(key, length)) &&
13088 !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
13092 if (support.nonEnumShadows && object !== objectProto) {
13093 var tag = object === stringProto ? stringTag : (object === errorProto ? errorTag : objToString.call(object)),
13094 nonEnums = nonEnumProps[tag] || nonEnumProps[objectTag];
13096 if (tag == objectTag) {
13097 proto = objectProto;
13099 length = shadowProps.length;
13101 key = shadowProps[length];
13102 var nonEnum = nonEnums[key];
13103 if (!(isProto && nonEnum) &&
13104 (nonEnum ? hasOwnProperty.call(object, key) : object[key] !== proto[key])) {
13113 * Recursively merges own enumerable properties of the source object(s), that
13114 * don't resolve to `undefined` into the destination object. Subsequent sources
13115 * overwrite property assignments of previous sources. If `customizer` is
13116 * provided it is invoked to produce the merged values of the destination and
13117 * source properties. If `customizer` returns `undefined` merging is handled
13118 * by the method instead. The `customizer` is bound to `thisArg` and invoked
13119 * with five arguments: (objectValue, sourceValue, key, object, source).
13124 * @param {Object} object The destination object.
13125 * @param {...Object} [sources] The source objects.
13126 * @param {Function} [customizer] The function to customize assigned values.
13127 * @param {*} [thisArg] The `this` binding of `customizer`.
13128 * @returns {Object} Returns `object`.
13132 * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
13136 * 'data': [{ 'age': 36 }, { 'age': 40 }]
13139 * _.merge(users, ages);
13140 * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
13142 * // using a customizer callback
13144 * 'fruits': ['apple'],
13145 * 'vegetables': ['beet']
13149 * 'fruits': ['banana'],
13150 * 'vegetables': ['carrot']
13153 * _.merge(object, other, function(a, b) {
13154 * if (_.isArray(a)) {
13155 * return a.concat(b);
13158 * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
13160 var merge = createAssigner(baseMerge);
13163 * The opposite of `_.pick`; this method creates an object composed of the
13164 * own and inherited enumerable properties of `object` that are not omitted.
13169 * @param {Object} object The source object.
13170 * @param {Function|...(string|string[])} [predicate] The function invoked per
13171 * iteration or property names to omit, specified as individual property
13172 * names or arrays of property names.
13173 * @param {*} [thisArg] The `this` binding of `predicate`.
13174 * @returns {Object} Returns the new object.
13177 * var object = { 'user': 'fred', 'age': 40 };
13179 * _.omit(object, 'age');
13180 * // => { 'user': 'fred' }
13182 * _.omit(object, _.isNumber);
13183 * // => { 'user': 'fred' }
13185 var omit = restParam(function(object, props) {
13186 if (object == null) {
13189 if (typeof props[0] != 'function') {
13190 var props = arrayMap(baseFlatten(props), String);
13191 return pickByArray(object, baseDifference(keysIn(object), props));
13193 var predicate = bindCallback(props[0], props[1], 3);
13194 return pickByCallback(object, function(value, key, object) {
13195 return !predicate(value, key, object);
13200 * Creates a two dimensional array of the key-value pairs for `object`,
13201 * e.g. `[[key1, value1], [key2, value2]]`.
13206 * @param {Object} object The object to query.
13207 * @returns {Array} Returns the new array of key-value pairs.
13210 * _.pairs({ 'barney': 36, 'fred': 40 });
13211 * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed)
13213 function pairs(object) {
13214 object = toObject(object);
13217 props = keys(object),
13218 length = props.length,
13219 result = Array(length);
13221 while (++index < length) {
13222 var key = props[index];
13223 result[index] = [key, object[key]];
13229 * Creates an object composed of the picked `object` properties. Property
13230 * names may be specified as individual arguments or as arrays of property
13231 * names. If `predicate` is provided it is invoked for each property of `object`
13232 * picking the properties `predicate` returns truthy for. The predicate is
13233 * bound to `thisArg` and invoked with three arguments: (value, key, object).
13238 * @param {Object} object The source object.
13239 * @param {Function|...(string|string[])} [predicate] The function invoked per
13240 * iteration or property names to pick, specified as individual property
13241 * names or arrays of property names.
13242 * @param {*} [thisArg] The `this` binding of `predicate`.
13243 * @returns {Object} Returns the new object.
13246 * var object = { 'user': 'fred', 'age': 40 };
13248 * _.pick(object, 'user');
13249 * // => { 'user': 'fred' }
13251 * _.pick(object, _.isString);
13252 * // => { 'user': 'fred' }
13254 var pick = restParam(function(object, props) {
13255 if (object == null) {
13258 return typeof props[0] == 'function'
13259 ? pickByCallback(object, bindCallback(props[0], props[1], 3))
13260 : pickByArray(object, baseFlatten(props));
13264 * Creates an array of the own enumerable property values of `object`.
13266 * **Note:** Non-object values are coerced to objects.
13271 * @param {Object} object The object to query.
13272 * @returns {Array} Returns the array of property values.
13280 * Foo.prototype.c = 3;
13282 * _.values(new Foo);
13283 * // => [1, 2] (iteration order is not guaranteed)
13288 function values(object) {
13289 return baseValues(object, keys(object));
13292 /*------------------------------------------------------------------------*/
13295 * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?",
13296 * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`.
13301 * @param {string} [string=''] The string to escape.
13302 * @returns {string} Returns the escaped string.
13305 * _.escapeRegExp('[lodash](https://lodash.com/)');
13306 * // => '\[lodash\]\(https:\/\/lodash\.com\/\)'
13308 function escapeRegExp(string) {
13309 string = baseToString(string);
13310 return (string && reHasRegExpChars.test(string))
13311 ? string.replace(reRegExpChars, '\\$&')
13315 /*------------------------------------------------------------------------*/
13318 * Creates a function that invokes `func` with the `this` binding of `thisArg`
13319 * and arguments of the created function. If `func` is a property name the
13320 * created callback returns the property value for a given element. If `func`
13321 * is an object the created callback returns `true` for elements that contain
13322 * the equivalent object properties, otherwise it returns `false`.
13327 * @category Utility
13328 * @param {*} [func=_.identity] The value to convert to a callback.
13329 * @param {*} [thisArg] The `this` binding of `func`.
13330 * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
13331 * @returns {Function} Returns the callback.
13335 * { 'user': 'barney', 'age': 36 },
13336 * { 'user': 'fred', 'age': 40 }
13339 * // wrap to create custom callback shorthands
13340 * _.callback = _.wrap(_.callback, function(callback, func, thisArg) {
13341 * var match = /^(.+?)__([gl]t)(.+)$/.exec(func);
13343 * return callback(func, thisArg);
13345 * return function(object) {
13346 * return match[2] == 'gt'
13347 * ? object[match[1]] > match[3]
13348 * : object[match[1]] < match[3];
13352 * _.filter(users, 'age__gt36');
13353 * // => [{ 'user': 'fred', 'age': 40 }]
13355 function callback(func, thisArg, guard) {
13356 if (guard && isIterateeCall(func, thisArg, guard)) {
13359 return isObjectLike(func)
13361 : baseCallback(func, thisArg);
13365 * Creates a function that returns `value`.
13369 * @category Utility
13370 * @param {*} value The value to return from the new function.
13371 * @returns {Function} Returns the new function.
13374 * var object = { 'user': 'fred' };
13375 * var getter = _.constant(object);
13377 * getter() === object;
13380 function constant(value) {
13381 return function() {
13387 * This method returns the first argument provided to it.
13391 * @category Utility
13392 * @param {*} value Any value.
13393 * @returns {*} Returns `value`.
13396 * var object = { 'user': 'fred' };
13398 * _.identity(object) === object;
13401 function identity(value) {
13406 * Creates a function that performs a deep comparison between a given object
13407 * and `source`, returning `true` if the given object has equivalent property
13408 * values, else `false`.
13410 * **Note:** This method supports comparing arrays, booleans, `Date` objects,
13411 * numbers, `Object` objects, regexes, and strings. Objects are compared by
13412 * their own, not inherited, enumerable properties. For comparing a single
13413 * own or inherited property value see `_.matchesProperty`.
13417 * @category Utility
13418 * @param {Object} source The object of property values to match.
13419 * @returns {Function} Returns the new function.
13423 * { 'user': 'barney', 'age': 36, 'active': true },
13424 * { 'user': 'fred', 'age': 40, 'active': false }
13427 * _.filter(users, _.matches({ 'age': 40, 'active': false }));
13428 * // => [{ 'user': 'fred', 'age': 40, 'active': false }]
13430 function matches(source) {
13431 return baseMatches(baseClone(source, true));
13435 * Adds all own enumerable function properties of a source object to the
13436 * destination object. If `object` is a function then methods are added to
13437 * its prototype as well.
13439 * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
13440 * avoid conflicts caused by modifying the original.
13444 * @category Utility
13445 * @param {Function|Object} [object=lodash] The destination object.
13446 * @param {Object} source The object of functions to add.
13447 * @param {Object} [options] The options object.
13448 * @param {boolean} [options.chain=true] Specify whether the functions added
13450 * @returns {Function|Object} Returns `object`.
13453 * function vowels(string) {
13454 * return _.filter(string, function(v) {
13455 * return /[aeiou]/i.test(v);
13459 * _.mixin({ 'vowels': vowels });
13460 * _.vowels('fred');
13463 * _('fred').vowels().value();
13466 * _.mixin({ 'vowels': vowels }, { 'chain': false });
13467 * _('fred').vowels();
13470 function mixin(object, source, options) {
13471 if (options == null) {
13472 var isObj = isObject(source),
13473 props = isObj ? keys(source) : null,
13474 methodNames = (props && props.length) ? baseFunctions(source, props) : null;
13476 if (!(methodNames ? methodNames.length : isObj)) {
13477 methodNames = false;
13483 if (!methodNames) {
13484 methodNames = baseFunctions(source, keys(source));
13488 isFunc = isFunction(object),
13489 length = methodNames.length;
13491 if (options === false) {
13493 } else if (isObject(options) && 'chain' in options) {
13494 chain = options.chain;
13496 while (++index < length) {
13497 var methodName = methodNames[index],
13498 func = source[methodName];
13500 object[methodName] = func;
13502 object.prototype[methodName] = (function(func) {
13503 return function() {
13504 var chainAll = this.__chain__;
13505 if (chain || chainAll) {
13506 var result = object(this.__wrapped__),
13507 actions = result.__actions__ = arrayCopy(this.__actions__);
13509 actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
13510 result.__chain__ = chainAll;
13513 var args = [this.value()];
13514 push.apply(args, arguments);
13515 return func.apply(object, args);
13524 * A no-operation function that returns `undefined` regardless of the
13525 * arguments it receives.
13529 * @category Utility
13532 * var object = { 'user': 'fred' };
13534 * _.noop(object) === undefined;
13538 // No operation performed.
13542 * Creates a function that returns the property value at `path` on a
13547 * @category Utility
13548 * @param {Array|string} path The path of the property to get.
13549 * @returns {Function} Returns the new function.
13553 * { 'a': { 'b': { 'c': 2 } } },
13554 * { 'a': { 'b': { 'c': 1 } } }
13557 * _.map(objects, _.property('a.b.c'));
13560 * _.pluck(_.sortBy(objects, _.property(['a', 'b', 'c'])), 'a.b.c');
13563 function property(path) {
13564 return isKey(path) ? baseProperty(path) : basePropertyDeep(path);
13567 /*------------------------------------------------------------------------*/
13569 // Ensure wrappers are instances of `baseLodash`.
13570 lodash.prototype = baseLodash.prototype;
13572 LodashWrapper.prototype = baseCreate(baseLodash.prototype);
13573 LodashWrapper.prototype.constructor = LodashWrapper;
13575 LazyWrapper.prototype = baseCreate(baseLodash.prototype);
13576 LazyWrapper.prototype.constructor = LazyWrapper;
13578 // Add functions to the `Set` cache.
13579 SetCache.prototype.push = cachePush;
13581 // Add functions that return wrapped values when chaining.
13582 lodash.assign = assign;
13583 lodash.bind = bind;
13584 lodash.callback = callback;
13585 lodash.chain = chain;
13586 lodash.chunk = chunk;
13587 lodash.compact = compact;
13588 lodash.constant = constant;
13589 lodash.debounce = debounce;
13590 lodash.difference = difference;
13591 lodash.filter = filter;
13592 lodash.flatten = flatten;
13593 lodash.forEach = forEach;
13594 lodash.forOwn = forOwn;
13595 lodash.groupBy = groupBy;
13596 lodash.intersection = intersection;
13597 lodash.keys = keys;
13598 lodash.keysIn = keysIn;
13600 lodash.matches = matches;
13601 lodash.merge = merge;
13602 lodash.mixin = mixin;
13603 lodash.omit = omit;
13604 lodash.pairs = pairs;
13605 lodash.pick = pick;
13606 lodash.pluck = pluck;
13607 lodash.property = property;
13608 lodash.reject = reject;
13609 lodash.restParam = restParam;
13611 lodash.throttle = throttle;
13612 lodash.thru = thru;
13613 lodash.toPlainObject = toPlainObject;
13614 lodash.union = union;
13615 lodash.uniq = uniq;
13616 lodash.values = values;
13617 lodash.without = without;
13620 lodash.collect = map;
13621 lodash.each = forEach;
13622 lodash.extend = assign;
13623 lodash.iteratee = callback;
13624 lodash.select = filter;
13625 lodash.unique = uniq;
13627 // Add functions to `lodash.prototype`.
13628 mixin(lodash, lodash);
13630 /*------------------------------------------------------------------------*/
13632 // Add functions that return unwrapped values when chaining.
13633 lodash.clone = clone;
13634 lodash.cloneDeep = cloneDeep;
13635 lodash.escapeRegExp = escapeRegExp;
13636 lodash.every = every;
13637 lodash.find = find;
13638 lodash.first = first;
13639 lodash.identity = identity;
13640 lodash.includes = includes;
13641 lodash.indexOf = indexOf;
13642 lodash.isArguments = isArguments;
13643 lodash.isArray = isArray;
13644 lodash.isEmpty = isEmpty;
13645 lodash.isEqual = isEqual;
13646 lodash.isFunction = isFunction;
13647 lodash.isNative = isNative;
13648 lodash.isObject = isObject;
13649 lodash.isPlainObject = isPlainObject;
13650 lodash.isString = isString;
13651 lodash.isTypedArray = isTypedArray;
13652 lodash.last = last;
13653 lodash.noop = noop;
13655 lodash.reduce = reduce;
13656 lodash.some = some;
13659 lodash.all = every;
13661 lodash.contains = includes;
13662 lodash.eq = isEqual;
13663 lodash.detect = find;
13664 lodash.foldl = reduce;
13665 lodash.head = first;
13666 lodash.include = includes;
13667 lodash.inject = reduce;
13669 mixin(lodash, (function() {
13671 baseForOwn(lodash, function(func, methodName) {
13672 if (!lodash.prototype[methodName]) {
13673 source[methodName] = func;
13679 /*------------------------------------------------------------------------*/
13681 lodash.prototype.sample = function(n) {
13682 if (!this.__chain__ && n == null) {
13683 return sample(this.value());
13685 return this.thru(function(value) {
13686 return sample(value, n);
13690 /*------------------------------------------------------------------------*/
13693 * The semantic version number.
13699 lodash.VERSION = VERSION;
13701 // Assign default placeholders.
13702 bind.placeholder = lodash;
13704 // Add `LazyWrapper` methods that accept an `iteratee` value.
13705 arrayEach(['dropWhile', 'filter', 'map', 'takeWhile'], function(methodName, type) {
13706 var isFilter = type != LAZY_MAP_FLAG,
13707 isDropWhile = type == LAZY_DROP_WHILE_FLAG;
13709 LazyWrapper.prototype[methodName] = function(iteratee, thisArg) {
13710 var filtered = this.__filtered__,
13711 result = (filtered && isDropWhile) ? new LazyWrapper(this) : this.clone(),
13712 iteratees = result.__iteratees__ || (result.__iteratees__ = []);
13718 'iteratee': getCallback(iteratee, thisArg, 1),
13723 result.__filtered__ = filtered || isFilter;
13728 // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
13729 arrayEach(['drop', 'take'], function(methodName, index) {
13730 var whileName = methodName + 'While';
13732 LazyWrapper.prototype[methodName] = function(n) {
13733 var filtered = this.__filtered__,
13734 result = (filtered && !index) ? this.dropWhile() : this.clone();
13736 n = n == null ? 1 : nativeMax(floor(n) || 0, 0);
13739 result.__takeCount__ = nativeMin(result.__takeCount__, n);
13741 last(result.__iteratees__).limit = n;
13744 var views = result.__views__ || (result.__views__ = []);
13745 views.push({ 'size': n, 'type': methodName + (result.__dir__ < 0 ? 'Right' : '') });
13750 LazyWrapper.prototype[methodName + 'Right'] = function(n) {
13751 return this.reverse()[methodName](n).reverse();
13754 LazyWrapper.prototype[methodName + 'RightWhile'] = function(predicate, thisArg) {
13755 return this.reverse()[whileName](predicate, thisArg).reverse();
13759 // Add `LazyWrapper` methods for `_.first` and `_.last`.
13760 arrayEach(['first', 'last'], function(methodName, index) {
13761 var takeName = 'take' + (index ? 'Right' : '');
13763 LazyWrapper.prototype[methodName] = function() {
13764 return this[takeName](1).value()[0];
13768 // Add `LazyWrapper` methods for `_.initial` and `_.rest`.
13769 arrayEach(['initial', 'rest'], function(methodName, index) {
13770 var dropName = 'drop' + (index ? '' : 'Right');
13772 LazyWrapper.prototype[methodName] = function() {
13773 return this[dropName](1);
13777 // Add `LazyWrapper` methods for `_.pluck` and `_.where`.
13778 arrayEach(['pluck', 'where'], function(methodName, index) {
13779 var operationName = index ? 'filter' : 'map',
13780 createCallback = index ? baseMatches : property;
13782 LazyWrapper.prototype[methodName] = function(value) {
13783 return this[operationName](createCallback(value));
13787 LazyWrapper.prototype.compact = function() {
13788 return this.filter(identity);
13791 LazyWrapper.prototype.reject = function(predicate, thisArg) {
13792 predicate = getCallback(predicate, thisArg, 1);
13793 return this.filter(function(value) {
13794 return !predicate(value);
13798 LazyWrapper.prototype.slice = function(start, end) {
13799 start = start == null ? 0 : (+start || 0);
13803 result = this.takeRight(-start);
13804 } else if (start) {
13805 result = this.drop(start);
13807 if (end !== undefined) {
13809 result = end < 0 ? result.dropRight(-end) : result.take(end - start);
13814 LazyWrapper.prototype.toArray = function() {
13815 return this.drop(0);
13818 // Add `LazyWrapper` methods to `lodash.prototype`.
13819 baseForOwn(LazyWrapper.prototype, function(func, methodName) {
13820 var lodashFunc = lodash[methodName];
13824 var checkIteratee = /^(?:filter|map|reject)|While$/.test(methodName),
13825 retUnwrapped = /^(?:first|last)$/.test(methodName);
13827 lodash.prototype[methodName] = function() {
13828 var args = arguments,
13829 chainAll = this.__chain__,
13830 value = this.__wrapped__,
13831 isHybrid = !!this.__actions__.length,
13832 isLazy = value instanceof LazyWrapper,
13833 iteratee = args[0],
13834 useLazy = isLazy || isArray(value);
13836 if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
13837 // avoid lazy use if the iteratee has a "length" value other than `1`
13838 isLazy = useLazy = false;
13840 var onlyLazy = isLazy && !isHybrid;
13841 if (retUnwrapped && !chainAll) {
13844 : lodashFunc.call(lodash, this.value());
13846 var interceptor = function(value) {
13847 var otherArgs = [value];
13848 push.apply(otherArgs, args);
13849 return lodashFunc.apply(lodash, otherArgs);
13852 var wrapper = onlyLazy ? value : new LazyWrapper(this),
13853 result = func.apply(wrapper, args);
13855 if (!retUnwrapped && (isHybrid || result.__actions__)) {
13856 var actions = result.__actions__ || (result.__actions__ = []);
13857 actions.push({ 'func': thru, 'args': [interceptor], 'thisArg': lodash });
13859 return new LodashWrapper(result, chainAll);
13861 return this.thru(interceptor);
13865 // Add `Array` and `String` methods to `lodash.prototype`.
13866 arrayEach(['concat', 'join', 'pop', 'push', 'replace', 'shift', 'sort', 'splice', 'split', 'unshift'], function(methodName) {
13867 var protoFunc = (/^(?:replace|split)$/.test(methodName) ? stringProto : arrayProto)[methodName],
13868 chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
13869 fixObjects = !support.spliceObjects && /^(?:pop|shift|splice)$/.test(methodName),
13870 retUnwrapped = /^(?:join|pop|replace|shift)$/.test(methodName);
13872 // Avoid array-like object bugs with `Array#shift` and `Array#splice` in
13873 // IE < 9, Firefox < 10, and RingoJS.
13874 var func = !fixObjects ? protoFunc : function() {
13875 var result = protoFunc.apply(this, arguments);
13876 if (this.length === 0) {
13882 lodash.prototype[methodName] = function() {
13883 var args = arguments;
13884 if (retUnwrapped && !this.__chain__) {
13885 return func.apply(this.value(), args);
13887 return this[chainName](function(value) {
13888 return func.apply(value, args);
13893 // Map minified function names to their real names.
13894 baseForOwn(LazyWrapper.prototype, function(func, methodName) {
13895 var lodashFunc = lodash[methodName];
13897 var key = lodashFunc.name,
13898 names = realNames[key] || (realNames[key] = []);
13900 names.push({ 'name': methodName, 'func': lodashFunc });
13904 realNames[createHybridWrapper(null, BIND_KEY_FLAG).name] = [{ 'name': 'wrapper', 'func': null }];
13906 // Add functions to the lazy wrapper.
13907 LazyWrapper.prototype.clone = lazyClone;
13908 LazyWrapper.prototype.reverse = lazyReverse;
13909 LazyWrapper.prototype.value = lazyValue;
13911 // Add chaining functions to the `lodash` wrapper.
13912 lodash.prototype.chain = wrapperChain;
13913 lodash.prototype.commit = wrapperCommit;
13914 lodash.prototype.plant = wrapperPlant;
13915 lodash.prototype.reverse = wrapperReverse;
13916 lodash.prototype.toString = wrapperToString;
13917 lodash.prototype.run = lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
13919 // Add function aliases to the `lodash` wrapper.
13920 lodash.prototype.collect = lodash.prototype.map;
13921 lodash.prototype.head = lodash.prototype.first;
13922 lodash.prototype.select = lodash.prototype.filter;
13923 lodash.prototype.tail = lodash.prototype.rest;
13925 /*--------------------------------------------------------------------------*/
13927 if (freeExports && freeModule) {
13928 // Export for Node.js or RingoJS.
13929 if (moduleExports) {
13930 (freeModule.exports = lodash)._ = lodash;
13934 // Export for a browser or Rhino.
13938 (function(e){if("function"==typeof bootstrap)bootstrap("osmauth",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeOsmAuth=e}else"undefined"!=typeof window?window.osmAuth=e():global.osmAuth=e()})(function(){var define,ses,bootstrap,module,exports;
13939 return (function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){
13942 var ohauth = require('ohauth'),
13943 xtend = require('xtend'),
13944 store = require('store');
13948 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
13949 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
13950 // does not support custom headers, which this uses everywhere.
13951 module.exports = function(o) {
13955 // authenticated users will also have a request token secret, but it's
13956 // not used in transactions with the server
13957 oauth.authenticated = function() {
13958 return !!(token('oauth_token') && token('oauth_token_secret'));
13961 oauth.logout = function() {
13962 token('oauth_token', '');
13963 token('oauth_token_secret', '');
13964 token('oauth_request_token_secret', '');
13968 // TODO: detect lack of click event
13969 oauth.authenticate = function(callback) {
13970 if (oauth.authenticated()) return callback();
13974 // ## Getting a request token
13975 var params = timenonce(getAuth(o)),
13976 url = o.url + '/oauth/request_token',
13979 params.oauth_signature = ohauth.signature(
13980 o.oauth_secret, '',
13981 ohauth.baseString('POST', url, params));
13983 if (!o.singlepage) {
13984 // Create a 600x550 popup window in the center of the screen
13985 var w = 600, h = 550,
13987 ['width', w], ['height', h],
13988 ['left', screen.width / 2 - w / 2],
13989 ['top', screen.height / 2 - h / 2]].map(function(x) {
13990 return x.join('=');
13992 popup = window.open('about:blank', 'oauth_window', settings);
13995 timer = setInterval(function() {
13996 if (popup.closed) {
13998 clearInterval(timer);
13999 callback('not authenticated', null);
14004 // Request a request token. When this is complete, the popup
14005 // window is redirected to OSM's authorization page.
14006 ohauth.xhr('POST', url, params, null, {}, reqTokenDone);
14009 function reqTokenDone(err, xhr) {
14012 if (timer) clearInterval(timer);
14013 return callback(err);
14015 var resp = ohauth.stringQs(xhr.response);
14016 token('oauth_request_token_secret', resp.oauth_token_secret);
14017 var authorize_url = o.url + '/oauth/authorize?' + ohauth.qsString({
14018 oauth_token: resp.oauth_token,
14019 oauth_callback: location.href.replace('index.html', '')
14020 .replace(/#.*/, '') + o.landing
14023 if (o.singlepage) {
14024 location.href = authorize_url;
14026 popup.location = authorize_url;
14030 // Called by a function in a landing page, in the popup window. The
14031 // window closes itself.
14032 window.authComplete = function(token) {
14033 if (timer) clearInterval(timer);
14034 var oauth_token = ohauth.stringQs(token.split('?')[1]);
14035 get_access_token(oauth_token.oauth_token);
14036 delete window.authComplete;
14039 // ## Getting an request token
14041 // At this point we have an `oauth_token`, brought in from a function
14042 // call on a landing page popup.
14043 function get_access_token(oauth_token) {
14044 var url = o.url + '/oauth/access_token',
14045 params = timenonce(getAuth(o)),
14046 request_token_secret = token('oauth_request_token_secret');
14047 params.oauth_token = oauth_token;
14048 params.oauth_signature = ohauth.signature(
14050 request_token_secret,
14051 ohauth.baseString('POST', url, params));
14053 // ## Getting an access token
14055 // The final token required for authentication. At this point
14056 // we have a `request token secret`
14057 ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
14061 function accessTokenDone(err, xhr) {
14063 if (timer) clearInterval(timer);
14064 if (err) return callback(err);
14065 var access_token = ohauth.stringQs(xhr.response);
14066 token('oauth_token', access_token.oauth_token);
14067 token('oauth_token_secret', access_token.oauth_token_secret);
14068 callback(null, oauth);
14072 oauth.bootstrapToken = function(oauth_token, callback) {
14073 // ## Getting an request token
14074 // At this point we have an `oauth_token`, brought in from a function
14075 // call on a landing page popup.
14076 function get_access_token(oauth_token) {
14077 var url = o.url + '/oauth/access_token',
14078 params = timenonce(getAuth(o)),
14079 request_token_secret = token('oauth_request_token_secret');
14080 params.oauth_token = oauth_token;
14081 params.oauth_signature = ohauth.signature(
14083 request_token_secret,
14084 ohauth.baseString('POST', url, params));
14086 // ## Getting an access token
14087 // The final token required for authentication. At this point
14088 // we have a `request token secret`
14089 ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
14093 function accessTokenDone(err, xhr) {
14095 if (err) return callback(err);
14096 var access_token = ohauth.stringQs(xhr.response);
14097 token('oauth_token', access_token.oauth_token);
14098 token('oauth_token_secret', access_token.oauth_token_secret);
14099 callback(null, oauth);
14102 get_access_token(oauth_token);
14107 // A single XMLHttpRequest wrapper that does authenticated calls if the
14108 // user has logged in.
14109 oauth.xhr = function(options, callback) {
14110 if (!oauth.authenticated()) {
14111 if (o.auto) return oauth.authenticate(run);
14112 else return callback('not authenticated', null);
14113 } else return run();
14116 var params = timenonce(getAuth(o)),
14117 url = o.url + options.path,
14118 oauth_token_secret = token('oauth_token_secret');
14120 // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
14121 if ((!options.options || !options.options.header ||
14122 options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
14124 params = xtend(params, ohauth.stringQs(options.content));
14127 params.oauth_token = token('oauth_token');
14128 params.oauth_signature = ohauth.signature(
14130 oauth_token_secret,
14131 ohauth.baseString(options.method, url, params));
14133 ohauth.xhr(options.method,
14134 url, params, options.content, options.options, done);
14137 function done(err, xhr) {
14138 if (err) return callback(err);
14139 else if (xhr.responseXML) return callback(err, xhr.responseXML);
14140 else return callback(err, xhr.response);
14144 // pre-authorize this object, if we can just get a token and token_secret
14146 oauth.preauth = function(c) {
14148 if (c.oauth_token) token('oauth_token', c.oauth_token);
14149 if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
14153 oauth.options = function(_) {
14154 if (!arguments.length) return o;
14158 o.url = o.url || 'http://www.openstreetmap.org';
14159 o.landing = o.landing || 'land.html';
14161 o.singlepage = o.singlepage || false;
14163 // Optional loading and loading-done functions for nice UI feedback.
14164 // by default, no-ops
14165 o.loading = o.loading || function() {};
14166 o.done = o.done || function() {};
14168 return oauth.preauth(o);
14171 // 'stamp' an authentication object from `getAuth()`
14172 // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
14174 function timenonce(o) {
14175 o.oauth_timestamp = ohauth.timestamp();
14176 o.oauth_nonce = ohauth.nonce();
14180 // get/set tokens. These are prefixed with the base URL so that `osm-auth`
14181 // can be used with multiple APIs and the keys in `localStorage`
14185 if (store.enabled) {
14186 token = function (x, y) {
14187 if (arguments.length === 1) return store.get(o.url + x);
14188 else if (arguments.length === 2) return store.set(o.url + x, y);
14192 token = function (x, y) {
14193 if (arguments.length === 1) return storage[o.url + x];
14194 else if (arguments.length === 2) return storage[o.url + x] = y;
14198 // Get an authentication object. If you just add and remove properties
14199 // from a single object, you'll need to use `delete` to make sure that
14200 // it doesn't contain undesired properties for authentication
14201 function getAuth(o) {
14203 oauth_consumer_key: o.oauth_consumer_key,
14204 oauth_signature_method: "HMAC-SHA1"
14208 // potentially pre-authorize
14214 },{"ohauth":2,"store":3,"xtend":4}],3:[function(require,module,exports){
14215 (function(global){;(function(win){
14217 doc = win.document,
14218 localStorageName = 'localStorage',
14221 store.disabled = false
14222 store.set = function(key, value) {}
14223 store.get = function(key) {}
14224 store.remove = function(key) {}
14225 store.clear = function() {}
14226 store.transact = function(key, defaultVal, transactionFn) {
14227 var val = store.get(key)
14228 if (transactionFn == null) {
14229 transactionFn = defaultVal
14232 if (typeof val == 'undefined') { val = defaultVal || {} }
14234 store.set(key, val)
14236 store.getAll = function() {}
14237 store.forEach = function() {}
14239 store.serialize = function(value) {
14240 return JSON.stringify(value)
14242 store.deserialize = function(value) {
14243 if (typeof value != 'string') { return undefined }
14244 try { return JSON.parse(value) }
14245 catch(e) { return value || undefined }
14248 // Functions to encapsulate questionable FireFox 3.6.13 behavior
14249 // when about.config::dom.storage.enabled === false
14250 // See https://github.com/marcuswestin/store.js/issues#issue/13
14251 function isLocalStorageNameSupported() {
14252 try { return (localStorageName in win && win[localStorageName]) }
14253 catch(err) { return false }
14256 if (isLocalStorageNameSupported()) {
14257 storage = win[localStorageName]
14258 store.set = function(key, val) {
14259 if (val === undefined) { return store.remove(key) }
14260 storage.setItem(key, store.serialize(val))
14263 store.get = function(key) { return store.deserialize(storage.getItem(key)) }
14264 store.remove = function(key) { storage.removeItem(key) }
14265 store.clear = function() { storage.clear() }
14266 store.getAll = function() {
14268 store.forEach(function(key, val) {
14273 store.forEach = function(callback) {
14274 for (var i=0; i<storage.length; i++) {
14275 var key = storage.key(i)
14276 callback(key, store.get(key))
14279 } else if (doc.documentElement.addBehavior) {
14282 // Since #userData storage applies only to specific paths, we need to
14283 // somehow link our data to a specific path. We choose /favicon.ico
14284 // as a pretty safe option, since all browsers already make a request to
14285 // this URL anyway and being a 404 will not hurt us here. We wrap an
14286 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
14287 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
14288 // since the iframe access rules appear to allow direct access and
14289 // manipulation of the document element, even for a 404 page. This
14290 // document can be used instead of the current document (which would
14291 // have been limited to the current path) to perform #userData storage.
14293 storageContainer = new ActiveXObject('htmlfile')
14294 storageContainer.open()
14295 storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></iframe>')
14296 storageContainer.close()
14297 storageOwner = storageContainer.w.frames[0].document
14298 storage = storageOwner.createElement('div')
14300 // somehow ActiveXObject instantiation failed (perhaps some special
14301 // security settings or otherwse), fall back to per-path storage
14302 storage = doc.createElement('div')
14303 storageOwner = doc.body
14305 function withIEStorage(storeFunction) {
14306 return function() {
14307 var args = Array.prototype.slice.call(arguments, 0)
14308 args.unshift(storage)
14309 // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
14310 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
14311 storageOwner.appendChild(storage)
14312 storage.addBehavior('#default#userData')
14313 storage.load(localStorageName)
14314 var result = storeFunction.apply(store, args)
14315 storageOwner.removeChild(storage)
14320 // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
14321 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
14322 function ieKeyFix(key) {
14323 return key.replace(forbiddenCharsRegex, '___')
14325 store.set = withIEStorage(function(storage, key, val) {
14326 key = ieKeyFix(key)
14327 if (val === undefined) { return store.remove(key) }
14328 storage.setAttribute(key, store.serialize(val))
14329 storage.save(localStorageName)
14332 store.get = withIEStorage(function(storage, key) {
14333 key = ieKeyFix(key)
14334 return store.deserialize(storage.getAttribute(key))
14336 store.remove = withIEStorage(function(storage, key) {
14337 key = ieKeyFix(key)
14338 storage.removeAttribute(key)
14339 storage.save(localStorageName)
14341 store.clear = withIEStorage(function(storage) {
14342 var attributes = storage.XMLDocument.documentElement.attributes
14343 storage.load(localStorageName)
14344 for (var i=0, attr; attr=attributes[i]; i++) {
14345 storage.removeAttribute(attr.name)
14347 storage.save(localStorageName)
14349 store.getAll = function(storage) {
14351 store.forEach(function(key, val) {
14356 store.forEach = withIEStorage(function(storage, callback) {
14357 var attributes = storage.XMLDocument.documentElement.attributes
14358 for (var i=0, attr; attr=attributes[i]; ++i) {
14359 callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
14365 var testKey = '__storejs__'
14366 store.set(testKey, testKey)
14367 if (store.get(testKey) != testKey) { store.disabled = true }
14368 store.remove(testKey)
14370 store.disabled = true
14372 store.enabled = !store.disabled
14374 if (typeof module != 'undefined' && module.exports) { module.exports = store }
14375 else if (typeof define === 'function' && define.amd) { define(store) }
14376 else { win.store = store }
14378 })(this.window || global);
14381 },{}],5:[function(require,module,exports){
14382 module.exports = hasKeys
14384 function hasKeys(source) {
14385 return source !== null &&
14386 (typeof source === "object" ||
14387 typeof source === "function")
14390 },{}],4:[function(require,module,exports){
14391 var Keys = require("object-keys")
14392 var hasKeys = require("./has-keys")
14394 module.exports = extend
14396 function extend() {
14399 for (var i = 0; i < arguments.length; i++) {
14400 var source = arguments[i]
14402 if (!hasKeys(source)) {
14406 var keys = Keys(source)
14408 for (var j = 0; j < keys.length; j++) {
14410 target[name] = source[name]
14417 },{"./has-keys":5,"object-keys":6}],7:[function(require,module,exports){
14418 (function(global){/**
14419 * jsHashes - A fast and independent hashing library pure JavaScript implemented (ES3 compliant) for both server and client side
14422 * @author Tomas Aparicio <tomas@rijndael-project.com>
14423 * @license New BSD (see LICENSE file)
14426 * Algorithms specification:
14428 * MD5 <http://www.ietf.org/rfc/rfc1321.txt>
14429 * RIPEMD-160 <http://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
14430 * SHA1 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14431 * SHA256 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14432 * SHA512 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14433 * HMAC <http://www.ietf.org/rfc/rfc2104.txt>
14439 // private helper methods
14440 function utf8Encode(str) {
14441 var x, y, output = '', i = -1, l;
14443 if (str && str.length) {
14445 while ((i+=1) < l) {
14446 /* Decode utf-16 surrogate pairs */
14447 x = str.charCodeAt(i);
14448 y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
14449 if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
14450 x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
14453 /* Encode output as utf-8 */
14455 output += String.fromCharCode(x);
14456 } else if (x <= 0x7FF) {
14457 output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
14458 0x80 | ( x & 0x3F));
14459 } else if (x <= 0xFFFF) {
14460 output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
14461 0x80 | ((x >>> 6 ) & 0x3F),
14462 0x80 | ( x & 0x3F));
14463 } else if (x <= 0x1FFFFF) {
14464 output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
14465 0x80 | ((x >>> 12) & 0x3F),
14466 0x80 | ((x >>> 6 ) & 0x3F),
14467 0x80 | ( x & 0x3F));
14474 function utf8Decode(str) {
14475 var i, ac, c1, c2, c3, arr = [], l;
14476 i = ac = c1 = c2 = c3 = 0;
14478 if (str && str.length) {
14483 c1 = str.charCodeAt(i);
14486 arr[ac] = String.fromCharCode(c1);
14488 } else if (c1 > 191 && c1 < 224) {
14489 c2 = str.charCodeAt(i + 1);
14490 arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
14493 c2 = str.charCodeAt(i + 1);
14494 c3 = str.charCodeAt(i + 2);
14495 arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
14500 return arr.join('');
14504 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
14505 * to work around bugs in some JS interpreters.
14507 function safe_add(x, y) {
14508 var lsw = (x & 0xFFFF) + (y & 0xFFFF),
14509 msw = (x >> 16) + (y >> 16) + (lsw >> 16);
14510 return (msw << 16) | (lsw & 0xFFFF);
14514 * Bitwise rotate a 32-bit number to the left.
14516 function bit_rol(num, cnt) {
14517 return (num << cnt) | (num >>> (32 - cnt));
14521 * Convert a raw string to a hex string
14523 function rstr2hex(input, hexcase) {
14524 var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
14525 output = '', x, i = 0, l = input.length;
14526 for (; i < l; i+=1) {
14527 x = input.charCodeAt(i);
14528 output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
14534 * Encode a string as utf-16
14536 function str2rstr_utf16le(input) {
14537 var i, l = input.length, output = '';
14538 for (i = 0; i < l; i+=1) {
14539 output += String.fromCharCode( input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF);
14544 function str2rstr_utf16be(input) {
14545 var i, l = input.length, output = '';
14546 for (i = 0; i < l; i+=1) {
14547 output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF);
14553 * Convert an array of big-endian words to a string
14555 function binb2rstr(input) {
14556 var i, l = input.length * 32, output = '';
14557 for (i = 0; i < l; i += 8) {
14558 output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
14564 * Convert an array of little-endian words to a string
14566 function binl2rstr(input) {
14567 var i, l = input.length * 32, output = '';
14568 for (i = 0;i < l; i += 8) {
14569 output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
14575 * Convert a raw string to an array of little-endian words
14576 * Characters >255 have their high-byte silently ignored.
14578 function rstr2binl(input) {
14579 var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
14580 for (i = 0; i < lo; i+=1) {
14583 for (i = 0; i < l; i += 8) {
14584 output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
14590 * Convert a raw string to an array of big-endian words
14591 * Characters >255 have their high-byte silently ignored.
14593 function rstr2binb(input) {
14594 var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
14595 for (i = 0; i < lo; i+=1) {
14598 for (i = 0; i < l; i += 8) {
14599 output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
14605 * Convert a raw string to an arbitrary string encoding
14607 function rstr2any(input, encoding) {
14608 var divisor = encoding.length,
14609 remainders = Array(),
14610 i, q, x, ld, quotient, dividend, output, full_length;
14612 /* Convert to an array of 16-bit big-endian values, forming the dividend */
14613 dividend = Array(Math.ceil(input.length / 2));
14614 ld = dividend.length;
14615 for (i = 0; i < ld; i+=1) {
14616 dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
14620 * Repeatedly perform a long division. The binary array forms the dividend,
14621 * the length of the encoding is the divisor. Once computed, the quotient
14622 * forms the dividend for the next step. We stop when the dividend is zerHashes.
14623 * All remainders are stored for later use.
14625 while(dividend.length > 0) {
14626 quotient = Array();
14628 for (i = 0; i < dividend.length; i+=1) {
14629 x = (x << 16) + dividend[i];
14630 q = Math.floor(x / divisor);
14632 if (quotient.length > 0 || q > 0) {
14633 quotient[quotient.length] = q;
14636 remainders[remainders.length] = x;
14637 dividend = quotient;
14640 /* Convert the remainders to the output string */
14642 for (i = remainders.length - 1; i >= 0; i--) {
14643 output += encoding.charAt(remainders[i]);
14646 /* Append leading zero equivalents */
14647 full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
14648 for (i = output.length; i < full_length; i+=1) {
14649 output = encoding[0] + output;
14655 * Convert a raw string to a base-64 string
14657 function rstr2b64(input, b64pad) {
14658 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
14660 len = input.length, i, j, triplet;
14661 b64pad= b64pad || '=';
14662 for (i = 0; i < len; i += 3) {
14663 triplet = (input.charCodeAt(i) << 16)
14664 | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
14665 | (i + 2 < len ? input.charCodeAt(i+2) : 0);
14666 for (j = 0; j < 4; j+=1) {
14667 if (i * 8 + j * 6 > input.length * 8) {
14670 output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
14679 * @property {String} version
14688 Base64 : function () {
14689 // private properties
14690 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
14691 pad = '=', // default pad according with the RFC standard
14692 url = false, // URL encoding support @todo
14693 utf8 = true; // by default enable UTF-8 support encoding
14695 // public method for encoding
14696 this.encode = function (input) {
14699 len = input.length;
14702 input = (utf8) ? utf8Encode(input) : input;
14704 for (i = 0; i < len; i += 3) {
14705 triplet = (input.charCodeAt(i) << 16)
14706 | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
14707 | (i + 2 < len ? input.charCodeAt(i+2) : 0);
14708 for (j = 0; j < 4; j+=1) {
14709 if (i * 8 + j * 6 > len * 8) {
14712 output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
14719 // public method for decoding
14720 this.decode = function (input) {
14721 // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
14722 var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
14725 if (!input) { return input; }
14728 input = input.replace(new RegExp('\\'+pad,'gi'),''); // use '='
14731 do { // unpack four hexets into three octets using index points in b64
14732 h1 = tab.indexOf(input.charAt(i+=1));
14733 h2 = tab.indexOf(input.charAt(i+=1));
14734 h3 = tab.indexOf(input.charAt(i+=1));
14735 h4 = tab.indexOf(input.charAt(i+=1));
14737 bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
14739 o1 = bits >> 16 & 0xff;
14740 o2 = bits >> 8 & 0xff;
14745 arr[ac] = String.fromCharCode(o1);
14746 } else if (h4 === 64) {
14747 arr[ac] = String.fromCharCode(o1, o2);
14749 arr[ac] = String.fromCharCode(o1, o2, o3);
14751 } while (i < input.length);
14753 dec = arr.join('');
14754 dec = (utf8) ? utf8Decode(dec) : dec;
14759 // set custom pad string
14760 this.setPad = function (str) {
14764 // set custom tab string characters
14765 this.setTab = function (str) {
14769 this.setUTF8 = function (bool) {
14770 if (typeof bool === 'boolean') {
14778 * CRC-32 calculation
14782 * @param {String} str Input String
14785 CRC32 : function (str) {
14786 var crc = 0, x = 0, y = 0, table, i, iTop;
14787 str = utf8Encode(str);
14790 '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
14791 '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
14792 '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
14793 '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
14794 'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
14795 '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
14796 'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
14797 '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
14798 'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
14799 '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
14800 'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
14801 '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
14802 'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
14803 '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
14804 '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
14805 '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
14806 '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
14807 'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
14808 '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
14809 'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
14810 '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
14811 'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
14812 '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
14813 'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
14814 '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
14815 'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
14819 for (i = 0, iTop = str.length; i < iTop; i+=1 ) {
14820 y = ( crc ^ str.charCodeAt( i ) ) & 0xFF;
14821 x = '0x' + table.substr( y * 9, 8 );
14822 crc = ( crc >>> 8 ) ^ x;
14824 // always return a positive number (that's what >>> 0 does)
14825 return (crc ^ (-1)) >>> 0;
14831 * @param {Object} [config]
14833 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
14834 * Digest Algorithm, as defined in RFC 1321.
14835 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
14836 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
14837 * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
14839 MD5 : function (options) {
14841 * Private config properties. You may need to tweak these to be compatible with
14842 * the server-side, but the defaults work in most cases.
14843 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
14845 var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
14846 b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
14847 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
14849 // privileged (public) methods
14850 this.hex = function (s) {
14851 return rstr2hex(rstr(s, utf8), hexcase);
14853 this.b64 = function (s) {
14854 return rstr2b64(rstr(s), b64pad);
14856 this.any = function(s, e) {
14857 return rstr2any(rstr(s, utf8), e);
14859 this.hex_hmac = function (k, d) {
14860 return rstr2hex(rstr_hmac(k, d), hexcase);
14862 this.b64_hmac = function (k, d) {
14863 return rstr2b64(rstr_hmac(k,d), b64pad);
14865 this.any_hmac = function (k, d, e) {
14866 return rstr2any(rstr_hmac(k, d), e);
14869 * Perform a simple self-test to see if the VM is working
14870 * @return {String} Hexadecimal hash sample
14872 this.vm_test = function () {
14873 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
14876 * Enable/disable uppercase hexadecimal returned string
14878 * @return {Object} this
14880 this.setUpperCase = function (a) {
14881 if (typeof a === 'boolean' ) {
14887 * Defines a base64 pad string
14888 * @param {String} Pad
14889 * @return {Object} this
14891 this.setPad = function (a) {
14892 b64pad = a || b64pad;
14896 * Defines a base64 pad string
14898 * @return {Object} [this]
14900 this.setUTF8 = function (a) {
14901 if (typeof a === 'boolean') {
14910 * Calculate the MD5 of a raw string
14913 s = (utf8) ? utf8Encode(s): s;
14914 return binl2rstr(binl(rstr2binl(s), s.length * 8));
14918 * Calculate the HMAC-MD5, of a key and some data (raw strings)
14920 function rstr_hmac(key, data) {
14921 var bkey, ipad, opad, hash, i;
14923 key = (utf8) ? utf8Encode(key) : key;
14924 data = (utf8) ? utf8Encode(data) : data;
14925 bkey = rstr2binl(key);
14926 if (bkey.length > 16) {
14927 bkey = binl(bkey, key.length * 8);
14930 ipad = Array(16), opad = Array(16);
14931 for (i = 0; i < 16; i+=1) {
14932 ipad[i] = bkey[i] ^ 0x36363636;
14933 opad[i] = bkey[i] ^ 0x5C5C5C5C;
14935 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
14936 return binl2rstr(binl(opad.concat(hash), 512 + 128));
14940 * Calculate the MD5 of an array of little-endian words, and a bit length.
14942 function binl(x, len) {
14943 var i, olda, oldb, oldc, oldd,
14949 /* append padding */
14950 x[len >> 5] |= 0x80 << ((len) % 32);
14951 x[(((len + 64) >>> 9) << 4) + 14] = len;
14953 for (i = 0; i < x.length; i += 16) {
14959 a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
14960 d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
14961 c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
14962 b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
14963 a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
14964 d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
14965 c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
14966 b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
14967 a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
14968 d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
14969 c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
14970 b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
14971 a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
14972 d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
14973 c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
14974 b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
14976 a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
14977 d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
14978 c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
14979 b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
14980 a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
14981 d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
14982 c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
14983 b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
14984 a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
14985 d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
14986 c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
14987 b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
14988 a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
14989 d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
14990 c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
14991 b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
14993 a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
14994 d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
14995 c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
14996 b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
14997 a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
14998 d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
14999 c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
15000 b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
15001 a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
15002 d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
15003 c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
15004 b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
15005 a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
15006 d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
15007 c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
15008 b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
15010 a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
15011 d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
15012 c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
15013 b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
15014 a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
15015 d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
15016 c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
15017 b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
15018 a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
15019 d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
15020 c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
15021 b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
15022 a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
15023 d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
15024 c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
15025 b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
15027 a = safe_add(a, olda);
15028 b = safe_add(b, oldb);
15029 c = safe_add(c, oldc);
15030 d = safe_add(d, oldd);
15032 return Array(a, b, c, d);
15036 * These functions implement the four basic operations the algorithm uses.
15038 function md5_cmn(q, a, b, x, s, t) {
15039 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
15041 function md5_ff(a, b, c, d, x, s, t) {
15042 return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
15044 function md5_gg(a, b, c, d, x, s, t) {
15045 return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
15047 function md5_hh(a, b, c, d, x, s, t) {
15048 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
15050 function md5_ii(a, b, c, d, x, s, t) {
15051 return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
15056 * @class Hashes.SHA1
15057 * @param {Object} [config]
15060 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
15061 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
15062 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15063 * See http://pajhome.org.uk/crypt/md5 for details.
15065 SHA1 : function (options) {
15067 * Private config properties. You may need to tweak these to be compatible with
15068 * the server-side, but the defaults work in most cases.
15069 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
15071 var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
15072 b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
15073 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
15076 this.hex = function (s) {
15077 return rstr2hex(rstr(s, utf8), hexcase);
15079 this.b64 = function (s) {
15080 return rstr2b64(rstr(s, utf8), b64pad);
15082 this.any = function (s, e) {
15083 return rstr2any(rstr(s, utf8), e);
15085 this.hex_hmac = function (k, d) {
15086 return rstr2hex(rstr_hmac(k, d));
15088 this.b64_hmac = function (k, d) {
15089 return rstr2b64(rstr_hmac(k, d), b64pad);
15091 this.any_hmac = function (k, d, e) {
15092 return rstr2any(rstr_hmac(k, d), e);
15095 * Perform a simple self-test to see if the VM is working
15096 * @return {String} Hexadecimal hash sample
15099 this.vm_test = function () {
15100 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15103 * @description Enable/disable uppercase hexadecimal returned string
15105 * @return {Object} this
15108 this.setUpperCase = function (a) {
15109 if (typeof a === 'boolean') {
15115 * @description Defines a base64 pad string
15116 * @param {string} Pad
15117 * @return {Object} this
15120 this.setPad = function (a) {
15121 b64pad = a || b64pad;
15125 * @description Defines a base64 pad string
15127 * @return {Object} this
15130 this.setUTF8 = function (a) {
15131 if (typeof a === 'boolean') {
15140 * Calculate the SHA-512 of a raw string
15143 s = (utf8) ? utf8Encode(s) : s;
15144 return binb2rstr(binb(rstr2binb(s), s.length * 8));
15148 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
15150 function rstr_hmac(key, data) {
15151 var bkey, ipad, opad, i, hash;
15152 key = (utf8) ? utf8Encode(key) : key;
15153 data = (utf8) ? utf8Encode(data) : data;
15154 bkey = rstr2binb(key);
15156 if (bkey.length > 16) {
15157 bkey = binb(bkey, key.length * 8);
15159 ipad = Array(16), opad = Array(16);
15160 for (i = 0; i < 16; i+=1) {
15161 ipad[i] = bkey[i] ^ 0x36363636;
15162 opad[i] = bkey[i] ^ 0x5C5C5C5C;
15164 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
15165 return binb2rstr(binb(opad.concat(hash), 512 + 160));
15169 * Calculate the SHA-1 of an array of big-endian words, and a bit length
15171 function binb(x, len) {
15172 var i, j, t, olda, oldb, oldc, oldd, olde,
15180 /* append padding */
15181 x[len >> 5] |= 0x80 << (24 - len % 32);
15182 x[((len + 64 >> 9) << 4) + 15] = len;
15184 for (i = 0; i < x.length; i += 16) {
15191 for (j = 0; j < 80; j+=1) {
15195 w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
15197 t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
15198 safe_add(safe_add(e, w[j]), sha1_kt(j)));
15201 c = bit_rol(b, 30);
15206 a = safe_add(a, olda);
15207 b = safe_add(b, oldb);
15208 c = safe_add(c, oldc);
15209 d = safe_add(d, oldd);
15210 e = safe_add(e, olde);
15212 return Array(a, b, c, d, e);
15216 * Perform the appropriate triplet combination function for the current
15219 function sha1_ft(t, b, c, d) {
15220 if (t < 20) { return (b & c) | ((~b) & d); }
15221 if (t < 40) { return b ^ c ^ d; }
15222 if (t < 60) { return (b & c) | (b & d) | (c & d); }
15227 * Determine the appropriate additive constant for the current iteration
15229 function sha1_kt(t) {
15230 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
15231 (t < 60) ? -1894007588 : -899497514;
15235 * @class Hashes.SHA256
15238 * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
15239 * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
15240 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15241 * See http://pajhome.org.uk/crypt/md5 for details.
15242 * Also http://anmar.eu.org/projects/jssha2/
15244 SHA256 : function (options) {
15246 * Private properties configuration variables. You may need to tweak these to be compatible with
15247 * the server-side, but the defaults work in most cases.
15248 * @see this.setUpperCase() method
15249 * @see this.setPad() method
15251 var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase */
15252 b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', /* base-64 pad character. Default '=' for strict RFC compliance */
15253 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15256 /* privileged (public) methods */
15257 this.hex = function (s) {
15258 return rstr2hex(rstr(s, utf8));
15260 this.b64 = function (s) {
15261 return rstr2b64(rstr(s, utf8), b64pad);
15263 this.any = function (s, e) {
15264 return rstr2any(rstr(s, utf8), e);
15266 this.hex_hmac = function (k, d) {
15267 return rstr2hex(rstr_hmac(k, d));
15269 this.b64_hmac = function (k, d) {
15270 return rstr2b64(rstr_hmac(k, d), b64pad);
15272 this.any_hmac = function (k, d, e) {
15273 return rstr2any(rstr_hmac(k, d), e);
15276 * Perform a simple self-test to see if the VM is working
15277 * @return {String} Hexadecimal hash sample
15280 this.vm_test = function () {
15281 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15284 * Enable/disable uppercase hexadecimal returned string
15286 * @return {Object} this
15289 this.setUpperCase = function (a) {
15290 if (typeof a === 'boolean') {
15296 * @description Defines a base64 pad string
15297 * @param {string} Pad
15298 * @return {Object} this
15301 this.setPad = function (a) {
15302 b64pad = a || b64pad;
15306 * Defines a base64 pad string
15308 * @return {Object} this
15311 this.setUTF8 = function (a) {
15312 if (typeof a === 'boolean') {
15321 * Calculate the SHA-512 of a raw string
15323 function rstr(s, utf8) {
15324 s = (utf8) ? utf8Encode(s) : s;
15325 return binb2rstr(binb(rstr2binb(s), s.length * 8));
15329 * Calculate the HMAC-sha256 of a key and some data (raw strings)
15331 function rstr_hmac(key, data) {
15332 key = (utf8) ? utf8Encode(key) : key;
15333 data = (utf8) ? utf8Encode(data) : data;
15335 bkey = rstr2binb(key),
15339 if (bkey.length > 16) { bkey = binb(bkey, key.length * 8); }
15341 for (; i < 16; i+=1) {
15342 ipad[i] = bkey[i] ^ 0x36363636;
15343 opad[i] = bkey[i] ^ 0x5C5C5C5C;
15346 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
15347 return binb2rstr(binb(opad.concat(hash), 512 + 256));
15351 * Main sha256 function, with its support functions
15353 function sha256_S (X, n) {return ( X >>> n ) | (X << (32 - n));}
15354 function sha256_R (X, n) {return ( X >>> n );}
15355 function sha256_Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
15356 function sha256_Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
15357 function sha256_Sigma0256(x) {return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));}
15358 function sha256_Sigma1256(x) {return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));}
15359 function sha256_Gamma0256(x) {return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));}
15360 function sha256_Gamma1256(x) {return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));}
15361 function sha256_Sigma0512(x) {return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39));}
15362 function sha256_Sigma1512(x) {return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41));}
15363 function sha256_Gamma0512(x) {return (sha256_S(x, 1) ^ sha256_S(x, 8) ^ sha256_R(x, 7));}
15364 function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));}
15367 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,
15368 -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
15369 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
15370 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
15371 -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
15372 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
15373 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885,
15374 -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
15375 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
15376 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,
15377 -1866530822, -1538233109, -1090935817, -965641998
15380 function binb(m, l) {
15381 var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
15382 1359893119, -1694144372, 528734635, 1541459225];
15383 var W = new Array(64);
15384 var a, b, c, d, e, f, g, h;
15387 /* append padding */
15388 m[l >> 5] |= 0x80 << (24 - l % 32);
15389 m[((l + 64 >> 9) << 4) + 15] = l;
15391 for (i = 0; i < m.length; i += 16)
15402 for (j = 0; j < 64; j+=1)
15407 W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
15408 sha256_Gamma0256(W[j - 15])), W[j - 16]);
15411 T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
15412 sha256_K[j]), W[j]);
15413 T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
15417 e = safe_add(d, T1);
15421 a = safe_add(T1, T2);
15424 HASH[0] = safe_add(a, HASH[0]);
15425 HASH[1] = safe_add(b, HASH[1]);
15426 HASH[2] = safe_add(c, HASH[2]);
15427 HASH[3] = safe_add(d, HASH[3]);
15428 HASH[4] = safe_add(e, HASH[4]);
15429 HASH[5] = safe_add(f, HASH[5]);
15430 HASH[6] = safe_add(g, HASH[6]);
15431 HASH[7] = safe_add(h, HASH[7]);
15439 * @class Hashes.SHA512
15442 * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
15443 * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
15444 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15445 * See http://pajhome.org.uk/crypt/md5 for details.
15447 SHA512 : function (options) {
15449 * Private properties configuration variables. You may need to tweak these to be compatible with
15450 * the server-side, but the defaults work in most cases.
15451 * @see this.setUpperCase() method
15452 * @see this.setPad() method
15454 var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false , /* hexadecimal output case format. false - lowercase; true - uppercase */
15455 b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', /* base-64 pad character. Default '=' for strict RFC compliance */
15456 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15459 /* privileged (public) methods */
15460 this.hex = function (s) {
15461 return rstr2hex(rstr(s));
15463 this.b64 = function (s) {
15464 return rstr2b64(rstr(s), b64pad);
15466 this.any = function (s, e) {
15467 return rstr2any(rstr(s), e);
15469 this.hex_hmac = function (k, d) {
15470 return rstr2hex(rstr_hmac(k, d));
15472 this.b64_hmac = function (k, d) {
15473 return rstr2b64(rstr_hmac(k, d), b64pad);
15475 this.any_hmac = function (k, d, e) {
15476 return rstr2any(rstr_hmac(k, d), e);
15479 * Perform a simple self-test to see if the VM is working
15480 * @return {String} Hexadecimal hash sample
15483 this.vm_test = function () {
15484 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15487 * @description Enable/disable uppercase hexadecimal returned string
15489 * @return {Object} this
15492 this.setUpperCase = function (a) {
15493 if (typeof a === 'boolean') {
15499 * @description Defines a base64 pad string
15500 * @param {string} Pad
15501 * @return {Object} this
15504 this.setPad = function (a) {
15505 b64pad = a || b64pad;
15509 * @description Defines a base64 pad string
15511 * @return {Object} this
15514 this.setUTF8 = function (a) {
15515 if (typeof a === 'boolean') {
15521 /* private methods */
15524 * Calculate the SHA-512 of a raw string
15527 s = (utf8) ? utf8Encode(s) : s;
15528 return binb2rstr(binb(rstr2binb(s), s.length * 8));
15531 * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
15533 function rstr_hmac(key, data) {
15534 key = (utf8) ? utf8Encode(key) : key;
15535 data = (utf8) ? utf8Encode(data) : data;
15538 bkey = rstr2binb(key),
15539 ipad = Array(32), opad = Array(32);
15541 if (bkey.length > 32) { bkey = binb(bkey, key.length * 8); }
15543 for (; i < 32; i+=1) {
15544 ipad[i] = bkey[i] ^ 0x36363636;
15545 opad[i] = bkey[i] ^ 0x5C5C5C5C;
15548 hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
15549 return binb2rstr(binb(opad.concat(hash), 1024 + 512));
15553 * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
15555 function binb(x, len) {
15558 hash = new Array(16),
15559 //Initial hash values
15561 new int64(0x6a09e667, -205731576),
15562 new int64(-1150833019, -2067093701),
15563 new int64(0x3c6ef372, -23791573),
15564 new int64(-1521486534, 0x5f1d36f1),
15565 new int64(0x510e527f, -1377402159),
15566 new int64(-1694144372, 0x2b3e6c1f),
15567 new int64(0x1f83d9ab, -79577749),
15568 new int64(0x5be0cd19, 0x137e2179)
15570 T1 = new int64(0, 0),
15571 T2 = new int64(0, 0),
15572 a = new int64(0,0),
15573 b = new int64(0,0),
15574 c = new int64(0,0),
15575 d = new int64(0,0),
15576 e = new int64(0,0),
15577 f = new int64(0,0),
15578 g = new int64(0,0),
15579 h = new int64(0,0),
15580 //Temporary variables not specified by the document
15581 s0 = new int64(0, 0),
15582 s1 = new int64(0, 0),
15583 Ch = new int64(0, 0),
15584 Maj = new int64(0, 0),
15585 r1 = new int64(0, 0),
15586 r2 = new int64(0, 0),
15587 r3 = new int64(0, 0);
15589 if (sha512_k === undefined) {
15592 new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
15593 new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
15594 new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
15595 new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
15596 new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
15597 new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
15598 new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
15599 new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
15600 new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
15601 new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
15602 new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
15603 new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
15604 new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
15605 new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
15606 new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
15607 new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
15608 new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
15609 new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
15610 new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
15611 new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
15612 new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
15613 new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
15614 new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
15615 new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
15616 new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
15617 new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
15618 new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
15619 new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
15620 new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
15621 new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
15622 new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
15623 new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
15624 new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
15625 new int64(-354779690, -840897762), new int64(-176337025, -294727304),
15626 new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
15627 new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
15628 new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
15629 new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
15630 new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
15631 new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
15635 for (i=0; i<80; i+=1) {
15636 W[i] = new int64(0, 0);
15639 // append padding to the source string. The format is described in the FIPS.
15640 x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
15641 x[((len + 128 >> 10)<< 5) + 31] = len;
15643 for (i = 0; i<l; i+=32) { //32 dwords is the block size
15644 int64copy(a, H[0]);
15645 int64copy(b, H[1]);
15646 int64copy(c, H[2]);
15647 int64copy(d, H[3]);
15648 int64copy(e, H[4]);
15649 int64copy(f, H[5]);
15650 int64copy(g, H[6]);
15651 int64copy(h, H[7]);
15653 for (j=0; j<16; j+=1) {
15654 W[j].h = x[i + 2*j];
15655 W[j].l = x[i + 2*j + 1];
15658 for (j=16; j<80; j+=1) {
15660 int64rrot(r1, W[j-2], 19);
15661 int64revrrot(r2, W[j-2], 29);
15662 int64shr(r3, W[j-2], 6);
15663 s1.l = r1.l ^ r2.l ^ r3.l;
15664 s1.h = r1.h ^ r2.h ^ r3.h;
15666 int64rrot(r1, W[j-15], 1);
15667 int64rrot(r2, W[j-15], 8);
15668 int64shr(r3, W[j-15], 7);
15669 s0.l = r1.l ^ r2.l ^ r3.l;
15670 s0.h = r1.h ^ r2.h ^ r3.h;
15672 int64add4(W[j], s1, W[j-7], s0, W[j-16]);
15675 for (j = 0; j < 80; j+=1) {
15677 Ch.l = (e.l & f.l) ^ (~e.l & g.l);
15678 Ch.h = (e.h & f.h) ^ (~e.h & g.h);
15681 int64rrot(r1, e, 14);
15682 int64rrot(r2, e, 18);
15683 int64revrrot(r3, e, 9);
15684 s1.l = r1.l ^ r2.l ^ r3.l;
15685 s1.h = r1.h ^ r2.h ^ r3.h;
15688 int64rrot(r1, a, 28);
15689 int64revrrot(r2, a, 2);
15690 int64revrrot(r3, a, 7);
15691 s0.l = r1.l ^ r2.l ^ r3.l;
15692 s0.h = r1.h ^ r2.h ^ r3.h;
15695 Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
15696 Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
15698 int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
15699 int64add(T2, s0, Maj);
15704 int64add(e, d, T1);
15708 int64add(a, T1, T2);
15710 int64add(H[0], H[0], a);
15711 int64add(H[1], H[1], b);
15712 int64add(H[2], H[2], c);
15713 int64add(H[3], H[3], d);
15714 int64add(H[4], H[4], e);
15715 int64add(H[5], H[5], f);
15716 int64add(H[6], H[6], g);
15717 int64add(H[7], H[7], h);
15720 //represent the hash as an array of 32-bit dwords
15721 for (i=0; i<8; i+=1) {
15722 hash[2*i] = H[i].h;
15723 hash[2*i + 1] = H[i].l;
15728 //A constructor for 64-bit numbers
15729 function int64(h, l) {
15732 //this.toString = int64toString;
15735 //Copies src into dst, assuming both are 64-bit numbers
15736 function int64copy(dst, src) {
15741 //Right-rotates a 64-bit number by shift
15742 //Won't handle cases of shift>=32
15743 //The function revrrot() is for that
15744 function int64rrot(dst, x, shift) {
15745 dst.l = (x.l >>> shift) | (x.h << (32-shift));
15746 dst.h = (x.h >>> shift) | (x.l << (32-shift));
15749 //Reverses the dwords of the source and then rotates right by shift.
15750 //This is equivalent to rotation by 32+shift
15751 function int64revrrot(dst, x, shift) {
15752 dst.l = (x.h >>> shift) | (x.l << (32-shift));
15753 dst.h = (x.l >>> shift) | (x.h << (32-shift));
15756 //Bitwise-shifts right a 64-bit number by shift
15757 //Won't handle shift>=32, but it's never needed in SHA512
15758 function int64shr(dst, x, shift) {
15759 dst.l = (x.l >>> shift) | (x.h << (32-shift));
15760 dst.h = (x.h >>> shift);
15763 //Adds two 64-bit numbers
15764 //Like the original implementation, does not rely on 32-bit operations
15765 function int64add(dst, x, y) {
15766 var w0 = (x.l & 0xffff) + (y.l & 0xffff);
15767 var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
15768 var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
15769 var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
15770 dst.l = (w0 & 0xffff) | (w1 << 16);
15771 dst.h = (w2 & 0xffff) | (w3 << 16);
15774 //Same, except with 4 addends. Works faster than adding them one by one.
15775 function int64add4(dst, a, b, c, d) {
15776 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
15777 var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
15778 var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
15779 var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
15780 dst.l = (w0 & 0xffff) | (w1 << 16);
15781 dst.h = (w2 & 0xffff) | (w3 << 16);
15784 //Same, except with 5 addends
15785 function int64add5(dst, a, b, c, d, e) {
15786 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
15787 w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
15788 w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
15789 w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
15790 dst.l = (w0 & 0xffff) | (w1 << 16);
15791 dst.h = (w2 & 0xffff) | (w3 << 16);
15795 * @class Hashes.RMD160
15797 * @param {Object} [config]
15799 * A JavaScript implementation of the RIPEMD-160 Algorithm
15800 * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
15801 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15802 * See http://pajhome.org.uk/crypt/md5 for details.
15803 * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
15805 RMD160 : function (options) {
15807 * Private properties configuration variables. You may need to tweak these to be compatible with
15808 * the server-side, but the defaults work in most cases.
15809 * @see this.setUpperCase() method
15810 * @see this.setPad() method
15812 var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, /* hexadecimal output case format. false - lowercase; true - uppercase */
15813 b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', /* base-64 pad character. Default '=' for strict RFC compliance */
15814 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15816 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
15817 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
15818 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
15819 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
15820 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13
15823 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
15824 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
15825 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
15826 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
15827 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11
15830 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
15831 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
15832 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
15833 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
15834 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6
15837 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
15838 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
15839 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
15840 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
15841 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11
15844 /* privileged (public) methods */
15845 this.hex = function (s) {
15846 return rstr2hex(rstr(s, utf8));
15848 this.b64 = function (s) {
15849 return rstr2b64(rstr(s, utf8), b64pad);
15851 this.any = function (s, e) {
15852 return rstr2any(rstr(s, utf8), e);
15854 this.hex_hmac = function (k, d) {
15855 return rstr2hex(rstr_hmac(k, d));
15857 this.b64_hmac = function (k, d) {
15858 return rstr2b64(rstr_hmac(k, d), b64pad);
15860 this.any_hmac = function (k, d, e) {
15861 return rstr2any(rstr_hmac(k, d), e);
15864 * Perform a simple self-test to see if the VM is working
15865 * @return {String} Hexadecimal hash sample
15868 this.vm_test = function () {
15869 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15872 * @description Enable/disable uppercase hexadecimal returned string
15874 * @return {Object} this
15877 this.setUpperCase = function (a) {
15878 if (typeof a === 'boolean' ) { hexcase = a; }
15882 * @description Defines a base64 pad string
15883 * @param {string} Pad
15884 * @return {Object} this
15887 this.setPad = function (a) {
15888 if (typeof a !== 'undefined' ) { b64pad = a; }
15892 * @description Defines a base64 pad string
15894 * @return {Object} this
15897 this.setUTF8 = function (a) {
15898 if (typeof a === 'boolean') { utf8 = a; }
15902 /* private methods */
15905 * Calculate the rmd160 of a raw string
15908 s = (utf8) ? utf8Encode(s) : s;
15909 return binl2rstr(binl(rstr2binl(s), s.length * 8));
15913 * Calculate the HMAC-rmd160 of a key and some data (raw strings)
15915 function rstr_hmac(key, data) {
15916 key = (utf8) ? utf8Encode(key) : key;
15917 data = (utf8) ? utf8Encode(data) : data;
15919 bkey = rstr2binl(key),
15920 ipad = Array(16), opad = Array(16);
15922 if (bkey.length > 16) {
15923 bkey = binl(bkey, key.length * 8);
15926 for (i = 0; i < 16; i+=1) {
15927 ipad[i] = bkey[i] ^ 0x36363636;
15928 opad[i] = bkey[i] ^ 0x5C5C5C5C;
15930 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
15931 return binl2rstr(binl(opad.concat(hash), 512 + 160));
15935 * Convert an array of little-endian words to a string
15937 function binl2rstr(input) {
15938 var i, output = '', l = input.length * 32;
15939 for (i = 0; i < l; i += 8) {
15940 output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
15946 * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
15948 function binl(x, len) {
15955 A1, B1, C1, D1, E1,
15956 A2, B2, C2, D2, E2;
15958 /* append padding */
15959 x[len >> 5] |= 0x80 << (len % 32);
15960 x[(((len + 64) >>> 9) << 4) + 14] = len;
15963 for (i = 0; i < l; i+=16) {
15964 A1 = A2 = h0; B1 = B2 = h1; C1 = C2 = h2; D1 = D2 = h3; E1 = E2 = h4;
15965 for (j = 0; j <= 79; j+=1) {
15966 T = safe_add(A1, rmd160_f(j, B1, C1, D1));
15967 T = safe_add(T, x[i + rmd160_r1[j]]);
15968 T = safe_add(T, rmd160_K1(j));
15969 T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
15970 A1 = E1; E1 = D1; D1 = bit_rol(C1, 10); C1 = B1; B1 = T;
15971 T = safe_add(A2, rmd160_f(79-j, B2, C2, D2));
15972 T = safe_add(T, x[i + rmd160_r2[j]]);
15973 T = safe_add(T, rmd160_K2(j));
15974 T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
15975 A2 = E2; E2 = D2; D2 = bit_rol(C2, 10); C2 = B2; B2 = T;
15978 T = safe_add(h1, safe_add(C1, D2));
15979 h1 = safe_add(h2, safe_add(D1, E2));
15980 h2 = safe_add(h3, safe_add(E1, A2));
15981 h3 = safe_add(h4, safe_add(A1, B2));
15982 h4 = safe_add(h0, safe_add(B1, C2));
15985 return [h0, h1, h2, h3, h4];
15988 // specific algorithm methods
15989 function rmd160_f(j, x, y, z) {
15990 return ( 0 <= j && j <= 15) ? (x ^ y ^ z) :
15991 (16 <= j && j <= 31) ? (x & y) | (~x & z) :
15992 (32 <= j && j <= 47) ? (x | ~y) ^ z :
15993 (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
15994 (64 <= j && j <= 79) ? x ^ (y | ~z) :
15995 'rmd160_f: j out of range';
15998 function rmd160_K1(j) {
15999 return ( 0 <= j && j <= 15) ? 0x00000000 :
16000 (16 <= j && j <= 31) ? 0x5a827999 :
16001 (32 <= j && j <= 47) ? 0x6ed9eba1 :
16002 (48 <= j && j <= 63) ? 0x8f1bbcdc :
16003 (64 <= j && j <= 79) ? 0xa953fd4e :
16004 'rmd160_K1: j out of range';
16007 function rmd160_K2(j){
16008 return ( 0 <= j && j <= 15) ? 0x50a28be6 :
16009 (16 <= j && j <= 31) ? 0x5c4dd124 :
16010 (32 <= j && j <= 47) ? 0x6d703ef3 :
16011 (48 <= j && j <= 63) ? 0x7a6d76e9 :
16012 (64 <= j && j <= 79) ? 0x00000000 :
16013 'rmd160_K2: j out of range';
16019 (function( window, undefined ) {
16020 var freeExports = false;
16021 if (typeof exports === 'object' ) {
16022 freeExports = exports;
16023 if (exports && typeof global === 'object' && global && global === global.global ) { window = global; }
16026 if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
16027 // define as an anonymous module, so, through path mapping, it can be aliased
16028 define(function () { return Hashes; });
16030 else if ( freeExports ) {
16031 // in Node.js or RingoJS v0.8.0+
16032 if ( typeof module === 'object' && module && module.exports === freeExports ) {
16033 module.exports = Hashes;
16035 // in Narwhal or RingoJS v0.7.0-
16037 freeExports.Hashes = Hashes;
16041 // in a browser or Rhino
16042 window.Hashes = Hashes;
16048 },{}],2:[function(require,module,exports){
16051 var hashes = require('jshashes'),
16052 xtend = require('xtend'),
16053 sha1 = new hashes.SHA1();
16057 ohauth.qsString = function(obj) {
16058 return Object.keys(obj).sort().map(function(key) {
16059 return ohauth.percentEncode(key) + '=' +
16060 ohauth.percentEncode(obj[key]);
16064 ohauth.stringQs = function(str) {
16065 return str.split('&').reduce(function(obj, pair){
16066 var parts = pair.split('=');
16067 obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
16068 '' : decodeURIComponent(parts[1]);
16073 ohauth.rawxhr = function(method, url, data, headers, callback) {
16074 var xhr = new XMLHttpRequest(),
16075 twoHundred = /^20\d$/;
16076 xhr.onreadystatechange = function() {
16077 if (4 == xhr.readyState && 0 !== xhr.status) {
16078 if (twoHundred.test(xhr.status)) callback(null, xhr);
16079 else return callback(xhr, null);
16082 xhr.onerror = function(e) { return callback(e, null); };
16083 xhr.open(method, url, true);
16084 for (var h in headers) xhr.setRequestHeader(h, headers[h]);
16088 ohauth.xhr = function(method, url, auth, data, options, callback) {
16089 var headers = (options && options.header) || {
16090 'Content-Type': 'application/x-www-form-urlencoded'
16092 headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
16093 ohauth.rawxhr(method, url, data, headers, callback);
16096 ohauth.nonce = function() {
16097 for (var o = ''; o.length < 6;) {
16098 o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
16103 ohauth.authHeader = function(obj) {
16104 return Object.keys(obj).sort().map(function(key) {
16105 return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
16109 ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
16111 ohauth.percentEncode = function(s) {
16112 return encodeURIComponent(s)
16113 .replace(/\!/g, '%21').replace(/\'/g, '%27')
16114 .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
16117 ohauth.baseString = function(method, url, params) {
16118 if (params.oauth_signature) delete params.oauth_signature;
16121 ohauth.percentEncode(url),
16122 ohauth.percentEncode(ohauth.qsString(params))].join('&');
16125 ohauth.signature = function(oauth_secret, token_secret, baseString) {
16126 return sha1.b64_hmac(
16127 ohauth.percentEncode(oauth_secret) + '&' +
16128 ohauth.percentEncode(token_secret),
16133 * Takes an options object for configuration (consumer_key,
16134 * consumer_secret, version, signature_method, token) and returns a
16135 * function that generates the Authorization header for given data.
16137 * The returned function takes these parameters:
16138 * - method: GET/POST/...
16139 * - uri: full URI with protocol, port, path and query string
16140 * - extra_params: any extra parameters (that are passed in the POST data),
16141 * can be an object or a from-urlencoded string.
16143 * Returned function returns full OAuth header with "OAuth" string in it.
16146 ohauth.headerGenerator = function(options) {
16147 options = options || {};
16148 var consumer_key = options.consumer_key || '',
16149 consumer_secret = options.consumer_secret || '',
16150 signature_method = options.signature_method || 'HMAC-SHA1',
16151 version = options.version || '1.0',
16152 token = options.token || '';
16154 return function(method, uri, extra_params) {
16155 method = method.toUpperCase();
16156 if (typeof extra_params === 'string' && extra_params.length > 0) {
16157 extra_params = ohauth.stringQs(extra_params);
16160 var uri_parts = uri.split('?', 2),
16161 base_uri = uri_parts[0];
16163 var query_params = uri_parts.length === 2 ?
16164 ohauth.stringQs(uri_parts[1]) : {};
16166 var oauth_params = {
16167 oauth_consumer_key: consumer_key,
16168 oauth_signature_method: signature_method,
16169 oauth_version: version,
16170 oauth_timestamp: ohauth.timestamp(),
16171 oauth_nonce: ohauth.nonce()
16174 if (token) oauth_params.oauth_token = token;
16176 var all_params = xtend({}, oauth_params, query_params, extra_params),
16177 base_str = ohauth.baseString(method, base_uri, all_params);
16179 oauth_params.oauth_signature = ohauth.signature(consumer_secret, token, base_str);
16181 return 'OAuth ' + ohauth.authHeader(oauth_params);
16185 module.exports = ohauth;
16187 },{"jshashes":7,"xtend":4}],6:[function(require,module,exports){
16188 module.exports = Object.keys || require('./shim');
16191 },{"./shim":8}],8:[function(require,module,exports){
16195 // modified from https://github.com/kriskowal/es5-shim
16196 var has = Object.prototype.hasOwnProperty,
16197 is = require('is'),
16198 forEach = require('foreach'),
16199 hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'),
16206 "propertyIsEnumerable",
16211 keysShim = function keys(object) {
16212 if (!is.object(object) && !is.array(object)) {
16213 throw new TypeError("Object.keys called on a non-object");
16216 var name, theKeys = [];
16217 for (name in object) {
16218 if (has.call(object, name)) {
16219 theKeys.push(name);
16223 if (hasDontEnumBug) {
16224 forEach(dontEnums, function (dontEnum) {
16225 if (has.call(object, dontEnum)) {
16226 theKeys.push(dontEnum);
16233 module.exports = keysShim;
16237 },{"is":9,"foreach":10}],9:[function(require,module,exports){
16241 * the definitive JavaScript type testing library
16243 * @copyright 2013 Enrico Marino
16247 var objProto = Object.prototype;
16248 var owns = objProto.hasOwnProperty;
16249 var toString = objProto.toString;
16250 var isActualNaN = function (value) {
16251 return value !== value;
16253 var NON_HOST_TYPES = {
16264 var is = module.exports = {};
16272 * Test if `value` is a type of `type`.
16274 * @param {Mixed} value value to test
16275 * @param {String} type type
16276 * @return {Boolean} true if `value` is a type of `type`, false otherwise
16281 is.type = function (value, type) {
16282 return typeof value === type;
16287 * Test if `value` is defined.
16289 * @param {Mixed} value value to test
16290 * @return {Boolean} true if 'value' is defined, false otherwise
16294 is.defined = function (value) {
16295 return value !== undefined;
16300 * Test if `value` is empty.
16302 * @param {Mixed} value value to test
16303 * @return {Boolean} true if `value` is empty, false otherwise
16307 is.empty = function (value) {
16308 var type = toString.call(value);
16311 if ('[object Array]' === type || '[object Arguments]' === type) {
16312 return value.length === 0;
16315 if ('[object Object]' === type) {
16316 for (key in value) if (owns.call(value, key)) return false;
16320 if ('[object String]' === type) {
16321 return '' === value;
16329 * Test if `value` is equal to `other`.
16331 * @param {Mixed} value value to test
16332 * @param {Mixed} other value to compare with
16333 * @return {Boolean} true if `value` is equal to `other`, false otherwise
16336 is.equal = function (value, other) {
16337 var type = toString.call(value)
16340 if (type !== toString.call(other)) {
16344 if ('[object Object]' === type) {
16345 for (key in value) {
16346 if (!is.equal(value[key], other[key])) {
16353 if ('[object Array]' === type) {
16354 key = value.length;
16355 if (key !== other.length) {
16359 if (!is.equal(value[key], other[key])) {
16366 if ('[object Function]' === type) {
16367 return value.prototype === other.prototype;
16370 if ('[object Date]' === type) {
16371 return value.getTime() === other.getTime();
16374 return value === other;
16379 * Test if `value` is hosted by `host`.
16381 * @param {Mixed} value to test
16382 * @param {Mixed} host host to test with
16383 * @return {Boolean} true if `value` is hosted by `host`, false otherwise
16387 is.hosted = function (value, host) {
16388 var type = typeof host[value];
16389 return type === 'object' ? !!host[value] : !NON_HOST_TYPES[type];
16394 * Test if `value` is an instance of `constructor`.
16396 * @param {Mixed} value value to test
16397 * @return {Boolean} true if `value` is an instance of `constructor`
16401 is.instance = is['instanceof'] = function (value, constructor) {
16402 return value instanceof constructor;
16407 * Test if `value` is null.
16409 * @param {Mixed} value value to test
16410 * @return {Boolean} true if `value` is null, false otherwise
16414 is['null'] = function (value) {
16415 return value === null;
16420 * Test if `value` is undefined.
16422 * @param {Mixed} value value to test
16423 * @return {Boolean} true if `value` is undefined, false otherwise
16427 is.undefined = function (value) {
16428 return value === undefined;
16437 * Test if `value` is an arguments object.
16439 * @param {Mixed} value value to test
16440 * @return {Boolean} true if `value` is an arguments object, false otherwise
16444 is.arguments = function (value) {
16445 var isStandardArguments = '[object Arguments]' === toString.call(value);
16446 var isOldArguments = !is.array(value) && is.arraylike(value) && is.object(value) && is.fn(value.callee);
16447 return isStandardArguments || isOldArguments;
16456 * Test if 'value' is an array.
16458 * @param {Mixed} value value to test
16459 * @return {Boolean} true if `value` is an array, false otherwise
16463 is.array = function (value) {
16464 return '[object Array]' === toString.call(value);
16468 * is.arguments.empty
16469 * Test if `value` is an empty arguments object.
16471 * @param {Mixed} value value to test
16472 * @return {Boolean} true if `value` is an empty arguments object, false otherwise
16475 is.arguments.empty = function (value) {
16476 return is.arguments(value) && value.length === 0;
16481 * Test if `value` is an empty array.
16483 * @param {Mixed} value value to test
16484 * @return {Boolean} true if `value` is an empty array, false otherwise
16487 is.array.empty = function (value) {
16488 return is.array(value) && value.length === 0;
16493 * Test if `value` is an arraylike object.
16495 * @param {Mixed} value value to test
16496 * @return {Boolean} true if `value` is an arguments object, false otherwise
16500 is.arraylike = function (value) {
16501 return !!value && !is.boolean(value)
16502 && owns.call(value, 'length')
16503 && isFinite(value.length)
16504 && is.number(value.length)
16505 && value.length >= 0;
16514 * Test if `value` is a boolean.
16516 * @param {Mixed} value value to test
16517 * @return {Boolean} true if `value` is a boolean, false otherwise
16521 is.boolean = function (value) {
16522 return '[object Boolean]' === toString.call(value);
16527 * Test if `value` is false.
16529 * @param {Mixed} value value to test
16530 * @return {Boolean} true if `value` is false, false otherwise
16534 is['false'] = function (value) {
16535 return is.boolean(value) && (value === false || value.valueOf() === false);
16540 * Test if `value` is true.
16542 * @param {Mixed} value value to test
16543 * @return {Boolean} true if `value` is true, false otherwise
16547 is['true'] = function (value) {
16548 return is.boolean(value) && (value === true || value.valueOf() === true);
16557 * Test if `value` is a date.
16559 * @param {Mixed} value value to test
16560 * @return {Boolean} true if `value` is a date, false otherwise
16564 is.date = function (value) {
16565 return '[object Date]' === toString.call(value);
16574 * Test if `value` is an html element.
16576 * @param {Mixed} value value to test
16577 * @return {Boolean} true if `value` is an HTML Element, false otherwise
16581 is.element = function (value) {
16582 return value !== undefined
16583 && typeof HTMLElement !== 'undefined'
16584 && value instanceof HTMLElement
16585 && value.nodeType === 1;
16594 * Test if `value` is an error object.
16596 * @param {Mixed} value value to test
16597 * @return {Boolean} true if `value` is an error object, false otherwise
16601 is.error = function (value) {
16602 return '[object Error]' === toString.call(value);
16610 * is.fn / is.function (deprecated)
16611 * Test if `value` is a function.
16613 * @param {Mixed} value value to test
16614 * @return {Boolean} true if `value` is a function, false otherwise
16618 is.fn = is['function'] = function (value) {
16619 var isAlert = typeof window !== 'undefined' && value === window.alert;
16620 return isAlert || '[object Function]' === toString.call(value);
16629 * Test if `value` is a number.
16631 * @param {Mixed} value value to test
16632 * @return {Boolean} true if `value` is a number, false otherwise
16636 is.number = function (value) {
16637 return '[object Number]' === toString.call(value);
16642 * Test if `value` is positive or negative infinity.
16644 * @param {Mixed} value value to test
16645 * @return {Boolean} true if `value` is positive or negative Infinity, false otherwise
16648 is.infinite = function (value) {
16649 return value === Infinity || value === -Infinity;
16654 * Test if `value` is a decimal number.
16656 * @param {Mixed} value value to test
16657 * @return {Boolean} true if `value` is a decimal number, false otherwise
16661 is.decimal = function (value) {
16662 return is.number(value) && !isActualNaN(value) && value % 1 !== 0;
16667 * Test if `value` is divisible by `n`.
16669 * @param {Number} value value to test
16670 * @param {Number} n dividend
16671 * @return {Boolean} true if `value` is divisible by `n`, false otherwise
16675 is.divisibleBy = function (value, n) {
16676 var isDividendInfinite = is.infinite(value);
16677 var isDivisorInfinite = is.infinite(n);
16678 var isNonZeroNumber = is.number(value) && !isActualNaN(value) && is.number(n) && !isActualNaN(n) && n !== 0;
16679 return isDividendInfinite || isDivisorInfinite || (isNonZeroNumber && value % n === 0);
16684 * Test if `value` is an integer.
16686 * @param value to test
16687 * @return {Boolean} true if `value` is an integer, false otherwise
16691 is.int = function (value) {
16692 return is.number(value) && !isActualNaN(value) && value % 1 === 0;
16697 * Test if `value` is greater than 'others' values.
16699 * @param {Number} value value to test
16700 * @param {Array} others values to compare with
16701 * @return {Boolean} true if `value` is greater than `others` values
16705 is.maximum = function (value, others) {
16706 if (isActualNaN(value)) {
16707 throw new TypeError('NaN is not a valid value');
16708 } else if (!is.arraylike(others)) {
16709 throw new TypeError('second argument must be array-like');
16711 var len = others.length;
16713 while (--len >= 0) {
16714 if (value < others[len]) {
16724 * Test if `value` is less than `others` values.
16726 * @param {Number} value value to test
16727 * @param {Array} others values to compare with
16728 * @return {Boolean} true if `value` is less than `others` values
16732 is.minimum = function (value, others) {
16733 if (isActualNaN(value)) {
16734 throw new TypeError('NaN is not a valid value');
16735 } else if (!is.arraylike(others)) {
16736 throw new TypeError('second argument must be array-like');
16738 var len = others.length;
16740 while (--len >= 0) {
16741 if (value > others[len]) {
16751 * Test if `value` is not a number.
16753 * @param {Mixed} value value to test
16754 * @return {Boolean} true if `value` is not a number, false otherwise
16758 is.nan = function (value) {
16759 return !is.number(value) || value !== value;
16764 * Test if `value` is an even number.
16766 * @param {Number} value value to test
16767 * @return {Boolean} true if `value` is an even number, false otherwise
16771 is.even = function (value) {
16772 return is.infinite(value) || (is.number(value) && value === value && value % 2 === 0);
16777 * Test if `value` is an odd number.
16779 * @param {Number} value value to test
16780 * @return {Boolean} true if `value` is an odd number, false otherwise
16784 is.odd = function (value) {
16785 return is.infinite(value) || (is.number(value) && value === value && value % 2 !== 0);
16790 * Test if `value` is greater than or equal to `other`.
16792 * @param {Number} value value to test
16793 * @param {Number} other value to compare with
16794 * @return {Boolean}
16798 is.ge = function (value, other) {
16799 if (isActualNaN(value) || isActualNaN(other)) {
16800 throw new TypeError('NaN is not a valid value');
16802 return !is.infinite(value) && !is.infinite(other) && value >= other;
16807 * Test if `value` is greater than `other`.
16809 * @param {Number} value value to test
16810 * @param {Number} other value to compare with
16811 * @return {Boolean}
16815 is.gt = function (value, other) {
16816 if (isActualNaN(value) || isActualNaN(other)) {
16817 throw new TypeError('NaN is not a valid value');
16819 return !is.infinite(value) && !is.infinite(other) && value > other;
16824 * Test if `value` is less than or equal to `other`.
16826 * @param {Number} value value to test
16827 * @param {Number} other value to compare with
16828 * @return {Boolean} if 'value' is less than or equal to 'other'
16832 is.le = function (value, other) {
16833 if (isActualNaN(value) || isActualNaN(other)) {
16834 throw new TypeError('NaN is not a valid value');
16836 return !is.infinite(value) && !is.infinite(other) && value <= other;
16841 * Test if `value` is less than `other`.
16843 * @param {Number} value value to test
16844 * @param {Number} other value to compare with
16845 * @return {Boolean} if `value` is less than `other`
16849 is.lt = function (value, other) {
16850 if (isActualNaN(value) || isActualNaN(other)) {
16851 throw new TypeError('NaN is not a valid value');
16853 return !is.infinite(value) && !is.infinite(other) && value < other;
16858 * Test if `value` is within `start` and `finish`.
16860 * @param {Number} value value to test
16861 * @param {Number} start lower bound
16862 * @param {Number} finish upper bound
16863 * @return {Boolean} true if 'value' is is within 'start' and 'finish'
16866 is.within = function (value, start, finish) {
16867 if (isActualNaN(value) || isActualNaN(start) || isActualNaN(finish)) {
16868 throw new TypeError('NaN is not a valid value');
16869 } else if (!is.number(value) || !is.number(start) || !is.number(finish)) {
16870 throw new TypeError('all arguments must be numbers');
16872 var isAnyInfinite = is.infinite(value) || is.infinite(start) || is.infinite(finish);
16873 return isAnyInfinite || (value >= start && value <= finish);
16882 * Test if `value` is an object.
16884 * @param {Mixed} value value to test
16885 * @return {Boolean} true if `value` is an object, false otherwise
16889 is.object = function (value) {
16890 return value && '[object Object]' === toString.call(value);
16895 * Test if `value` is a hash - a plain object literal.
16897 * @param {Mixed} value value to test
16898 * @return {Boolean} true if `value` is a hash, false otherwise
16902 is.hash = function (value) {
16903 return is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval;
16912 * Test if `value` is a regular expression.
16914 * @param {Mixed} value value to test
16915 * @return {Boolean} true if `value` is a regexp, false otherwise
16919 is.regexp = function (value) {
16920 return '[object RegExp]' === toString.call(value);
16929 * Test if `value` is a string.
16931 * @param {Mixed} value value to test
16932 * @return {Boolean} true if 'value' is a string, false otherwise
16936 is.string = function (value) {
16937 return '[object String]' === toString.call(value);
16941 },{}],10:[function(require,module,exports){
16943 var hasOwn = Object.prototype.hasOwnProperty;
16944 var toString = Object.prototype.toString;
16946 module.exports = function forEach (obj, fn, ctx) {
16947 if (toString.call(fn) !== '[object Function]') {
16948 throw new TypeError('iterator must be a function');
16950 var l = obj.length;
16952 for (var i = 0; i < l; i++) {
16953 fn.call(ctx, obj[i], i, obj);
16956 for (var k in obj) {
16957 if (hasOwn.call(obj, k)) {
16958 fn.call(ctx, obj[k], k, obj);
16969 (c) 2015, Vladimir Agafonkin
16970 RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
16971 https://github.com/mourner/rbush
16977 function rbush(maxEntries, format) {
16979 // jshint newcap: false, validthis: true
16980 if (!(this instanceof rbush)) return new rbush(maxEntries, format);
16982 // max entries in a node is 9 by default; min node fill is 40% for best performance
16983 this._maxEntries = Math.max(4, maxEntries || 9);
16984 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
16987 this._initFormat(format);
16993 rbush.prototype = {
16996 return this._all(this.data, []);
16999 search: function (bbox) {
17001 var node = this.data,
17003 toBBox = this.toBBox;
17005 if (!intersects(bbox, node.bbox)) return result;
17007 var nodesToSearch = [],
17008 i, len, child, childBBox;
17011 for (i = 0, len = node.children.length; i < len; i++) {
17013 child = node.children[i];
17014 childBBox = node.leaf ? toBBox(child) : child.bbox;
17016 if (intersects(bbox, childBBox)) {
17017 if (node.leaf) result.push(child);
17018 else if (contains(bbox, childBBox)) this._all(child, result);
17019 else nodesToSearch.push(child);
17022 node = nodesToSearch.pop();
17028 collides: function (bbox) {
17030 var node = this.data,
17031 toBBox = this.toBBox;
17033 if (!intersects(bbox, node.bbox)) return false;
17035 var nodesToSearch = [],
17036 i, len, child, childBBox;
17039 for (i = 0, len = node.children.length; i < len; i++) {
17041 child = node.children[i];
17042 childBBox = node.leaf ? toBBox(child) : child.bbox;
17044 if (intersects(bbox, childBBox)) {
17045 if (node.leaf || contains(bbox, childBBox)) return true;
17046 nodesToSearch.push(child);
17049 node = nodesToSearch.pop();
17055 load: function (data) {
17056 if (!(data && data.length)) return this;
17058 if (data.length < this._minEntries) {
17059 for (var i = 0, len = data.length; i < len; i++) {
17060 this.insert(data[i]);
17065 // recursively build the tree with the given data from stratch using OMT algorithm
17066 var node = this._build(data.slice(), 0, data.length - 1, 0);
17068 if (!this.data.children.length) {
17069 // save as is if tree is empty
17072 } else if (this.data.height === node.height) {
17073 // split root if trees have the same height
17074 this._splitRoot(this.data, node);
17077 if (this.data.height < node.height) {
17078 // swap trees if inserted one is bigger
17079 var tmpNode = this.data;
17084 // insert the small tree into the large tree at appropriate level
17085 this._insert(node, this.data.height - node.height - 1, true);
17091 insert: function (item) {
17092 if (item) this._insert(item, this.data.height - 1);
17096 clear: function () {
17106 remove: function (item) {
17107 if (!item) return this;
17109 var node = this.data,
17110 bbox = this.toBBox(item),
17113 i, parent, index, goingUp;
17115 // depth-first iterative tree traversal
17116 while (node || path.length) {
17118 if (!node) { // go up
17120 parent = path[path.length - 1];
17125 if (node.leaf) { // check current node
17126 index = node.children.indexOf(item);
17128 if (index !== -1) {
17129 // item found, remove the item and condense tree upwards
17130 node.children.splice(index, 1);
17132 this._condense(path);
17137 if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
17142 node = node.children[0];
17144 } else if (parent) { // go right
17146 node = parent.children[i];
17149 } else node = null; // nothing found
17155 toBBox: function (item) { return item; },
17157 compareMinX: function (a, b) { return a[0] - b[0]; },
17158 compareMinY: function (a, b) { return a[1] - b[1]; },
17160 toJSON: function () { return this.data; },
17162 fromJSON: function (data) {
17167 _all: function (node, result) {
17168 var nodesToSearch = [];
17170 if (node.leaf) result.push.apply(result, node.children);
17171 else nodesToSearch.push.apply(nodesToSearch, node.children);
17173 node = nodesToSearch.pop();
17178 _build: function (items, left, right, height) {
17180 var N = right - left + 1,
17181 M = this._maxEntries,
17185 // reached leaf level; return leaf
17187 children: items.slice(left, right + 1),
17192 calcBBox(node, this.toBBox);
17197 // target height of the bulk-loaded tree
17198 height = Math.ceil(Math.log(N) / Math.log(M));
17200 // target number of root entries to maximize storage utilization
17201 M = Math.ceil(N / Math.pow(M, height - 1));
17211 // split the items into M mostly square tiles
17213 var N2 = Math.ceil(N / M),
17214 N1 = N2 * Math.ceil(Math.sqrt(M)),
17215 i, j, right2, right3;
17217 multiSelect(items, left, right, N1, this.compareMinX);
17219 for (i = left; i <= right; i += N1) {
17221 right2 = Math.min(i + N1 - 1, right);
17223 multiSelect(items, i, right2, N2, this.compareMinY);
17225 for (j = i; j <= right2; j += N2) {
17227 right3 = Math.min(j + N2 - 1, right2);
17229 // pack each entry recursively
17230 node.children.push(this._build(items, j, right3, height - 1));
17234 calcBBox(node, this.toBBox);
17239 _chooseSubtree: function (bbox, node, level, path) {
17241 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
17246 if (node.leaf || path.length - 1 === level) break;
17248 minArea = minEnlargement = Infinity;
17250 for (i = 0, len = node.children.length; i < len; i++) {
17251 child = node.children[i];
17252 area = bboxArea(child.bbox);
17253 enlargement = enlargedArea(bbox, child.bbox) - area;
17255 // choose entry with the least area enlargement
17256 if (enlargement < minEnlargement) {
17257 minEnlargement = enlargement;
17258 minArea = area < minArea ? area : minArea;
17259 targetNode = child;
17261 } else if (enlargement === minEnlargement) {
17262 // otherwise choose one with the smallest area
17263 if (area < minArea) {
17265 targetNode = child;
17276 _insert: function (item, level, isNode) {
17278 var toBBox = this.toBBox,
17279 bbox = isNode ? item.bbox : toBBox(item),
17282 // find the best node for accommodating the item, saving all nodes along the path too
17283 var node = this._chooseSubtree(bbox, this.data, level, insertPath);
17285 // put the item into the node
17286 node.children.push(item);
17287 extend(node.bbox, bbox);
17289 // split on node overflow; propagate upwards if necessary
17290 while (level >= 0) {
17291 if (insertPath[level].children.length > this._maxEntries) {
17292 this._split(insertPath, level);
17297 // adjust bboxes along the insertion path
17298 this._adjustParentBBoxes(bbox, insertPath, level);
17301 // split overflowed node into two
17302 _split: function (insertPath, level) {
17304 var node = insertPath[level],
17305 M = node.children.length,
17306 m = this._minEntries;
17308 this._chooseSplitAxis(node, m, M);
17310 var splitIndex = this._chooseSplitIndex(node, m, M);
17313 children: node.children.splice(splitIndex, node.children.length - splitIndex),
17314 height: node.height,
17319 if (node.leaf) newNode.leaf = true;
17321 calcBBox(node, this.toBBox);
17322 calcBBox(newNode, this.toBBox);
17324 if (level) insertPath[level - 1].children.push(newNode);
17325 else this._splitRoot(node, newNode);
17328 _splitRoot: function (node, newNode) {
17331 children: [node, newNode],
17332 height: node.height + 1,
17336 calcBBox(this.data, this.toBBox);
17339 _chooseSplitIndex: function (node, m, M) {
17341 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
17343 minOverlap = minArea = Infinity;
17345 for (i = m; i <= M - m; i++) {
17346 bbox1 = distBBox(node, 0, i, this.toBBox);
17347 bbox2 = distBBox(node, i, M, this.toBBox);
17349 overlap = intersectionArea(bbox1, bbox2);
17350 area = bboxArea(bbox1) + bboxArea(bbox2);
17352 // choose distribution with minimum overlap
17353 if (overlap < minOverlap) {
17354 minOverlap = overlap;
17357 minArea = area < minArea ? area : minArea;
17359 } else if (overlap === minOverlap) {
17360 // otherwise choose distribution with minimum area
17361 if (area < minArea) {
17371 // sorts node children by the best axis for split
17372 _chooseSplitAxis: function (node, m, M) {
17374 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
17375 compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
17376 xMargin = this._allDistMargin(node, m, M, compareMinX),
17377 yMargin = this._allDistMargin(node, m, M, compareMinY);
17379 // if total distributions margin value is minimal for x, sort by minX,
17380 // otherwise it's already sorted by minY
17381 if (xMargin < yMargin) node.children.sort(compareMinX);
17384 // total margin of all possible split distributions where each node is at least m full
17385 _allDistMargin: function (node, m, M, compare) {
17387 node.children.sort(compare);
17389 var toBBox = this.toBBox,
17390 leftBBox = distBBox(node, 0, m, toBBox),
17391 rightBBox = distBBox(node, M - m, M, toBBox),
17392 margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
17395 for (i = m; i < M - m; i++) {
17396 child = node.children[i];
17397 extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
17398 margin += bboxMargin(leftBBox);
17401 for (i = M - m - 1; i >= m; i--) {
17402 child = node.children[i];
17403 extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
17404 margin += bboxMargin(rightBBox);
17410 _adjustParentBBoxes: function (bbox, path, level) {
17411 // adjust bboxes along the given tree path
17412 for (var i = level; i >= 0; i--) {
17413 extend(path[i].bbox, bbox);
17417 _condense: function (path) {
17418 // go through the path, removing empty nodes and updating bboxes
17419 for (var i = path.length - 1, siblings; i >= 0; i--) {
17420 if (path[i].children.length === 0) {
17422 siblings = path[i - 1].children;
17423 siblings.splice(siblings.indexOf(path[i]), 1);
17425 } else this.clear();
17427 } else calcBBox(path[i], this.toBBox);
17431 _initFormat: function (format) {
17432 // data format (minX, minY, maxX, maxY accessors)
17434 // uses eval-type function compilation instead of just accepting a toBBox function
17435 // because the algorithms are very sensitive to sorting functions performance,
17436 // so they should be dead simple and without inner calls
17438 // jshint evil: true
17440 var compareArr = ['return a', ' - b', ';'];
17442 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
17443 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
17445 this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
17450 // calculate node's bbox from bboxes of its children
17451 function calcBBox(node, toBBox) {
17452 node.bbox = distBBox(node, 0, node.children.length, toBBox);
17455 // min bounding rectangle of node children from k to p-1
17456 function distBBox(node, k, p, toBBox) {
17457 var bbox = empty();
17459 for (var i = k, child; i < p; i++) {
17460 child = node.children[i];
17461 extend(bbox, node.leaf ? toBBox(child) : child.bbox);
17467 function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
17469 function extend(a, b) {
17470 a[0] = Math.min(a[0], b[0]);
17471 a[1] = Math.min(a[1], b[1]);
17472 a[2] = Math.max(a[2], b[2]);
17473 a[3] = Math.max(a[3], b[3]);
17477 function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
17478 function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
17480 function bboxArea(a) { return (a[2] - a[0]) * (a[3] - a[1]); }
17481 function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
17483 function enlargedArea(a, b) {
17484 return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
17485 (Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
17488 function intersectionArea(a, b) {
17489 var minX = Math.max(a[0], b[0]),
17490 minY = Math.max(a[1], b[1]),
17491 maxX = Math.min(a[2], b[2]),
17492 maxY = Math.min(a[3], b[3]);
17494 return Math.max(0, maxX - minX) *
17495 Math.max(0, maxY - minY);
17498 function contains(a, b) {
17499 return a[0] <= b[0] &&
17505 function intersects(a, b) {
17506 return b[0] <= a[2] &&
17512 // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
17513 // combines selection algorithm with binary divide & conquer approach
17515 function multiSelect(arr, left, right, n, compare) {
17516 var stack = [left, right],
17519 while (stack.length) {
17520 right = stack.pop();
17521 left = stack.pop();
17523 if (right - left <= n) continue;
17525 mid = left + Math.ceil((right - left) / n / 2) * n;
17526 select(arr, left, right, mid, compare);
17528 stack.push(left, mid, mid, right);
17532 // Floyd-Rivest selection algorithm:
17533 // sort an array between left and right (inclusive) so that the smallest k elements come first (unordered)
17534 function select(arr, left, right, k, compare) {
17535 var n, i, z, s, sd, newLeft, newRight, t, j;
17537 while (right > left) {
17538 if (right - left > 600) {
17539 n = right - left + 1;
17542 s = 0.5 * Math.exp(2 * z / 3);
17543 sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1);
17544 newLeft = Math.max(left, Math.floor(k - i * s / n + sd));
17545 newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd));
17546 select(arr, newLeft, newRight, k, compare);
17553 swap(arr, left, k);
17554 if (compare(arr[right], t) > 0) swap(arr, left, right);
17560 while (compare(arr[i], t) < 0) i++;
17561 while (compare(arr[j], t) > 0) j--;
17564 if (compare(arr[left], t) === 0) swap(arr, left, j);
17567 swap(arr, j, right);
17570 if (j <= k) left = j + 1;
17571 if (k <= j) right = j - 1;
17575 function swap(arr, i, j) {
17582 // export as AMD/CommonJS module or global variable
17583 if (typeof define === 'function' && define.amd) define('rbush', function () { return rbush; });
17584 else if (typeof module !== 'undefined') module.exports = rbush;
17585 else if (typeof self !== 'undefined') self.rbush = rbush;
17586 else window.rbush = rbush;
17589 (function(e){if("function"==typeof bootstrap)bootstrap("sexagesimal",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeSexagesimal=e}else"undefined"!=typeof window?window.sexagesimal=e():global.sexagesimal=e()})(function(){var define,ses,bootstrap,module,exports;
17590 return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
17591 module.exports = element;
17592 module.exports.pair = pair;
17593 module.exports.format = format;
17594 module.exports.formatPair = formatPair;
17596 function element(x, dims) {
17597 return search(x, dims).val;
17600 function formatPair(x) {
17601 return format(x.lat, 'lat') + ' ' + format(x.lon, 'lon');
17604 // Is 0 North or South?
17605 function format(x, dim) {
17610 dir = dirs[x >= 0 ? 0 : 1],
17612 whole = Math.floor(abs),
17613 fraction = abs - whole,
17614 fractionMinutes = fraction * 60,
17615 minutes = Math.floor(fractionMinutes),
17616 seconds = Math.floor((fractionMinutes - minutes) * 60);
17618 return whole + '° ' +
17619 (minutes ? minutes + "' " : '') +
17620 (seconds ? seconds + '" ' : '') + dir;
17623 function search(x, dims, r) {
17624 if (!dims) dims = 'NSEW';
17625 if (typeof x !== 'string') return { val: null, regex: r };
17626 r = r || /[\s\,]*([\-|\—|\―]?[0-9.]+)°? *(?:([0-9.]+)['’′‘] *)?(?:([0-9.]+)(?:''|"|”|″) *)?([NSEW])?/gi;
17628 if (!m) return { val: null, regex: r };
17629 else if (m[4] && dims.indexOf(m[4]) === -1) return { val: null, regex: r };
17631 val: (((m[1]) ? parseFloat(m[1]) : 0) +
17632 ((m[2] ? parseFloat(m[2]) / 60 : 0)) +
17633 ((m[3] ? parseFloat(m[3]) / 3600 : 0))) *
17634 ((m[4] && m[4] === 'S' || m[4] === 'W') ? -1 : 1),
17641 function pair(x, dims) {
17643 var one = search(x, dims);
17644 if (one.val === null) return null;
17645 var two = search(x, dims, one.regex);
17646 if (two.val === null) return null;
17647 // null if one/two are not contiguous.
17648 if (one.raw + two.raw !== x) return null;
17649 if (one.dim) return swapdim(one.val, two.val, one.dim);
17650 else return [one.val, two.val];
17653 function swapdim(a, b, dim) {
17654 if (dim == 'N' || dim == 'S') return [a, b];
17655 if (dim == 'W' || dim == 'E') return [b, a];
17661 ;toGeoJSON = (function() {
17664 var removeSpace = (/\s*/g),
17665 trimSpace = (/^\s*|\s*$/g),
17666 splitSpace = (/\s+/);
17667 // generate a short, numeric hash of a string
17668 function okhash(x) {
17669 if (!x || !x.length) return 0;
17670 for (var i = 0, h = 0; i < x.length; i++) {
17671 h = ((h << 5) - h) + x.charCodeAt(i) | 0;
17674 // all Y children of X
17675 function get(x, y) { return x.getElementsByTagName(y); }
17676 function attr(x, y) { return x.getAttribute(y); }
17677 function attrf(x, y) { return parseFloat(attr(x, y)); }
17678 // one Y child of X, if any, otherwise null
17679 function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
17680 // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
17681 function norm(el) { if (el.normalize) { el.normalize(); } return el; }
17682 // cast array x into numbers
17683 function numarray(x) {
17684 for (var j = 0, o = []; j < x.length; j++) o[j] = parseFloat(x[j]);
17687 function clean(x) {
17689 for (var i in x) if (x[i]) o[i] = x[i];
17692 // get the content of a text node, if any
17693 function nodeVal(x) { if (x) {norm(x);} return x && x.firstChild && x.firstChild.nodeValue; }
17694 // get one coordinate from a coordinate array, if any
17695 function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
17696 // get all coordinates from a coordinate array as [[],[]]
17697 function coord(v) {
17698 var coords = v.replace(trimSpace, '').split(splitSpace),
17700 for (var i = 0; i < coords.length; i++) {
17701 o.push(coord1(coords[i]));
17705 function coordPair(x) { return [attrf(x, 'lon'), attrf(x, 'lat')]; }
17707 // create a new feature collection parent object
17710 type: 'FeatureCollection',
17715 var styleSupport = false;
17716 if (typeof XMLSerializer !== 'undefined') {
17717 var serializer = new XMLSerializer();
17718 styleSupport = true;
17720 function xml2str(str) { return serializer.serializeToString(str); }
17723 kml: function(doc, o) {
17727 // styleindex keeps track of hashed styles in order to match features
17729 // atomic geospatial types supported by KML - MultiGeometry is
17730 // handled separately
17731 geotypes = ['Polygon', 'LineString', 'Point', 'Track'],
17732 // all root placemarks in the file
17733 placemarks = get(doc, 'Placemark'),
17734 styles = get(doc, 'Style');
17736 if (styleSupport) for (var k = 0; k < styles.length; k++) {
17737 styleIndex['#' + attr(styles[k], 'id')] = okhash(xml2str(styles[k])).toString(16);
17739 for (var j = 0; j < placemarks.length; j++) {
17740 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
17742 function gxCoord(v) { return numarray(v.split(' ')); }
17743 function gxCoords(root) {
17744 var elems = get(root, 'coord', 'gx'), coords = [];
17745 for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
17748 function getGeometry(root) {
17749 var geomNode, geomNodes, i, j, k, geoms = [];
17750 if (get1(root, 'MultiGeometry')) return getGeometry(get1(root, 'MultiGeometry'));
17751 if (get1(root, 'MultiTrack')) return getGeometry(get1(root, 'MultiTrack'));
17752 for (i = 0; i < geotypes.length; i++) {
17753 geomNodes = get(root, geotypes[i]);
17755 for (j = 0; j < geomNodes.length; j++) {
17756 geomNode = geomNodes[j];
17757 if (geotypes[i] == 'Point') {
17760 coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
17762 } else if (geotypes[i] == 'LineString') {
17764 type: 'LineString',
17765 coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
17767 } else if (geotypes[i] == 'Polygon') {
17768 var rings = get(geomNode, 'LinearRing'),
17770 for (k = 0; k < rings.length; k++) {
17771 coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
17775 coordinates: coords
17777 } else if (geotypes[i] == 'Track') {
17779 type: 'LineString',
17780 coordinates: gxCoords(geomNode)
17788 function getPlacemark(root) {
17789 var geoms = getGeometry(root), i, properties = {},
17790 name = nodeVal(get1(root, 'name')),
17791 styleUrl = nodeVal(get1(root, 'styleUrl')),
17792 description = nodeVal(get1(root, 'description')),
17793 extendedData = get1(root, 'ExtendedData');
17795 if (!geoms.length) return [];
17796 if (name) properties.name = name;
17797 if (styleUrl && styleIndex[styleUrl]) {
17798 properties.styleUrl = styleUrl;
17799 properties.styleHash = styleIndex[styleUrl];
17801 if (description) properties.description = description;
17802 if (extendedData) {
17803 var datas = get(extendedData, 'Data'),
17804 simpleDatas = get(extendedData, 'SimpleData');
17806 for (i = 0; i < datas.length; i++) {
17807 properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
17809 for (i = 0; i < simpleDatas.length; i++) {
17810 properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
17815 geometry: (geoms.length === 1) ? geoms[0] : {
17816 type: 'GeometryCollection',
17819 properties: properties
17824 gpx: function(doc, o) {
17826 tracks = get(doc, 'trk'),
17827 routes = get(doc, 'rte'),
17828 waypoints = get(doc, 'wpt'),
17829 // a feature collection
17831 for (i = 0; i < tracks.length; i++) {
17832 gj.features.push(getLinestring(tracks[i], 'trkpt'));
17834 for (i = 0; i < routes.length; i++) {
17835 gj.features.push(getLinestring(routes[i], 'rtept'));
17837 for (i = 0; i < waypoints.length; i++) {
17838 gj.features.push(getPoint(waypoints[i]));
17840 function getLinestring(node, pointname) {
17841 var j, pts = get(node, pointname), line = [];
17842 for (j = 0; j < pts.length; j++) {
17843 line.push(coordPair(pts[j]));
17847 properties: getProperties(node),
17849 type: 'LineString',
17854 function getPoint(node) {
17855 var prop = getProperties(node);
17856 prop.ele = nodeVal(get1(node, 'ele'));
17857 prop.sym = nodeVal(get1(node, 'sym'));
17863 coordinates: coordPair(node)
17867 function getProperties(node) {
17868 var meta = ['name', 'desc', 'author', 'copyright', 'link',
17869 'time', 'keywords'],
17872 for (k = 0; k < meta.length; k++) {
17873 prop[meta[k]] = nodeVal(get1(node, meta[k]));
17875 return clean(prop);
17883 if (typeof module !== 'undefined') module.exports = toGeoJSON;
17885 * marked - a markdown parser
17886 * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
17887 * https://github.com/chjj/marked
17893 * Block-Level Grammar
17898 code: /^( {4}[^\n]+\n*)+/,
17900 hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17901 heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17903 lheading: /^([^\n]+)\n *(=|-){3,} *\n*/,
17904 blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
17905 list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17906 html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
17907 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17909 paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17913 block.bullet = /(?:[*+-]|\d+\.)/;
17914 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17915 block.item = replace(block.item, 'gm')
17916 (/bull/g, block.bullet)
17919 block.list = replace(block.list)
17920 (/bull/g, block.bullet)
17921 ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)
17924 block._tag = '(?!(?:'
17925 + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17926 + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17927 + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b';
17929 block.html = replace(block.html)
17930 ('comment', /<!--[\s\S]*?-->/)
17931 ('closed', /<(tag)[\s\S]+?<\/\1>/)
17932 ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17933 (/tag/g, block._tag)
17936 block.paragraph = replace(block.paragraph)
17938 ('heading', block.heading)
17939 ('lheading', block.lheading)
17940 ('blockquote', block.blockquote)
17941 ('tag', '<' + block._tag)
17946 * Normal Block Grammar
17949 block.normal = merge({}, block);
17952 * GFM Block Grammar
17955 block.gfm = merge({}, block.normal, {
17956 fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
17960 block.gfm.paragraph = replace(block.paragraph)
17961 ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|')
17965 * GFM + Tables Block Grammar
17968 block.tables = merge({}, block.gfm, {
17969 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17970 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17977 function Lexer(options) {
17979 this.tokens.links = {};
17980 this.options = options || marked.defaults;
17981 this.rules = block.normal;
17983 if (this.options.gfm) {
17984 if (this.options.tables) {
17985 this.rules = block.tables;
17987 this.rules = block.gfm;
17993 * Expose Block Rules
17996 Lexer.rules = block;
17999 * Static Lex Method
18002 Lexer.lex = function(src, options) {
18003 var lexer = new Lexer(options);
18004 return lexer.lex(src);
18011 Lexer.prototype.lex = function(src) {
18013 .replace(/\r\n|\r/g, '\n')
18014 .replace(/\t/g, ' ')
18015 .replace(/\u00a0/g, ' ')
18016 .replace(/\u2424/g, '\n');
18018 return this.token(src, true);
18025 Lexer.prototype.token = function(src, top) {
18026 var src = src.replace(/^ +$/gm, '')
18039 if (cap = this.rules.newline.exec(src)) {
18040 src = src.substring(cap[0].length);
18041 if (cap[0].length > 1) {
18049 if (cap = this.rules.code.exec(src)) {
18050 src = src.substring(cap[0].length);
18051 cap = cap[0].replace(/^ {4}/gm, '');
18054 text: !this.options.pedantic
18055 ? cap.replace(/\n+$/, '')
18062 if (cap = this.rules.fences.exec(src)) {
18063 src = src.substring(cap[0].length);
18073 if (cap = this.rules.heading.exec(src)) {
18074 src = src.substring(cap[0].length);
18077 depth: cap[1].length,
18083 // table no leading pipe (gfm)
18084 if (top && (cap = this.rules.nptable.exec(src))) {
18085 src = src.substring(cap[0].length);
18089 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18090 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18091 cells: cap[3].replace(/\n$/, '').split('\n')
18094 for (i = 0; i < item.align.length; i++) {
18095 if (/^ *-+: *$/.test(item.align[i])) {
18096 item.align[i] = 'right';
18097 } else if (/^ *:-+: *$/.test(item.align[i])) {
18098 item.align[i] = 'center';
18099 } else if (/^ *:-+ *$/.test(item.align[i])) {
18100 item.align[i] = 'left';
18102 item.align[i] = null;
18106 for (i = 0; i < item.cells.length; i++) {
18107 item.cells[i] = item.cells[i].split(/ *\| */);
18110 this.tokens.push(item);
18116 if (cap = this.rules.lheading.exec(src)) {
18117 src = src.substring(cap[0].length);
18120 depth: cap[2] === '=' ? 1 : 2,
18127 if (cap = this.rules.hr.exec(src)) {
18128 src = src.substring(cap[0].length);
18136 if (cap = this.rules.blockquote.exec(src)) {
18137 src = src.substring(cap[0].length);
18140 type: 'blockquote_start'
18143 cap = cap[0].replace(/^ *> ?/gm, '');
18145 // Pass `top` to keep the current
18146 // "toplevel" state. This is exactly
18147 // how markdown.pl works.
18148 this.token(cap, top);
18151 type: 'blockquote_end'
18158 if (cap = this.rules.list.exec(src)) {
18159 src = src.substring(cap[0].length);
18163 type: 'list_start',
18164 ordered: bull.length > 1
18167 // Get each top-level item.
18168 cap = cap[0].match(this.rules.item);
18174 for (; i < l; i++) {
18177 // Remove the list item's bullet
18178 // so it is seen as the next token.
18179 space = item.length;
18180 item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18182 // Outdent whatever the
18183 // list item contains. Hacky.
18184 if (~item.indexOf('\n ')) {
18185 space -= item.length;
18186 item = !this.options.pedantic
18187 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18188 : item.replace(/^ {1,4}/gm, '');
18191 // Determine whether the next list item belongs here.
18192 // Backpedal if it does not belong in this list.
18193 if (this.options.smartLists && i !== l - 1) {
18194 b = block.bullet.exec(cap[i+1])[0];
18195 if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18196 src = cap.slice(i + 1).join('\n') + src;
18201 // Determine whether item is loose or not.
18202 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18203 // for discount behavior.
18204 loose = next || /\n\n(?!\s*$)/.test(item);
18206 next = item[item.length-1] === '\n';
18207 if (!loose) loose = next;
18212 ? 'loose_item_start'
18213 : 'list_item_start'
18217 this.token(item, false);
18220 type: 'list_item_end'
18232 if (cap = this.rules.html.exec(src)) {
18233 src = src.substring(cap[0].length);
18235 type: this.options.sanitize
18238 pre: cap[1] === 'pre' || cap[1] === 'script',
18245 if (top && (cap = this.rules.def.exec(src))) {
18246 src = src.substring(cap[0].length);
18247 this.tokens.links[cap[1].toLowerCase()] = {
18255 if (top && (cap = this.rules.table.exec(src))) {
18256 src = src.substring(cap[0].length);
18260 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18261 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18262 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18265 for (i = 0; i < item.align.length; i++) {
18266 if (/^ *-+: *$/.test(item.align[i])) {
18267 item.align[i] = 'right';
18268 } else if (/^ *:-+: *$/.test(item.align[i])) {
18269 item.align[i] = 'center';
18270 } else if (/^ *:-+ *$/.test(item.align[i])) {
18271 item.align[i] = 'left';
18273 item.align[i] = null;
18277 for (i = 0; i < item.cells.length; i++) {
18278 item.cells[i] = item.cells[i]
18279 .replace(/^ *\| *| *\| *$/g, '')
18283 this.tokens.push(item);
18288 // top-level paragraph
18289 if (top && (cap = this.rules.paragraph.exec(src))) {
18290 src = src.substring(cap[0].length);
18293 text: cap[1][cap[1].length-1] === '\n'
18294 ? cap[1].slice(0, -1)
18301 if (cap = this.rules.text.exec(src)) {
18302 // Top-level should never reach here.
18303 src = src.substring(cap[0].length);
18313 Error('Infinite loop on byte: ' + src.charCodeAt(0));
18317 return this.tokens;
18321 * Inline-Level Grammar
18325 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18326 autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18328 tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18329 link: /^!?\[(inside)\]\(href\)/,
18330 reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18331 nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18332 strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18333 em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18334 code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18335 br: /^ {2,}\n(?!\s*$)/,
18337 text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18340 inline._inside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
18341 inline._href = /\s*<?([^\s]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18343 inline.link = replace(inline.link)
18344 ('inside', inline._inside)
18345 ('href', inline._href)
18348 inline.reflink = replace(inline.reflink)
18349 ('inside', inline._inside)
18353 * Normal Inline Grammar
18356 inline.normal = merge({}, inline);
18359 * Pedantic Inline Grammar
18362 inline.pedantic = merge({}, inline.normal, {
18363 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18364 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18368 * GFM Inline Grammar
18371 inline.gfm = merge({}, inline.normal, {
18372 escape: replace(inline.escape)('])', '~|])')(),
18373 url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18374 del: /^~~(?=\S)([\s\S]*?\S)~~/,
18375 text: replace(inline.text)
18377 ('|', '|https?://|')
18382 * GFM + Line Breaks Inline Grammar
18385 inline.breaks = merge({}, inline.gfm, {
18386 br: replace(inline.br)('{2,}', '*')(),
18387 text: replace(inline.gfm.text)('{2,}', '*')()
18391 * Inline Lexer & Compiler
18394 function InlineLexer(links, options) {
18395 this.options = options || marked.defaults;
18396 this.links = links;
18397 this.rules = inline.normal;
18401 Error('Tokens array requires a `links` property.');
18404 if (this.options.gfm) {
18405 if (this.options.breaks) {
18406 this.rules = inline.breaks;
18408 this.rules = inline.gfm;
18410 } else if (this.options.pedantic) {
18411 this.rules = inline.pedantic;
18416 * Expose Inline Rules
18419 InlineLexer.rules = inline;
18422 * Static Lexing/Compiling Method
18425 InlineLexer.output = function(src, links, options) {
18426 var inline = new InlineLexer(links, options);
18427 return inline.output(src);
18434 InlineLexer.prototype.output = function(src) {
18443 if (cap = this.rules.escape.exec(src)) {
18444 src = src.substring(cap[0].length);
18450 if (cap = this.rules.autolink.exec(src)) {
18451 src = src.substring(cap[0].length);
18452 if (cap[2] === '@') {
18453 text = cap[1][6] === ':'
18454 ? this.mangle(cap[1].substring(7))
18455 : this.mangle(cap[1]);
18456 href = this.mangle('mailto:') + text;
18458 text = escape(cap[1]);
18470 if (cap = this.rules.url.exec(src)) {
18471 src = src.substring(cap[0].length);
18472 text = escape(cap[1]);
18483 if (cap = this.rules.tag.exec(src)) {
18484 src = src.substring(cap[0].length);
18485 out += this.options.sanitize
18492 if (cap = this.rules.link.exec(src)) {
18493 src = src.substring(cap[0].length);
18494 out += this.outputLink(cap, {
18502 if ((cap = this.rules.reflink.exec(src))
18503 || (cap = this.rules.nolink.exec(src))) {
18504 src = src.substring(cap[0].length);
18505 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18506 link = this.links[link.toLowerCase()];
18507 if (!link || !link.href) {
18509 src = cap[0].substring(1) + src;
18512 out += this.outputLink(cap, link);
18517 if (cap = this.rules.strong.exec(src)) {
18518 src = src.substring(cap[0].length);
18520 + this.output(cap[2] || cap[1])
18526 if (cap = this.rules.em.exec(src)) {
18527 src = src.substring(cap[0].length);
18529 + this.output(cap[2] || cap[1])
18535 if (cap = this.rules.code.exec(src)) {
18536 src = src.substring(cap[0].length);
18538 + escape(cap[2], true)
18544 if (cap = this.rules.br.exec(src)) {
18545 src = src.substring(cap[0].length);
18551 if (cap = this.rules.del.exec(src)) {
18552 src = src.substring(cap[0].length);
18554 + this.output(cap[1])
18560 if (cap = this.rules.text.exec(src)) {
18561 src = src.substring(cap[0].length);
18562 out += escape(cap[0]);
18568 Error('Infinite loop on byte: ' + src.charCodeAt(0));
18579 InlineLexer.prototype.outputLink = function(cap, link) {
18580 if (cap[0][0] !== '!') {
18582 + escape(link.href)
18586 + escape(link.title)
18590 + this.output(cap[1])
18593 return '<img src="'
18594 + escape(link.href)
18600 + escape(link.title)
18608 * Smartypants Transformations
18611 InlineLexer.prototype.smartypants = function(text) {
18612 if (!this.options.smartypants) return text;
18614 .replace(/--/g, '—')
18615 .replace(/'([^']*)'/g, '‘$1’')
18616 .replace(/"([^"]*)"/g, '“$1”')
18617 .replace(/\.{3}/g, '…');
18624 InlineLexer.prototype.mangle = function(text) {
18630 for (; i < l; i++) {
18631 ch = text.charCodeAt(i);
18632 if (Math.random() > 0.5) {
18633 ch = 'x' + ch.toString(16);
18635 out += '&#' + ch + ';';
18642 * Parsing & Compiling
18645 function Parser(options) {
18648 this.options = options || marked.defaults;
18652 * Static Parse Method
18655 Parser.parse = function(src, options) {
18656 var parser = new Parser(options);
18657 return parser.parse(src);
18664 Parser.prototype.parse = function(src) {
18665 this.inline = new InlineLexer(src.links, this.options);
18666 this.tokens = src.reverse();
18669 while (this.next()) {
18680 Parser.prototype.next = function() {
18681 return this.token = this.tokens.pop();
18685 * Preview Next Token
18688 Parser.prototype.peek = function() {
18689 return this.tokens[this.tokens.length-1] || 0;
18693 * Parse Text Tokens
18696 Parser.prototype.parseText = function() {
18697 var body = this.token.text;
18699 while (this.peek().type === 'text') {
18700 body += '\n' + this.next().text;
18703 return this.inline.output(body);
18707 * Parse Current Token
18710 Parser.prototype.tok = function() {
18711 switch (this.token.type) {
18722 + this.inline.output(this.token.text)
18728 if (this.options.highlight) {
18729 var code = this.options.highlight(this.token.text, this.token.lang);
18730 if (code != null && code !== this.token.text) {
18731 this.token.escaped = true;
18732 this.token.text = code;
18736 if (!this.token.escaped) {
18737 this.token.text = escape(this.token.text, true);
18740 return '<pre><code'
18743 + this.options.langPrefix
18749 + '</code></pre>\n';
18760 body += '<thead>\n<tr>\n';
18761 for (i = 0; i < this.token.header.length; i++) {
18762 heading = this.inline.output(this.token.header[i]);
18763 body += this.token.align[i]
18764 ? '<th align="' + this.token.align[i] + '">' + heading + '</th>\n'
18765 : '<th>' + heading + '</th>\n';
18767 body += '</tr>\n</thead>\n';
18770 body += '<tbody>\n'
18771 for (i = 0; i < this.token.cells.length; i++) {
18772 row = this.token.cells[i];
18774 for (j = 0; j < row.length; j++) {
18775 cell = this.inline.output(row[j]);
18776 body += this.token.align[j]
18777 ? '<td align="' + this.token.align[j] + '">' + cell + '</td>\n'
18778 : '<td>' + cell + '</td>\n';
18782 body += '</tbody>\n';
18788 case 'blockquote_start': {
18791 while (this.next().type !== 'blockquote_end') {
18792 body += this.tok();
18795 return '<blockquote>\n'
18797 + '</blockquote>\n';
18799 case 'list_start': {
18800 var type = this.token.ordered ? 'ol' : 'ul'
18803 while (this.next().type !== 'list_end') {
18804 body += this.tok();
18815 case 'list_item_start': {
18818 while (this.next().type !== 'list_item_end') {
18819 body += this.token.type === 'text'
18828 case 'loose_item_start': {
18831 while (this.next().type !== 'list_item_end') {
18832 body += this.tok();
18840 return !this.token.pre && !this.options.pedantic
18841 ? this.inline.output(this.token.text)
18844 case 'paragraph': {
18846 + this.inline.output(this.token.text)
18861 function escape(html, encode) {
18863 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
18864 .replace(/</g, '<')
18865 .replace(/>/g, '>')
18866 .replace(/"/g, '"')
18867 .replace(/'/g, ''');
18870 function replace(regex, opt) {
18871 regex = regex.source;
18873 return function self(name, val) {
18874 if (!name) return new RegExp(regex, opt);
18875 val = val.source || val;
18876 val = val.replace(/(^|[^\[])\^/g, '$1');
18877 regex = regex.replace(name, val);
18885 function merge(obj) {
18890 for (; i < arguments.length; i++) {
18891 target = arguments[i];
18892 for (key in target) {
18893 if (Object.prototype.hasOwnProperty.call(target, key)) {
18894 obj[key] = target[key];
18906 function marked(src, opt, callback) {
18907 if (callback || typeof opt === 'function') {
18913 if (opt) opt = merge({}, marked.defaults, opt);
18915 var tokens = Lexer.lex(tokens, opt)
18916 , highlight = opt.highlight
18918 , l = tokens.length
18921 if (!highlight || highlight.length < 3) {
18922 return callback(null, Parser.parse(tokens, opt));
18925 var done = function() {
18926 delete opt.highlight;
18927 var out = Parser.parse(tokens, opt);
18928 opt.highlight = highlight;
18929 return callback(null, out);
18932 for (; i < l; i++) {
18934 if (token.type !== 'code') return;
18936 return highlight(token.text, token.lang, function(err, code) {
18937 if (code == null || code === token.text) {
18938 return --pending || done();
18941 token.escaped = true;
18942 --pending || done();
18950 if (opt) opt = merge({}, marked.defaults, opt);
18951 return Parser.parse(Lexer.lex(src, opt), opt);
18953 e.message += '\nPlease report this to https://github.com/chjj/marked.';
18954 if ((opt || marked.defaults).silent) {
18955 return '<p>An error occured:</p><pre>'
18956 + escape(e.message + '', true)
18968 marked.setOptions = function(opt) {
18969 merge(marked.defaults, opt);
18973 marked.defaults = {
18982 langPrefix: 'lang-'
18989 marked.Parser = Parser;
18990 marked.parser = Parser.parse;
18992 marked.Lexer = Lexer;
18993 marked.lexer = Lexer.lex;
18995 marked.InlineLexer = InlineLexer;
18996 marked.inlineLexer = InlineLexer.output;
18998 marked.parse = marked;
19000 if (typeof exports === 'object') {
19001 module.exports = marked;
19002 } else if (typeof define === 'function' && define.amd) {
19003 define(function() { return marked; });
19005 this.marked = marked;
19008 }).call(function() {
19009 return this || (typeof window !== 'undefined' ? window : global);
19013 window.iD = function () {
19014 window.locale.en = iD.data.en;
19015 window.locale.current('en');
19017 var dispatch = d3.dispatch('enter', 'exit'),
19020 // https://github.com/openstreetmap/iD/issues/772
19021 // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
19023 try { storage = localStorage; } catch (e) {} // eslint-disable-line no-empty
19024 storage = storage || (function() {
19027 getItem: function(k) { return s[k]; },
19028 setItem: function(k, v) { s[k] = v; },
19029 removeItem: function(k) { delete s[k]; }
19033 context.storage = function(k, v) {
19035 if (arguments.length === 1) return storage.getItem(k);
19036 else if (v === null) storage.removeItem(k);
19037 else storage.setItem(k, v);
19039 // localstorage quota exceeded
19040 /* eslint-disable no-console */
19041 if (typeof console !== 'undefined') console.error('localStorage quota exceeded');
19042 /* eslint-enable no-console */
19047 /* Straight accessors. Avoid using these if you can. */
19048 var ui, connection, history;
19049 context.ui = function() { return ui; };
19050 context.connection = function() { return connection; };
19051 context.history = function() { return history; };
19055 function entitiesLoaded(err, result) {
19056 if (!err) history.merge(result.data, result.extent);
19059 context.preauth = function(options) {
19060 connection.switch(options);
19064 context.loadTiles = function(projection, dimensions, callback) {
19065 function done(err, result) {
19066 entitiesLoaded(err, result);
19067 if (callback) callback(err, result);
19069 connection.loadTiles(projection, dimensions, done);
19072 context.loadEntity = function(id, callback) {
19073 function done(err, result) {
19074 entitiesLoaded(err, result);
19075 if (callback) callback(err, result);
19077 connection.loadEntity(id, done);
19080 context.zoomToEntity = function(id, zoomTo) {
19081 if (zoomTo !== false) {
19082 this.loadEntity(id, function(err, result) {
19084 var entity = _.find(result.data, function(e) { return e.id === id; });
19085 if (entity) { map.zoomTo(entity); }
19089 map.on('drawn.zoomToEntity', function() {
19090 if (!context.hasEntity(id)) return;
19091 map.on('drawn.zoomToEntity', null);
19092 context.on('enter.zoomToEntity', null);
19093 context.enter(iD.modes.Select(context, [id]));
19096 context.on('enter.zoomToEntity', function() {
19097 if (mode.id !== 'browse') {
19098 map.on('drawn.zoomToEntity', null);
19099 context.on('enter.zoomToEntity', null);
19104 var minEditableZoom = 16;
19105 context.minEditableZoom = function(_) {
19106 if (!arguments.length) return minEditableZoom;
19107 minEditableZoom = _;
19108 connection.tileZoom(_);
19114 var inIntro = false;
19115 context.inIntro = function(_) {
19116 if (!arguments.length) return inIntro;
19121 context.save = function() {
19122 if (inIntro || (mode && mode.id === 'save') || d3.select('.modal').size()) return;
19124 if (history.hasChanges()) return t('save.unsaved_changes');
19127 context.flush = function() {
19128 context.debouncedSave.cancel();
19129 connection.flush();
19132 _.each(iD.services, function(service) {
19133 var reset = service().reset;
19134 if (reset) reset(context);
19141 context.hasEntity = function(id) {
19142 return history.graph().hasEntity(id);
19144 context.entity = function(id) {
19145 return history.graph().entity(id);
19147 context.childNodes = function(way) {
19148 return history.graph().childNodes(way);
19150 context.geometry = function(id) {
19151 return context.entity(id).geometry(history.graph());
19157 context.mode = function() {
19160 context.enter = function(newMode) {
19163 dispatch.exit(mode);
19168 dispatch.enter(mode);
19171 context.selectedIDs = function() {
19172 if (mode && mode.selectedIDs) {
19173 return mode.selectedIDs();
19181 context.install = function(behavior) {
19182 context.surface().call(behavior);
19184 context.uninstall = function(behavior) {
19185 context.surface().call(behavior.off);
19190 var copyIDs = [], copyGraph;
19191 context.copyGraph = function() { return copyGraph; };
19192 context.copyIDs = function(_) {
19193 if (!arguments.length) return copyIDs;
19195 copyGraph = history.graph();
19202 context.background = function() { return background; };
19207 context.features = function() { return features; };
19208 context.hasHiddenConnections = function(id) {
19209 var graph = history.graph(),
19210 entity = graph.entity(id);
19211 return features.hasHiddenConnections(entity, graph);
19217 context.map = function() { return map; };
19218 context.layers = function() { return map.layers; };
19219 context.surface = function() { return map.surface; };
19220 context.editable = function() { return map.editable(); };
19222 context.surfaceRect = function() {
19223 // Work around a bug in Firefox.
19224 // http://stackoverflow.com/questions/18153989/
19225 // https://bugzilla.mozilla.org/show_bug.cgi?id=530985
19226 return context.surface().node().parentNode.getBoundingClientRect();
19231 var debugTile = false, debugCollision = false;
19232 context.debugTile = function(_) {
19233 if (!arguments.length) return debugTile;
19237 context.debugCollision = function(_) {
19238 if (!arguments.length) return debugCollision;
19239 debugCollision = _;
19246 context.presets = function(_) {
19247 if (!arguments.length) return presets;
19249 iD.areaKeys = presets.areaKeys();
19255 context.imagery = function(_) {
19256 background.load(_);
19262 var container, embed;
19263 context.container = function(_) {
19264 if (!arguments.length) return container;
19266 container.classed('id-container', true);
19269 context.embed = function(_) {
19270 if (!arguments.length) return embed;
19278 context.taginfo = function(_) {
19279 if (!arguments.length) return taginfo;
19286 var assetPath = '';
19287 context.assetPath = function(_) {
19288 if (!arguments.length) return assetPath;
19294 context.assetMap = function(_) {
19295 if (!arguments.length) return assetMap;
19300 context.asset = function(_) {
19301 var filename = assetPath + _;
19302 return assetMap[filename] || filename;
19305 context.imagePath = function(_) {
19306 return context.asset('img/' + _);
19309 var locale, localePath;
19310 context.locale = function(loc, path) {
19314 // Also set iD.detect().locale (unless we detected 'en-us' and openstreetmap wants 'en')..
19315 if (!(loc.toLowerCase() === 'en' && iD.detect().locale.toLowerCase() === 'en-us')) {
19316 iD.detect().locale = loc;
19322 context.loadLocale = function(cb) {
19323 if (locale && locale !== 'en' && iD.data.locales.indexOf(locale) !== -1) {
19324 localePath = localePath || context.asset('locales/' + locale + '.json');
19325 d3.json(localePath, function(err, result) {
19326 window.locale[locale] = result;
19327 window.locale.current(locale);
19338 context.projection = iD.geo.RawMercator();
19340 locale = iD.detect().locale;
19341 if (locale && iD.data.locales.indexOf(locale) === -1) {
19342 locale = locale.split('-')[0];
19345 history = iD.History(context);
19346 context.graph = history.graph;
19347 context.changes = history.changes;
19348 context.intersects = history.intersects;
19350 // Debounce save, since it's a synchronous localStorage write,
19351 // and history changes can happen frequently (e.g. when dragging).
19352 context.debouncedSave = _.debounce(context.save, 350);
19353 function withDebouncedSave(fn) {
19354 return function() {
19355 var result = fn.apply(history, arguments);
19356 context.debouncedSave();
19361 context.perform = withDebouncedSave(history.perform);
19362 context.replace = withDebouncedSave(history.replace);
19363 context.pop = withDebouncedSave(history.pop);
19364 context.overwrite = withDebouncedSave(history.overwrite);
19365 context.undo = withDebouncedSave(history.undo);
19366 context.redo = withDebouncedSave(history.redo);
19368 ui = iD.ui(context);
19370 connection = iD.Connection();
19372 background = iD.Background(context);
19374 features = iD.Features(context);
19376 map = iD.Map(context);
19377 context.mouse = map.mouse;
19378 context.extent = map.extent;
19379 context.pan = map.pan;
19380 context.zoomIn = map.zoomIn;
19381 context.zoomOut = map.zoomOut;
19382 context.zoomInFurther = map.zoomInFurther;
19383 context.zoomOutFurther = map.zoomOutFurther;
19384 context.redrawEnable = map.redrawEnable;
19386 presets = iD.presets();
19388 return d3.rebind(context, dispatch, 'on');
19392 iD.version = '1.9.4';
19397 var ua = navigator.userAgent,
19400 m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge
19402 detected.browser = m[1];
19403 detected.version = m[2];
19405 if (!detected.browser) {
19406 m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i); // IE11
19408 detected.browser = 'msie';
19409 detected.version = m[1];
19412 if (!detected.browser) {
19413 m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i); // Opera 15+
19415 detected.browser = 'Opera';
19416 detected.version = m[2];
19419 if (!detected.browser) {
19420 m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
19422 detected.browser = m[1];
19423 detected.version = m[2];
19424 m = ua.match(/version\/([\.\d]+)/i);
19425 if (m !== null) detected.version = m[1];
19428 if (!detected.browser) {
19429 detected.browser = navigator.appName;
19430 detected.version = navigator.appVersion;
19433 // keep major.minor version only..
19434 detected.version = detected.version.split(/\W/).slice(0,2).join('.');
19436 if (detected.browser.toLowerCase() === 'msie') {
19437 detected.ie = true;
19438 detected.browser = 'Internet Explorer';
19439 detected.support = parseFloat(detected.version) >= 11;
19441 detected.ie = false;
19442 detected.support = true;
19445 // Added due to incomplete svg style support. See #715
19446 detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 );
19448 detected.locale = (navigator.languages && navigator.languages.length)
19449 ? navigator.languages[0] : (navigator.language || navigator.userLanguage || 'en-US');
19451 detected.filedrop = (window.FileReader && 'ondrop' in window);
19454 return navigator.userAgent.indexOf(x) !== -1;
19458 detected.os = 'win';
19459 detected.platform = 'Windows';
19461 else if (nav('Mac')) {
19462 detected.os = 'mac';
19463 detected.platform = 'Macintosh';
19465 else if (nav('X11') || nav('Linux')) {
19466 detected.os = 'linux';
19467 detected.platform = 'Linux';
19470 detected.os = 'win';
19471 detected.platform = 'Unknown';
19474 iD.detect = function() { return detected; };
19477 iD.services.mapillary = function() {
19478 var mapillary = {},
19479 dispatch = d3.dispatch('loadedImages', 'loadedSigns'),
19480 apibase = 'https://a.mapillary.com/v2/',
19481 urlImage = 'https://www.mapillary.com/map/im/',
19482 urlThumb = 'https://d1cuyjsrcm0gby.cloudfront.net/',
19483 clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi',
19488 function loadSignStyles(context) {
19489 d3.select('head').selectAll('#traffico')
19493 .attr('id', 'traffico')
19494 .attr('rel', 'stylesheet')
19495 .attr('href', context.asset('traffico/stylesheets/traffico.css'));
19498 function loadSignDefs(context) {
19499 if (!iD.services.mapillary.sign_defs) {
19500 iD.services.mapillary.sign_defs = {};
19501 _.each(['au', 'br', 'ca', 'de', 'us'], function(region) {
19502 d3.json(context.asset('traffico/string-maps/' + region + '-map.json'), function(err, data) {
19504 if (region === 'de') region = 'eu';
19505 iD.services.mapillary.sign_defs[region] = data;
19511 function abortRequest(i) {
19515 function nearNullIsland(x, y, z) {
19517 var center = Math.pow(2, z - 1),
19518 width = Math.pow(2, z - 6),
19519 min = center - (width / 2),
19520 max = center + (width / 2) - 1;
19521 return x >= min && x <= max && y >= min && y <= max;
19526 function getTiles(projection, dimensions) {
19527 var s = projection.scale() * 2 * Math.PI,
19528 z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
19529 ts = 256 * Math.pow(2, z - tileZoom),
19531 s / 2 - projection.translate()[0],
19532 s / 2 - projection.translate()[1]];
19534 return d3.geo.tile()
19535 .scaleExtent([tileZoom, tileZoom])
19538 .translate(projection.translate())()
19539 .map(function(tile) {
19540 var x = tile[0] * ts - origin[0],
19541 y = tile[1] * ts - origin[1];
19544 id: tile.toString(),
19545 extent: iD.geo.Extent(
19546 projection.invert([x, y + ts]),
19547 projection.invert([x + ts, y]))
19553 function loadTiles(which, url, projection, dimensions) {
19554 var tiles = getTiles(projection, dimensions).filter(function(t) {
19555 var xyz = t.id.split(',');
19556 return !nearNullIsland(xyz[0], xyz[1], xyz[2]);
19559 _.filter(which.inflight, function(v, k) {
19560 var wanted = _.find(tiles, function(tile) { return k === (tile.id + ',0'); });
19561 if (!wanted) delete which.inflight[k];
19563 }).map(abortRequest);
19565 tiles.forEach(function(tile) {
19566 loadTilePage(which, url, tile, 0);
19570 function loadTilePage(which, url, tile, page) {
19571 var cache = iD.services.mapillary.cache[which],
19572 id = tile.id + ',' + String(page),
19573 rect = tile.extent.rectangle();
19575 if (cache.loaded[id] || cache.inflight[id]) return;
19577 cache.inflight[id] = d3.json(url +
19582 client_id: clientId,
19587 }), function(err, data) {
19588 cache.loaded[id] = true;
19589 delete cache.inflight[id];
19590 if (err || !data.features || !data.features.length) return;
19595 for (var i = 0; i < data.features.length; i++) {
19596 feature = data.features[i];
19597 loc = feature.geometry.coordinates;
19598 d = { key: feature.properties.key, loc: loc };
19599 if (which === 'images') d.ca = feature.properties.ca;
19600 if (which === 'signs') d.signs = feature.properties.rects;
19602 features.push([loc[0], loc[1], loc[0], loc[1], d]);
19605 cache.rtree.load(features);
19607 if (which === 'images') dispatch.loadedImages();
19608 if (which === 'signs') dispatch.loadedSigns();
19610 if (data.features.length === maxResults) {
19611 loadTilePage(which, url, tile, ++page);
19617 mapillary.loadImages = function(projection, dimensions) {
19618 var url = apibase + 'search/im/geojson?';
19619 loadTiles('images', url, projection, dimensions);
19622 mapillary.loadSigns = function(context, projection, dimensions) {
19623 var url = apibase + 'search/im/geojson/or?';
19624 loadSignStyles(context);
19625 loadSignDefs(context);
19626 loadTiles('signs', url, projection, dimensions);
19630 // partition viewport into `psize` x `psize` regions
19631 function partitionViewport(psize, projection, dimensions) {
19632 psize = psize || 16;
19633 var cols = d3.range(0, dimensions[0], psize),
19634 rows = d3.range(0, dimensions[1], psize),
19637 rows.forEach(function(y) {
19638 cols.forEach(function(x) {
19639 var min = [x, y + psize],
19640 max = [x + psize, y];
19642 iD.geo.Extent(projection.invert(min), projection.invert(max)));
19649 // no more than `limit` results per partition.
19650 function searchLimited(psize, limit, projection, dimensions, rtree) {
19651 limit = limit || 3;
19653 var partitions = partitionViewport(psize, projection, dimensions);
19654 return _.flatten(_.compact(_.map(partitions, function(extent) {
19655 return rtree.search(extent.rectangle())
19657 .map(function(d) { return d[4]; });
19661 mapillary.images = function(projection, dimensions) {
19662 var psize = 16, limit = 3;
19663 return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.images.rtree);
19666 mapillary.signs = function(projection, dimensions) {
19667 var psize = 32, limit = 3;
19668 return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.signs.rtree);
19671 mapillary.signsSupported = function() {
19672 var detected = iD.detect();
19673 return (!(detected.ie || detected.browser.toLowerCase() === 'safari'));
19676 mapillary.signHTML = function(d) {
19677 if (!iD.services.mapillary.sign_defs) return;
19679 var detectionPackage = d.signs[0].package,
19680 type = d.signs[0].type,
19681 country = detectionPackage.split('_')[1];
19682 return iD.services.mapillary.sign_defs[country][type];
19685 mapillary.showThumbnail = function(imageKey, position) {
19686 if (!imageKey) return;
19688 var positionClass = {
19689 'ar': (position !== 'left'),
19690 'al': (position === 'left')
19693 var thumbnail = d3.select('#content').selectAll('.mapillary-image')
19697 var enter = thumbnail.enter().append('div')
19698 .attr('class', 'mapillary-image ar');
19700 enter.append('button')
19701 .on('click', function () {
19702 mapillary.hideThumbnail();
19705 .call(iD.svg.Icon('#icon-close'));
19707 enter.append('img');
19710 .attr('class', 'link ar')
19711 .attr('target', '_blank')
19712 .call(iD.svg.Icon('#icon-out-link', 'inline'))
19714 .text(t('mapillary.view_on_mapillary'));
19717 thumbnail.selectAll('img')
19718 .attr('src', urlThumb + imageKey + '/thumb-320.jpg');
19720 var link = thumbnail.selectAll('a')
19721 .attr('href', urlImage + imageKey);
19724 thumbnail.classed(positionClass);
19725 link.classed(positionClass);
19731 .style('opacity', 1);
19734 mapillary.hideThumbnail = function() {
19735 if (iD.services.mapillary) {
19736 iD.services.mapillary.thumb = null;
19738 d3.select('#content').selectAll('.mapillary-image')
19741 .style('opacity', 0)
19745 mapillary.selectedThumbnail = function(d) {
19746 if (!iD.services.mapillary) return null;
19747 if (!arguments.length) return iD.services.mapillary.thumb;
19748 iD.services.mapillary.thumb = d;
19751 mapillary.reset = function() {
19752 var cache = iD.services.mapillary.cache;
19755 _.forEach(cache.images.inflight, abortRequest);
19756 _.forEach(cache.signs.inflight, abortRequest);
19759 iD.services.mapillary.cache = {
19760 images: { inflight: {}, loaded: {}, rtree: rbush() },
19761 signs: { inflight: {}, loaded: {}, rtree: rbush() }
19764 iD.services.mapillary.thumb = null;
19770 if (!iD.services.mapillary.cache) {
19774 return d3.rebind(mapillary, dispatch, 'on');
19776 iD.services.nominatim = function() {
19777 var nominatim = {},
19778 endpoint = 'https://nominatim.openstreetmap.org/reverse?';
19781 nominatim.countryCode = function(location, callback) {
19782 var cache = iD.services.nominatim.cache,
19783 countryCodes = cache.search([location[0], location[1], location[0], location[1]]);
19785 if (countryCodes.length > 0)
19786 return callback(null, countryCodes[0][4]);
19794 }), function(err, result) {
19796 return callback(err);
19797 else if (result && result.error)
19798 return callback(result.error);
19800 var extent = iD.geo.Extent(location).padByMeters(1000);
19802 cache.insert(extent.rectangle().concat(result.address.country_code));
19804 callback(null, result.address.country_code);
19808 nominatim.reset = function() {
19809 iD.services.nominatim.cache = rbush();
19814 if (!iD.services.nominatim.cache) {
19820 iD.services.taginfo = function() {
19822 endpoint = 'https://taginfo.openstreetmap.org/api/4/',
19824 point: 'count_nodes',
19825 vertex: 'count_nodes',
19826 area: 'count_ways',
19837 function sets(parameters, n, o) {
19838 if (parameters.geometry && o[parameters.geometry]) {
19839 parameters[n] = o[parameters.geometry];
19844 function setFilter(parameters) {
19845 return sets(parameters, 'filter', tag_filters);
19848 function setSort(parameters) {
19849 return sets(parameters, 'sortname', tag_sorts);
19852 function clean(parameters) {
19853 return _.omit(parameters, 'geometry', 'debounce');
19856 function filterKeys(type) {
19857 var count_type = type ? 'count_' + type : 'count_all';
19858 return function(d) {
19859 return parseFloat(d[count_type]) > 2500 || d.in_wiki;
19863 function filterMultikeys() {
19864 return function(d) {
19865 return (d.key.match(/:/g) || []).length === 1; // exactly one ':'
19869 function filterValues() {
19870 return function(d) {
19871 if (d.value.match(/[A-Z*;,]/) !== null) return false; // exclude some punctuation, uppercase letters
19872 return parseFloat(d.fraction) > 0.0 || d.in_wiki;
19876 function valKey(d) {
19883 function valKeyDescription(d) {
19886 title: d.description || d.value
19890 // sort keys with ':' lower than keys without ':'
19891 function sortKeys(a, b) {
19892 return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
19893 : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
19897 var debounced = _.debounce(d3.json, 100, true);
19899 function request(url, debounce, callback) {
19900 var cache = iD.services.taginfo.cache;
19903 callback(null, cache[url]);
19904 } else if (debounce) {
19905 debounced(url, done);
19907 d3.json(url, done);
19910 function done(err, data) {
19911 if (!err) cache[url] = data;
19912 callback(err, data);
19916 taginfo.keys = function(parameters, callback) {
19917 var debounce = parameters.debounce;
19918 parameters = clean(setSort(parameters));
19919 request(endpoint + 'keys/all?' +
19920 iD.util.qsString(_.extend({
19922 sortname: 'count_all',
19925 }, parameters)), debounce, function(err, d) {
19926 if (err) return callback(err);
19927 var f = filterKeys(parameters.filter);
19928 callback(null, d.data.filter(f).sort(sortKeys).map(valKey));
19932 taginfo.multikeys = function(parameters, callback) {
19933 var debounce = parameters.debounce;
19934 parameters = clean(setSort(parameters));
19935 request(endpoint + 'keys/all?' +
19936 iD.util.qsString(_.extend({
19938 sortname: 'count_all',
19941 }, parameters)), debounce, function(err, d) {
19942 if (err) return callback(err);
19943 var f = filterMultikeys();
19944 callback(null, d.data.filter(f).map(valKey));
19948 taginfo.values = function(parameters, callback) {
19949 var debounce = parameters.debounce;
19950 parameters = clean(setSort(setFilter(parameters)));
19951 request(endpoint + 'key/values?' +
19952 iD.util.qsString(_.extend({
19954 sortname: 'count_all',
19957 }, parameters)), debounce, function(err, d) {
19958 if (err) return callback(err);
19959 var f = filterValues();
19960 callback(null, d.data.filter(f).map(valKeyDescription));
19964 taginfo.docs = function(parameters, callback) {
19965 var debounce = parameters.debounce;
19966 parameters = clean(setSort(parameters));
19968 var path = 'key/wiki_pages?';
19969 if (parameters.value) path = 'tag/wiki_pages?';
19970 else if (parameters.rtype) path = 'relation/wiki_pages?';
19972 request(endpoint + path + iD.util.qsString(parameters), debounce, function(err, d) {
19973 if (err) return callback(err);
19974 callback(null, d.data);
19978 taginfo.endpoint = function(_) {
19979 if (!arguments.length) return endpoint;
19984 taginfo.reset = function() {
19985 iD.services.taginfo.cache = {};
19990 if (!iD.services.taginfo.cache) {
19996 iD.services.wikipedia = function() {
19998 endpoint = 'https://en.wikipedia.org/w/api.php?';
20000 wiki.search = function(lang, query, callback) {
20001 lang = lang || 'en';
20002 d3.jsonp(endpoint.replace('en', lang) +
20007 srinfo: 'suggestion',
20009 callback: '{callback}',
20011 }), function(data) {
20012 if (!data.query) return;
20013 callback(query, data.query.search.map(function(d) {
20019 wiki.suggestions = function(lang, query, callback) {
20020 lang = lang || 'en';
20021 d3.jsonp(endpoint.replace('en', lang) +
20023 action: 'opensearch',
20027 callback: '{callback}',
20030 callback(d[0], d[1]);
20034 wiki.translations = function(lang, title, callback) {
20035 d3.jsonp(endpoint.replace('en', lang) +
20040 callback: '{callback}',
20044 var list = d.query.pages[Object.keys(d.query.pages)[0]],
20046 if (list && list.langlinks) {
20047 list.langlinks.forEach(function(d) {
20048 translations[d.lang] = d['*'];
20050 callback(translations);
20059 iD.util.tagText = function(entity) {
20060 return d3.entries(entity.tags).map(function(e) {
20061 return e.key + '=' + e.value;
20065 iD.util.entitySelector = function(ids) {
20066 return ids.length ? '.' + ids.join(',.') : 'nothing';
20069 iD.util.entityOrMemberSelector = function(ids, graph) {
20070 var s = iD.util.entitySelector(ids);
20072 ids.forEach(function(id) {
20073 var entity = graph.hasEntity(id);
20074 if (entity && entity.type === 'relation') {
20075 entity.members.forEach(function(member) {
20076 s += ',.' + member.id;
20084 iD.util.displayName = function(entity) {
20085 var localeName = 'name:' + iD.detect().locale.toLowerCase().split('-')[0];
20086 return entity.tags[localeName] || entity.tags.name || entity.tags.ref;
20089 iD.util.displayType = function(id) {
20091 n: t('inspector.node'),
20092 w: t('inspector.way'),
20093 r: t('inspector.relation')
20097 iD.util.stringQs = function(str) {
20098 return str.split('&').reduce(function(obj, pair){
20099 var parts = pair.split('=');
20100 if (parts.length === 2) {
20101 obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
20107 iD.util.qsString = function(obj, noencode) {
20108 function softEncode(s) {
20109 // encode everything except special characters used in certain hash parameters:
20110 // "/" in map states, ":", ",", {" and "}" in background
20111 return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
20113 return Object.keys(obj).sort().map(function(key) {
20114 return encodeURIComponent(key) + '=' + (
20115 noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
20119 iD.util.prefixDOMProperty = function(property) {
20120 var prefixes = ['webkit', 'ms', 'moz', 'o'],
20122 n = prefixes.length,
20128 property = property.substr(0, 1).toUpperCase() + property.substr(1);
20131 if (prefixes[i] + property in s)
20132 return prefixes[i] + property;
20137 iD.util.prefixCSSProperty = function(property) {
20138 var prefixes = ['webkit', 'ms', 'Moz', 'O'],
20140 n = prefixes.length,
20141 s = document.body.style;
20143 if (property.toLowerCase() in s)
20144 return property.toLowerCase();
20147 if (prefixes[i] + property in s)
20148 return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
20154 iD.util.setTransform = function(el, x, y, scale) {
20155 var prop = iD.util.transformProperty = iD.util.transformProperty || iD.util.prefixCSSProperty('Transform'),
20156 translate = iD.detect().opera ?
20157 'translate(' + x + 'px,' + y + 'px)' :
20158 'translate3d(' + x + 'px,' + y + 'px,0)';
20159 return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
20162 iD.util.getStyle = function(selector) {
20163 for (var i = 0; i < document.styleSheets.length; i++) {
20164 var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules || [];
20165 for (var k = 0; k < rules.length; k++) {
20166 var selectorText = rules[k].selectorText && rules[k].selectorText.split(', ');
20167 if (_.contains(selectorText, selector)) {
20174 iD.util.editDistance = function(a, b) {
20175 if (a.length === 0) return b.length;
20176 if (b.length === 0) return a.length;
20178 for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
20179 for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
20180 for (i = 1; i <= b.length; i++) {
20181 for (j = 1; j <= a.length; j++) {
20182 if (b.charAt(i-1) === a.charAt(j-1)) {
20183 matrix[i][j] = matrix[i-1][j-1];
20185 matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
20186 Math.min(matrix[i][j-1] + 1, // insertion
20187 matrix[i-1][j] + 1)); // deletion
20191 return matrix[b.length][a.length];
20194 // a d3.mouse-alike which
20195 // 1. Only works on HTML elements, not SVG
20196 // 2. Does not cause style recalculation
20197 iD.util.fastMouse = function(container) {
20198 var rect = container.getBoundingClientRect(),
20199 rectLeft = rect.left,
20200 rectTop = rect.top,
20201 clientLeft = +container.clientLeft,
20202 clientTop = +container.clientTop;
20203 return function(e) {
20205 e.clientX - rectLeft - clientLeft,
20206 e.clientY - rectTop - clientTop];
20210 /* eslint-disable no-proto */
20211 iD.util.getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; };
20212 /* eslint-enable no-proto */
20214 iD.util.asyncMap = function(inputs, func, callback) {
20215 var remaining = inputs.length,
20219 inputs.forEach(function(d, i) {
20220 func(d, function done(err, data) {
20224 if (!remaining) callback(errors, results);
20229 // wraps an index to an interval [0..length-1]
20230 iD.util.wrap = function(index, length) {
20232 index += Math.ceil(-index/length)*length;
20233 return index % length;
20235 // A per-domain session mutex backed by a cookie and dead man's
20236 // switch. If the session crashes, the mutex will auto-release
20237 // after 5 seconds.
20239 iD.util.SessionMutex = function(name) {
20244 var expires = new Date();
20245 expires.setSeconds(expires.getSeconds() + 5);
20246 document.cookie = name + '=1; expires=' + expires.toUTCString();
20249 mutex.lock = function() {
20250 if (intervalID) return true;
20251 var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
20252 if (cookie) return false;
20254 intervalID = window.setInterval(renew, 4000);
20258 mutex.unlock = function() {
20259 if (!intervalID) return;
20260 document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
20261 clearInterval(intervalID);
20265 mutex.locked = function() {
20266 return !!intervalID;
20271 iD.util.SuggestNames = function(preset, suggestions) {
20272 preset = preset.id.split('/', 2);
20276 return function(value, callback) {
20278 if (value && value.length > 2) {
20279 if (suggestions[k] && suggestions[k][v]) {
20280 for (var sugg in suggestions[k][v]) {
20281 var dist = iD.util.editDistance(value, sugg.substring(0, value.length));
20291 result.sort(function(a, b) {
20292 return a.dist - b.dist;
20295 result = result.slice(0,3);
20301 iD.geo.roundCoords = function(c) {
20302 return [Math.floor(c[0]), Math.floor(c[1])];
20305 iD.geo.interp = function(p1, p2, t) {
20306 return [p1[0] + (p2[0] - p1[0]) * t,
20307 p1[1] + (p2[1] - p1[1]) * t];
20310 // 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product.
20311 // Returns a positive value, if OAB makes a counter-clockwise turn,
20312 // negative for clockwise turn, and zero if the points are collinear.
20313 iD.geo.cross = function(o, a, b) {
20314 return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
20317 // http://jsperf.com/id-dist-optimization
20318 iD.geo.euclideanDistance = function(a, b) {
20319 var x = a[0] - b[0], y = a[1] - b[1];
20320 return Math.sqrt((x * x) + (y * y));
20323 // using WGS84 polar radius (6356752.314245179 m)
20324 // const = 2 * PI * r / 360
20325 iD.geo.latToMeters = function(dLat) {
20326 return dLat * 110946.257617;
20329 // using WGS84 equatorial radius (6378137.0 m)
20330 // const = 2 * PI * r / 360
20331 iD.geo.lonToMeters = function(dLon, atLat) {
20332 return Math.abs(atLat) >= 90 ? 0 :
20333 dLon * 111319.490793 * Math.abs(Math.cos(atLat * (Math.PI/180)));
20336 // using WGS84 polar radius (6356752.314245179 m)
20337 // const = 2 * PI * r / 360
20338 iD.geo.metersToLat = function(m) {
20339 return m / 110946.257617;
20342 // using WGS84 equatorial radius (6378137.0 m)
20343 // const = 2 * PI * r / 360
20344 iD.geo.metersToLon = function(m, atLat) {
20345 return Math.abs(atLat) >= 90 ? 0 :
20346 m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180)));
20349 iD.geo.offsetToMeters = function(offset) {
20350 var equatRadius = 6356752.314245179,
20351 polarRadius = 6378137.0,
20355 offset[0] * 2 * Math.PI * equatRadius / tileSize,
20356 -offset[1] * 2 * Math.PI * polarRadius / tileSize
20360 iD.geo.metersToOffset = function(meters) {
20361 var equatRadius = 6356752.314245179,
20362 polarRadius = 6378137.0,
20366 meters[0] * tileSize / (2 * Math.PI * equatRadius),
20367 -meters[1] * tileSize / (2 * Math.PI * polarRadius)
20371 // Equirectangular approximation of spherical distances on Earth
20372 iD.geo.sphericalDistance = function(a, b) {
20373 var x = iD.geo.lonToMeters(a[0] - b[0], (a[1] + b[1]) / 2),
20374 y = iD.geo.latToMeters(a[1] - b[1]);
20375 return Math.sqrt((x * x) + (y * y));
20378 iD.geo.edgeEqual = function(a, b) {
20379 return (a[0] === b[0] && a[1] === b[1]) ||
20380 (a[0] === b[1] && a[1] === b[0]);
20383 // Return the counterclockwise angle in the range (-pi, pi)
20384 // between the positive X axis and the line intersecting a and b.
20385 iD.geo.angle = function(a, b, projection) {
20386 a = projection(a.loc);
20387 b = projection(b.loc);
20388 return Math.atan2(b[1] - a[1], b[0] - a[0]);
20391 // Choose the edge with the minimal distance from `point` to its orthogonal
20392 // projection onto that edge, if such a projection exists, or the distance to
20393 // the closest vertex on that edge. Returns an object with the `index` of the
20394 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
20395 iD.geo.chooseEdge = function(nodes, point, projection) {
20396 var dist = iD.geo.euclideanDistance,
20397 points = nodes.map(function(n) { return projection(n.loc); }),
20401 function dot(p, q) {
20402 return p[0] * q[0] + p[1] * q[1];
20405 for (var i = 0; i < points.length - 1; i++) {
20407 s = [points[i + 1][0] - o[0],
20408 points[i + 1][1] - o[1]],
20409 v = [point[0] - o[0],
20411 proj = dot(v, s) / dot(s, s),
20416 } else if (proj > 1) {
20419 p = [o[0] + proj * s[0], o[1] + proj * s[1]];
20422 var d = dist(p, point);
20426 loc = projection.invert(p);
20437 // Return the intersection point of 2 line segments.
20438 // From https://github.com/pgkelley4/line-segments-intersect
20439 // This uses the vector cross product approach described below:
20440 // http://stackoverflow.com/a/565282/786339
20441 iD.geo.lineIntersection = function(a, b) {
20442 function subtractPoints(point1, point2) {
20443 return [point1[0] - point2[0], point1[1] - point2[1]];
20445 function crossProduct(point1, point2) {
20446 return point1[0] * point2[1] - point1[1] * point2[0];
20449 var p = [a[0][0], a[0][1]],
20450 p2 = [a[1][0], a[1][1]],
20451 q = [b[0][0], b[0][1]],
20452 q2 = [b[1][0], b[1][1]],
20453 r = subtractPoints(p2, p),
20454 s = subtractPoints(q2, q),
20455 uNumerator = crossProduct(subtractPoints(q, p), r),
20456 denominator = crossProduct(r, s);
20458 if (uNumerator && denominator) {
20459 var u = uNumerator / denominator,
20460 t = crossProduct(subtractPoints(q, p), s) / denominator;
20462 if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
20463 return iD.geo.interp(p, p2, t);
20470 iD.geo.pathIntersections = function(path1, path2) {
20471 var intersections = [];
20472 for (var i = 0; i < path1.length - 1; i++) {
20473 for (var j = 0; j < path2.length - 1; j++) {
20474 var a = [ path1[i], path1[i+1] ],
20475 b = [ path2[j], path2[j+1] ],
20476 hit = iD.geo.lineIntersection(a, b);
20477 if (hit) intersections.push(hit);
20480 return intersections;
20483 // Return whether point is contained in polygon.
20485 // `point` should be a 2-item array of coordinates.
20486 // `polygon` should be an array of 2-item arrays of coordinates.
20488 // From https://github.com/substack/point-in-polygon.
20489 // ray-casting algorithm based on
20490 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
20492 iD.geo.pointInPolygon = function(point, polygon) {
20497 for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
20498 var xi = polygon[i][0], yi = polygon[i][1];
20499 var xj = polygon[j][0], yj = polygon[j][1];
20501 var intersect = ((yi > y) !== (yj > y)) &&
20502 (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
20503 if (intersect) inside = !inside;
20509 iD.geo.polygonContainsPolygon = function(outer, inner) {
20510 return _.every(inner, function(point) {
20511 return iD.geo.pointInPolygon(point, outer);
20515 iD.geo.polygonIntersectsPolygon = function(outer, inner, checkSegments) {
20516 function testSegments(outer, inner) {
20517 for (var i = 0; i < outer.length - 1; i++) {
20518 for (var j = 0; j < inner.length - 1; j++) {
20519 var a = [ outer[i], outer[i+1] ],
20520 b = [ inner[j], inner[j+1] ];
20521 if (iD.geo.lineIntersection(a, b)) return true;
20527 function testPoints(outer, inner) {
20528 return _.some(inner, function(point) {
20529 return iD.geo.pointInPolygon(point, outer);
20533 return testPoints(outer, inner) || (!!checkSegments && testSegments(outer, inner));
20536 iD.geo.pathLength = function(path) {
20539 for (var i = 0; i < path.length - 1; i++) {
20540 dx = path[i][0] - path[i + 1][0];
20541 dy = path[i][1] - path[i + 1][1];
20542 length += Math.sqrt(dx * dx + dy * dy);
20546 iD.geo.Extent = function geoExtent(min, max) {
20547 if (!(this instanceof iD.geo.Extent)) return new iD.geo.Extent(min, max);
20548 if (min instanceof iD.geo.Extent) {
20550 } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
20554 this[0] = min || [ Infinity, Infinity];
20555 this[1] = max || min || [-Infinity, -Infinity];
20559 iD.geo.Extent.prototype = new Array(2);
20561 _.extend(iD.geo.Extent.prototype, {
20562 equals: function (obj) {
20563 return this[0][0] === obj[0][0] &&
20564 this[0][1] === obj[0][1] &&
20565 this[1][0] === obj[1][0] &&
20566 this[1][1] === obj[1][1];
20569 extend: function(obj) {
20570 if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20571 return iD.geo.Extent([Math.min(obj[0][0], this[0][0]),
20572 Math.min(obj[0][1], this[0][1])],
20573 [Math.max(obj[1][0], this[1][0]),
20574 Math.max(obj[1][1], this[1][1])]);
20577 _extend: function(extent) {
20578 this[0][0] = Math.min(extent[0][0], this[0][0]);
20579 this[0][1] = Math.min(extent[0][1], this[0][1]);
20580 this[1][0] = Math.max(extent[1][0], this[1][0]);
20581 this[1][1] = Math.max(extent[1][1], this[1][1]);
20585 return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
20588 center: function() {
20589 return [(this[0][0] + this[1][0]) / 2,
20590 (this[0][1] + this[1][1]) / 2];
20593 rectangle: function() {
20594 return [this[0][0], this[0][1], this[1][0], this[1][1]];
20597 polygon: function() {
20599 [this[0][0], this[0][1]],
20600 [this[0][0], this[1][1]],
20601 [this[1][0], this[1][1]],
20602 [this[1][0], this[0][1]],
20603 [this[0][0], this[0][1]]
20607 contains: function(obj) {
20608 if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20609 return obj[0][0] >= this[0][0] &&
20610 obj[0][1] >= this[0][1] &&
20611 obj[1][0] <= this[1][0] &&
20612 obj[1][1] <= this[1][1];
20615 intersects: function(obj) {
20616 if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20617 return obj[0][0] <= this[1][0] &&
20618 obj[0][1] <= this[1][1] &&
20619 obj[1][0] >= this[0][0] &&
20620 obj[1][1] >= this[0][1];
20623 intersection: function(obj) {
20624 if (!this.intersects(obj)) return new iD.geo.Extent();
20625 return new iD.geo.Extent([Math.max(obj[0][0], this[0][0]),
20626 Math.max(obj[0][1], this[0][1])],
20627 [Math.min(obj[1][0], this[1][0]),
20628 Math.min(obj[1][1], this[1][1])]);
20631 percentContainedIn: function(obj) {
20632 if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20633 var a1 = this.intersection(obj).area(),
20636 if (a1 === Infinity || a2 === Infinity || a1 === 0 || a2 === 0) {
20643 padByMeters: function(meters) {
20644 var dLat = iD.geo.metersToLat(meters),
20645 dLon = iD.geo.metersToLon(meters, this.center()[1]);
20646 return iD.geo.Extent(
20647 [this[0][0] - dLon, this[0][1] - dLat],
20648 [this[1][0] + dLon, this[1][1] + dLat]);
20651 toParam: function() {
20652 return this.rectangle().join(',');
20656 iD.geo.Turn = function(turn) {
20657 if (!(this instanceof iD.geo.Turn))
20658 return new iD.geo.Turn(turn);
20659 _.extend(this, turn);
20662 iD.geo.Intersection = function(graph, vertexId) {
20663 var vertex = graph.entity(vertexId),
20664 parentWays = graph.parentWays(vertex),
20668 function addHighway(way, adjacentNodeId) {
20669 if (highways[adjacentNodeId]) {
20670 coincident.push(adjacentNodeId);
20672 highways[adjacentNodeId] = way;
20676 // Pre-split ways that would need to be split in
20677 // order to add a restriction. The real split will
20678 // happen when the restriction is added.
20679 parentWays.forEach(function(way) {
20680 if (!way.tags.highway || way.isArea() || way.isDegenerate())
20683 var isFirst = (vertexId === way.first()),
20684 isLast = (vertexId === way.last()),
20685 isAffix = (isFirst || isLast),
20686 isClosingNode = (isFirst && isLast);
20688 if (isAffix && !isClosingNode) {
20689 var index = (isFirst ? 1 : way.nodes.length - 2);
20690 addHighway(way, way.nodes[index]);
20693 var splitIndex, wayA, wayB, indexA, indexB;
20694 if (isClosingNode) {
20695 splitIndex = Math.ceil(way.nodes.length / 2); // split at midpoint
20696 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
20697 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
20699 indexB = way.nodes.length - 2;
20701 splitIndex = _.indexOf(way.nodes, vertex.id, 1); // split at vertexid
20702 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
20703 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
20704 indexA = splitIndex - 1;
20705 indexB = splitIndex + 1;
20707 graph = graph.replace(wayA).replace(wayB);
20708 addHighway(wayA, way.nodes[indexA]);
20709 addHighway(wayB, way.nodes[indexB]);
20713 // remove any ways from this intersection that are coincident
20714 // (i.e. any adjacent node used by more than one intersecting way)
20715 coincident.forEach(function (n) {
20716 delete highways[n];
20720 var intersection = {
20721 highways: highways,
20722 ways: _.values(highways),
20726 intersection.adjacentNodeId = function(fromWayId) {
20727 return _.find(_.keys(highways), function(k) {
20728 return highways[k].id === fromWayId;
20732 intersection.turns = function(fromNodeId) {
20733 var start = highways[fromNodeId];
20737 if (start.first() === vertex.id && start.tags.oneway === 'yes')
20739 if (start.last() === vertex.id && start.tags.oneway === '-1')
20742 function withRestriction(turn) {
20743 graph.parentRelations(graph.entity(turn.from.way)).forEach(function(relation) {
20744 if (relation.tags.type !== 'restriction')
20747 var f = relation.memberByRole('from'),
20748 t = relation.memberByRole('to'),
20749 v = relation.memberByRole('via');
20751 if (f && f.id === turn.from.way &&
20752 v && v.id === turn.via.node &&
20753 t && t.id === turn.to.way) {
20754 turn.restriction = relation.id;
20755 } else if (/^only_/.test(relation.tags.restriction) &&
20756 f && f.id === turn.from.way &&
20757 v && v.id === turn.via.node &&
20758 t && t.id !== turn.to.way) {
20759 turn.restriction = relation.id;
20760 turn.indirect_restriction = true;
20764 return iD.geo.Turn(turn);
20769 way: start.id.split(/-(a|b)/)[0]
20771 via = { node: vertex.id },
20774 _.each(highways, function(end, adjacentNodeId) {
20779 if (end.first() !== vertex.id && end.tags.oneway !== 'yes') {
20780 turns.push(withRestriction({
20784 node: adjacentNodeId,
20785 way: end.id.split(/-(a|b)/)[0]
20791 if (end.last() !== vertex.id && end.tags.oneway !== '-1') {
20792 turns.push(withRestriction({
20796 node: adjacentNodeId,
20797 way: end.id.split(/-(a|b)/)[0]
20805 if (start.tags.oneway !== 'yes' && start.tags.oneway !== '-1') {
20806 turns.push(withRestriction({
20817 return intersection;
20821 iD.geo.inferRestriction = function(graph, from, via, to, projection) {
20822 var fromWay = graph.entity(from.way),
20823 fromNode = graph.entity(from.node),
20824 toWay = graph.entity(to.way),
20825 toNode = graph.entity(to.node),
20826 viaNode = graph.entity(via.node),
20827 fromOneWay = (fromWay.tags.oneway === 'yes' && fromWay.last() === via.node) ||
20828 (fromWay.tags.oneway === '-1' && fromWay.first() === via.node),
20829 toOneWay = (toWay.tags.oneway === 'yes' && toWay.first() === via.node) ||
20830 (toWay.tags.oneway === '-1' && toWay.last() === via.node),
20831 angle = iD.geo.angle(viaNode, fromNode, projection) -
20832 iD.geo.angle(viaNode, toNode, projection);
20834 angle = angle * 180 / Math.PI;
20839 if (fromNode === toNode)
20840 return 'no_u_turn';
20841 if ((angle < 23 || angle > 336) && fromOneWay && toOneWay)
20842 return 'no_u_turn';
20844 return 'no_right_turn';
20846 return 'no_left_turn';
20848 return 'no_straight_on';
20850 // For fixing up rendering of multipolygons with tags on the outer member.
20851 // https://github.com/openstreetmap/iD/issues/613
20852 iD.geo.isSimpleMultipolygonOuterMember = function(entity, graph) {
20853 if (entity.type !== 'way')
20856 var parents = graph.parentRelations(entity);
20857 if (parents.length !== 1)
20860 var parent = parents[0];
20861 if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
20864 var members = parent.members, member;
20865 for (var i = 0; i < members.length; i++) {
20866 member = members[i];
20867 if (member.id === entity.id && member.role && member.role !== 'outer')
20868 return false; // Not outer member
20869 if (member.id !== entity.id && (!member.role || member.role === 'outer'))
20870 return false; // Not a simple multipolygon
20876 iD.geo.simpleMultipolygonOuterMember = function(entity, graph) {
20877 if (entity.type !== 'way')
20880 var parents = graph.parentRelations(entity);
20881 if (parents.length !== 1)
20884 var parent = parents[0];
20885 if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
20888 var members = parent.members, member, outerMember;
20889 for (var i = 0; i < members.length; i++) {
20890 member = members[i];
20891 if (!member.role || member.role === 'outer') {
20893 return false; // Not a simple multipolygon
20894 outerMember = member;
20898 return outerMember && graph.hasEntity(outerMember.id);
20901 // Join `array` into sequences of connecting ways.
20903 // Segments which share identical start/end nodes will, as much as possible,
20904 // be connected with each other.
20906 // The return value is a nested array. Each constituent array contains elements
20907 // of `array` which have been determined to connect. Each consitituent array
20908 // also has a `nodes` property whose value is an ordered array of member nodes,
20909 // with appropriate order reversal and start/end coordinate de-duplication.
20911 // Members of `array` must have, at minimum, `type` and `id` properties.
20912 // Thus either an array of `iD.Way`s or a relation member array may be
20915 // If an member has a `tags` property, its tags will be reversed via
20916 // `iD.actions.Reverse` in the output.
20918 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
20919 // false) and non-way members are ignored.
20921 iD.geo.joinWays = function(array, graph) {
20922 var joined = [], member, current, nodes, first, last, i, how, what;
20924 array = array.filter(function(member) {
20925 return member.type === 'way' && graph.hasEntity(member.id);
20928 function resolve(member) {
20929 return graph.childNodes(graph.entity(member.id));
20932 function reverse(member) {
20933 return member.tags ? iD.actions.Reverse(member.id, {reverseOneway: true})(graph).entity(member.id) : member;
20936 while (array.length) {
20937 member = array.shift();
20938 current = [member];
20939 current.nodes = nodes = resolve(member).slice();
20940 joined.push(current);
20942 while (array.length && _.first(nodes) !== _.last(nodes)) {
20943 first = _.first(nodes);
20944 last = _.last(nodes);
20946 for (i = 0; i < array.length; i++) {
20948 what = resolve(member);
20950 if (last === _.first(what)) {
20952 what = what.slice(1);
20954 } else if (last === _.last(what)) {
20956 what = what.slice(0, -1).reverse();
20957 member = reverse(member);
20959 } else if (first === _.last(what)) {
20960 how = nodes.unshift;
20961 what = what.slice(0, -1);
20963 } else if (first === _.first(what)) {
20964 how = nodes.unshift;
20965 what = what.slice(1).reverse();
20966 member = reverse(member);
20974 break; // No more joinable ways.
20976 how.apply(current, [member]);
20977 how.apply(nodes, what);
20979 array.splice(i, 1);
20986 Bypasses features of D3's default projection stream pipeline that are unnecessary:
20987 * Antimeridian clipping
20988 * Spherical rotation
20991 iD.geo.RawMercator = function () {
20992 var project = d3.geo.mercator.raw,
20993 k = 512 / Math.PI, // scale
20994 x = 0, y = 0, // translate
20995 clipExtent = [[0, 0], [0, 0]];
20997 function projection(point) {
20998 point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
20999 return [point[0] * k + x, y - point[1] * k];
21002 projection.invert = function(point) {
21003 point = project.invert((point[0] - x) / k, (y - point[1]) / k);
21004 return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
21007 projection.scale = function(_) {
21008 if (!arguments.length) return k;
21013 projection.translate = function(_) {
21014 if (!arguments.length) return [x, y];
21020 projection.clipExtent = function(_) {
21021 if (!arguments.length) return clipExtent;
21026 projection.stream = d3.geo.transform({
21027 point: function(x, y) {
21028 x = projection([x, y]);
21029 this.stream.point(x[0], x[1]);
21036 iD.actions.AddEntity = function(way) {
21037 return function(graph) {
21038 return graph.replace(way);
21041 iD.actions.AddMember = function(relationId, member, memberIndex) {
21042 return function(graph) {
21043 var relation = graph.entity(relationId);
21045 if (isNaN(memberIndex) && member.type === 'way') {
21046 var members = relation.indexedMembers();
21047 members.push(member);
21049 var joined = iD.geo.joinWays(members, graph);
21050 for (var i = 0; i < joined.length; i++) {
21051 var segment = joined[i];
21052 for (var j = 0; j < segment.length && segment.length >= 2; j++) {
21053 if (segment[j] !== member)
21057 memberIndex = segment[j + 1].index;
21058 } else if (j === segment.length - 1) {
21059 memberIndex = segment[j - 1].index + 1;
21061 memberIndex = Math.min(segment[j - 1].index + 1, segment[j + 1].index + 1);
21067 return graph.replace(relation.addMember(member, memberIndex));
21070 iD.actions.AddMidpoint = function(midpoint, node) {
21071 return function(graph) {
21072 graph = graph.replace(node.move(midpoint.loc));
21074 var parents = _.intersection(
21075 graph.parentWays(graph.entity(midpoint.edge[0])),
21076 graph.parentWays(graph.entity(midpoint.edge[1])));
21078 parents.forEach(function(way) {
21079 for (var i = 0; i < way.nodes.length - 1; i++) {
21080 if (iD.geo.edgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
21081 graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
21083 // Add only one midpoint on doubled-back segments,
21084 // turning them into self-intersections.
21093 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
21094 iD.actions.AddVertex = function(wayId, nodeId, index) {
21095 return function(graph) {
21096 return graph.replace(graph.entity(wayId).addNode(nodeId, index));
21099 iD.actions.ChangeMember = function(relationId, member, memberIndex) {
21100 return function(graph) {
21101 return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
21104 iD.actions.ChangePreset = function(entityId, oldPreset, newPreset) {
21105 return function(graph) {
21106 var entity = graph.entity(entityId),
21107 geometry = entity.geometry(graph),
21108 tags = entity.tags;
21110 if (oldPreset) tags = oldPreset.removeTags(tags, geometry);
21111 if (newPreset) tags = newPreset.applyTags(tags, geometry);
21113 return graph.replace(entity.update({tags: tags}));
21116 iD.actions.ChangeTags = function(entityId, tags) {
21117 return function(graph) {
21118 var entity = graph.entity(entityId);
21119 return graph.replace(entity.update({tags: tags}));
21122 iD.actions.Circularize = function(wayId, projection, maxAngle) {
21123 maxAngle = (maxAngle || 20) * Math.PI / 180;
21125 var action = function(graph) {
21126 var way = graph.entity(wayId);
21128 if (!way.isConvex(graph)) {
21129 graph = action.makeConvex(graph);
21132 var nodes = _.uniq(graph.childNodes(way)),
21133 keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }),
21134 points = nodes.map(function(n) { return projection(n.loc); }),
21135 keyPoints = keyNodes.map(function(n) { return projection(n.loc); }),
21136 centroid = (points.length === 2) ? iD.geo.interp(points[0], points[1], 0.5) : d3.geom.polygon(points).centroid(),
21137 radius = d3.median(points, function(p) { return iD.geo.euclideanDistance(centroid, p); }),
21138 sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
21141 // we need atleast two key nodes for the algorithm to work
21142 if (!keyNodes.length) {
21143 keyNodes = [nodes[0]];
21144 keyPoints = [points[0]];
21147 if (keyNodes.length === 1) {
21148 var index = nodes.indexOf(keyNodes[0]),
21149 oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
21151 keyNodes.push(nodes[oppositeIndex]);
21152 keyPoints.push(points[oppositeIndex]);
21155 // key points and nodes are those connected to the ways,
21156 // they are projected onto the circle, inbetween nodes are moved
21157 // to constant intervals between key nodes, extra inbetween nodes are
21158 // added if necessary.
21159 for (var i = 0; i < keyPoints.length; i++) {
21160 var nextKeyNodeIndex = (i + 1) % keyNodes.length,
21161 startNode = keyNodes[i],
21162 endNode = keyNodes[nextKeyNodeIndex],
21163 startNodeIndex = nodes.indexOf(startNode),
21164 endNodeIndex = nodes.indexOf(endNode),
21165 numberNewPoints = -1,
21166 indexRange = endNodeIndex - startNodeIndex,
21167 distance, totalAngle, eachAngle, startAngle, endAngle,
21168 angle, loc, node, j,
21169 inBetweenNodes = [];
21171 if (indexRange < 0) {
21172 indexRange += nodes.length;
21175 // position this key node
21176 distance = iD.geo.euclideanDistance(centroid, keyPoints[i]);
21177 if (distance === 0) { distance = 1e-4; }
21179 centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
21180 centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
21181 graph = graph.replace(keyNodes[i].move(projection.invert(keyPoints[i])));
21183 // figure out the between delta angle we want to match to
21184 startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
21185 endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
21186 totalAngle = endAngle - startAngle;
21188 // detects looping around -pi/pi
21189 if (totalAngle * sign > 0) {
21190 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
21195 eachAngle = totalAngle / (indexRange + numberNewPoints);
21196 } while (Math.abs(eachAngle) > maxAngle);
21198 // move existing points
21199 for (j = 1; j < indexRange; j++) {
21200 angle = startAngle + j * eachAngle;
21201 loc = projection.invert([
21202 centroid[0] + Math.cos(angle)*radius,
21203 centroid[1] + Math.sin(angle)*radius]);
21205 node = nodes[(j + startNodeIndex) % nodes.length].move(loc);
21206 graph = graph.replace(node);
21209 // add new inbetween nodes if necessary
21210 for (j = 0; j < numberNewPoints; j++) {
21211 angle = startAngle + (indexRange + j) * eachAngle;
21212 loc = projection.invert([
21213 centroid[0] + Math.cos(angle) * radius,
21214 centroid[1] + Math.sin(angle) * radius]);
21216 node = iD.Node({loc: loc});
21217 graph = graph.replace(node);
21219 nodes.splice(endNodeIndex + j, 0, node);
21220 inBetweenNodes.push(node.id);
21223 // Check for other ways that share these keyNodes..
21224 // If keyNodes are adjacent in both ways,
21225 // we can add inBetween nodes to that shared way too..
21226 if (indexRange === 1 && inBetweenNodes.length) {
21227 var startIndex1 = way.nodes.lastIndexOf(startNode.id),
21228 endIndex1 = way.nodes.lastIndexOf(endNode.id),
21229 wayDirection1 = (endIndex1 - startIndex1);
21230 if (wayDirection1 < -1) { wayDirection1 = 1; }
21232 /* eslint-disable no-loop-func */
21233 _.each(_.without(graph.parentWays(keyNodes[i]), way), function(sharedWay) {
21234 if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
21235 var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id),
21236 endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id),
21237 wayDirection2 = (endIndex2 - startIndex2),
21238 insertAt = endIndex2;
21239 if (wayDirection2 < -1) { wayDirection2 = 1; }
21241 if (wayDirection1 !== wayDirection2) {
21242 inBetweenNodes.reverse();
21243 insertAt = startIndex2;
21245 for (j = 0; j < inBetweenNodes.length; j++) {
21246 sharedWay = sharedWay.addNode(inBetweenNodes[j], insertAt + j);
21248 graph = graph.replace(sharedWay);
21251 /* eslint-enable no-loop-func */
21256 // update the way to have all the new nodes
21257 ids = nodes.map(function(n) { return n.id; });
21260 way = way.update({nodes: ids});
21261 graph = graph.replace(way);
21266 action.makeConvex = function(graph) {
21267 var way = graph.entity(wayId),
21268 nodes = _.uniq(graph.childNodes(way)),
21269 points = nodes.map(function(n) { return projection(n.loc); }),
21270 sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
21271 hull = d3.geom.hull(points);
21273 // D3 convex hulls go counterclockwise..
21279 for (var i = 0; i < hull.length - 1; i++) {
21280 var startIndex = points.indexOf(hull[i]),
21281 endIndex = points.indexOf(hull[i+1]),
21282 indexRange = (endIndex - startIndex);
21284 if (indexRange < 0) {
21285 indexRange += nodes.length;
21288 // move interior nodes to the surface of the convex hull..
21289 for (var j = 1; j < indexRange; j++) {
21290 var point = iD.geo.interp(hull[i], hull[i+1], j / indexRange),
21291 node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
21292 graph = graph.replace(node);
21298 action.disabled = function(graph) {
21299 if (!graph.entity(wayId).isClosed())
21300 return 'not_closed';
21305 // Connect the ways at the given nodes.
21307 // The last node will survive. All other nodes will be replaced with
21308 // the surviving node in parent ways, and then removed.
21310 // Tags and relation memberships of of non-surviving nodes are merged
21311 // to the survivor.
21313 // This is the inverse of `iD.actions.Disconnect`.
21316 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
21317 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
21319 iD.actions.Connect = function(nodeIds) {
21320 return function(graph) {
21321 var survivor = graph.entity(_.last(nodeIds));
21323 for (var i = 0; i < nodeIds.length - 1; i++) {
21324 var node = graph.entity(nodeIds[i]);
21326 /* eslint-disable no-loop-func */
21327 graph.parentWays(node).forEach(function(parent) {
21328 if (!parent.areAdjacent(node.id, survivor.id)) {
21329 graph = graph.replace(parent.replaceNode(node.id, survivor.id));
21333 graph.parentRelations(node).forEach(function(parent) {
21334 graph = graph.replace(parent.replaceMember(node, survivor));
21336 /* eslint-enable no-loop-func */
21338 survivor = survivor.mergeTags(node.tags);
21339 graph = iD.actions.DeleteNode(node.id)(graph);
21342 graph = graph.replace(survivor);
21347 iD.actions.CopyEntities = function(ids, fromGraph) {
21350 var action = function(graph) {
21351 ids.forEach(function(id) {
21352 fromGraph.entity(id).copy(fromGraph, copies);
21355 for (var id in copies) {
21356 graph = graph.replace(copies[id]);
21362 action.copies = function() {
21368 iD.actions.DeleteMember = function(relationId, memberIndex) {
21369 return function(graph) {
21370 var relation = graph.entity(relationId)
21371 .removeMember(memberIndex);
21373 graph = graph.replace(relation);
21375 if (relation.isDegenerate())
21376 graph = iD.actions.DeleteRelation(relation.id)(graph);
21381 iD.actions.DeleteMultiple = function(ids) {
21383 way: iD.actions.DeleteWay,
21384 node: iD.actions.DeleteNode,
21385 relation: iD.actions.DeleteRelation
21388 var action = function(graph) {
21389 ids.forEach(function(id) {
21390 if (graph.hasEntity(id)) { // It may have been deleted aready.
21391 graph = actions[graph.entity(id).type](id)(graph);
21398 action.disabled = function(graph) {
21399 for (var i = 0; i < ids.length; i++) {
21401 disabled = actions[graph.entity(id).type](id).disabled(graph);
21402 if (disabled) return disabled;
21408 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
21409 iD.actions.DeleteNode = function(nodeId) {
21410 var action = function(graph) {
21411 var node = graph.entity(nodeId);
21413 graph.parentWays(node)
21414 .forEach(function(parent) {
21415 parent = parent.removeNode(nodeId);
21416 graph = graph.replace(parent);
21418 if (parent.isDegenerate()) {
21419 graph = iD.actions.DeleteWay(parent.id)(graph);
21423 graph.parentRelations(node)
21424 .forEach(function(parent) {
21425 parent = parent.removeMembersWithID(nodeId);
21426 graph = graph.replace(parent);
21428 if (parent.isDegenerate()) {
21429 graph = iD.actions.DeleteRelation(parent.id)(graph);
21433 return graph.remove(node);
21436 action.disabled = function() {
21442 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
21443 iD.actions.DeleteRelation = function(relationId) {
21444 function deleteEntity(entity, graph) {
21445 return !graph.parentWays(entity).length &&
21446 !graph.parentRelations(entity).length &&
21447 !entity.hasInterestingTags();
21450 var action = function(graph) {
21451 var relation = graph.entity(relationId);
21453 graph.parentRelations(relation)
21454 .forEach(function(parent) {
21455 parent = parent.removeMembersWithID(relationId);
21456 graph = graph.replace(parent);
21458 if (parent.isDegenerate()) {
21459 graph = iD.actions.DeleteRelation(parent.id)(graph);
21463 _.uniq(_.pluck(relation.members, 'id')).forEach(function(memberId) {
21464 graph = graph.replace(relation.removeMembersWithID(memberId));
21466 var entity = graph.entity(memberId);
21467 if (deleteEntity(entity, graph)) {
21468 graph = iD.actions.DeleteMultiple([memberId])(graph);
21472 return graph.remove(relation);
21475 action.disabled = function(graph) {
21476 if (!graph.entity(relationId).isComplete(graph))
21477 return 'incomplete_relation';
21482 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
21483 iD.actions.DeleteWay = function(wayId) {
21484 function deleteNode(node, graph) {
21485 return !graph.parentWays(node).length &&
21486 !graph.parentRelations(node).length &&
21487 !node.hasInterestingTags();
21490 var action = function(graph) {
21491 var way = graph.entity(wayId);
21493 graph.parentRelations(way)
21494 .forEach(function(parent) {
21495 parent = parent.removeMembersWithID(wayId);
21496 graph = graph.replace(parent);
21498 if (parent.isDegenerate()) {
21499 graph = iD.actions.DeleteRelation(parent.id)(graph);
21503 _.uniq(way.nodes).forEach(function(nodeId) {
21504 graph = graph.replace(way.removeNode(nodeId));
21506 var node = graph.entity(nodeId);
21507 if (deleteNode(node, graph)) {
21508 graph = graph.remove(node);
21512 return graph.remove(way);
21515 action.disabled = function(graph) {
21516 var disabled = false;
21518 graph.parentRelations(graph.entity(wayId)).forEach(function(parent) {
21519 var type = parent.tags.type,
21520 role = parent.memberById(wayId).role || 'outer';
21521 if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
21522 disabled = 'part_of_relation';
21531 iD.actions.DeprecateTags = function(entityId) {
21532 return function(graph) {
21533 var entity = graph.entity(entityId),
21534 newtags = _.clone(entity.tags),
21538 // This handles deprecated tags with a single condition
21539 for (var i = 0; i < iD.data.deprecated.length; i++) {
21541 rule = iD.data.deprecated[i];
21542 var match = _.pairs(rule.old)[0],
21543 replacements = rule.replace ? _.pairs(rule.replace) : null;
21545 if (entity.tags[match[0]] && match[1] === '*') {
21547 var value = entity.tags[match[0]];
21548 if (replacements && !newtags[replacements[0][0]]) {
21549 newtags[replacements[0][0]] = value;
21551 delete newtags[match[0]];
21554 } else if (entity.tags[match[0]] === match[1]) {
21555 newtags = _.assign({}, rule.replace || {}, _.omit(newtags, match[0]));
21561 return graph.replace(entity.update({tags: newtags}));
21567 iD.actions.DiscardTags = function(difference) {
21568 return function(graph) {
21569 function discardTags(entity) {
21570 if (!_.isEmpty(entity.tags)) {
21572 _.each(entity.tags, function(v, k) {
21573 if (v) tags[k] = v;
21576 graph = graph.replace(entity.update({
21577 tags: _.omit(tags, iD.data.discarded)
21582 difference.modified().forEach(discardTags);
21583 difference.created().forEach(discardTags);
21588 // Disconect the ways at the given node.
21590 // Optionally, disconnect only the given ways.
21592 // For testing convenience, accepts an ID to assign to the (first) new node.
21593 // Normally, this will be undefined and the way will automatically
21594 // be assigned a new ID.
21596 // This is the inverse of `iD.actions.Connect`.
21599 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
21600 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
21602 iD.actions.Disconnect = function(nodeId, newNodeId) {
21605 var action = function(graph) {
21606 var node = graph.entity(nodeId),
21607 connections = action.connections(graph);
21609 connections.forEach(function(connection) {
21610 var way = graph.entity(connection.wayID),
21611 newNode = iD.Node({id: newNodeId, loc: node.loc, tags: node.tags});
21613 graph = graph.replace(newNode);
21614 if (connection.index === 0 && way.isArea()) {
21615 // replace shared node with shared node..
21616 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
21618 // replace shared node with multiple new nodes..
21619 graph = graph.replace(way.updateNode(newNode.id, connection.index));
21626 action.connections = function(graph) {
21627 var candidates = [],
21629 parentWays = graph.parentWays(graph.entity(nodeId));
21631 parentWays.forEach(function(way) {
21632 if (wayIds && wayIds.indexOf(way.id) === -1) {
21636 if (way.isArea() && (way.nodes[0] === nodeId)) {
21637 candidates.push({wayID: way.id, index: 0});
21639 way.nodes.forEach(function(waynode, index) {
21640 if (waynode === nodeId) {
21641 candidates.push({wayID: way.id, index: index});
21647 return keeping ? candidates : candidates.slice(1);
21650 action.disabled = function(graph) {
21651 var connections = action.connections(graph);
21652 if (connections.length === 0 || (wayIds && wayIds.length !== connections.length))
21653 return 'not_connected';
21655 var parentWays = graph.parentWays(graph.entity(nodeId)),
21656 seenRelationIds = {},
21659 parentWays.forEach(function(way) {
21660 if (wayIds && wayIds.indexOf(way.id) === -1)
21663 var relations = graph.parentRelations(way);
21664 relations.forEach(function(relation) {
21665 if (relation.id in seenRelationIds) {
21666 sharedRelation = relation;
21668 seenRelationIds[relation.id] = true;
21673 if (sharedRelation)
21677 action.limitWays = function(_) {
21678 if (!arguments.length) return wayIds;
21685 // Join ways at the end node they share.
21687 // This is the inverse of `iD.actions.Split`.
21690 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
21691 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
21693 iD.actions.Join = function(ids) {
21695 function groupEntitiesByGeometry(graph) {
21696 var entities = ids.map(function(id) { return graph.entity(id); });
21697 return _.extend({line: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
21700 var action = function(graph) {
21701 var ways = ids.map(graph.entity, graph),
21702 survivor = ways[0];
21704 // Prefer to keep an existing way.
21705 for (var i = 0; i < ways.length; i++) {
21706 if (!ways[i].isNew()) {
21707 survivor = ways[i];
21712 var joined = iD.geo.joinWays(ways, graph)[0];
21714 survivor = survivor.update({nodes: _.pluck(joined.nodes, 'id')});
21715 graph = graph.replace(survivor);
21717 joined.forEach(function(way) {
21718 if (way.id === survivor.id)
21721 graph.parentRelations(way).forEach(function(parent) {
21722 graph = graph.replace(parent.replaceMember(way, survivor));
21725 survivor = survivor.mergeTags(way.tags);
21727 graph = graph.replace(survivor);
21728 graph = iD.actions.DeleteWay(way.id)(graph);
21734 action.disabled = function(graph) {
21735 var geometries = groupEntitiesByGeometry(graph);
21736 if (ids.length < 2 || ids.length !== geometries.line.length)
21737 return 'not_eligible';
21739 var joined = iD.geo.joinWays(ids.map(graph.entity, graph), graph);
21740 if (joined.length > 1)
21741 return 'not_adjacent';
21743 var nodeIds = _.pluck(joined[0].nodes, 'id').slice(1, -1),
21746 conflicting = false;
21748 joined[0].forEach(function(way) {
21749 var parents = graph.parentRelations(way);
21750 parents.forEach(function(parent) {
21751 if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; }))
21755 for (var k in way.tags) {
21756 if (!(k in tags)) {
21757 tags[k] = way.tags[k];
21758 } else if (tags[k] && iD.interestingTag(k) && tags[k] !== way.tags[k]) {
21759 conflicting = true;
21765 return 'restriction';
21768 return 'conflicting_tags';
21773 iD.actions.Merge = function(ids) {
21774 function groupEntitiesByGeometry(graph) {
21775 var entities = ids.map(function(id) { return graph.entity(id); });
21776 return _.extend({point: [], area: [], line: [], relation: []},
21777 _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
21780 var action = function(graph) {
21781 var geometries = groupEntitiesByGeometry(graph),
21782 target = geometries.area[0] || geometries.line[0],
21783 points = geometries.point;
21785 points.forEach(function(point) {
21786 target = target.mergeTags(point.tags);
21788 graph.parentRelations(point).forEach(function(parent) {
21789 graph = graph.replace(parent.replaceMember(point, target));
21792 graph = graph.remove(point);
21795 graph = graph.replace(target);
21800 action.disabled = function(graph) {
21801 var geometries = groupEntitiesByGeometry(graph);
21802 if (geometries.point.length === 0 ||
21803 (geometries.area.length + geometries.line.length) !== 1 ||
21804 geometries.relation.length !== 0)
21805 return 'not_eligible';
21810 iD.actions.MergePolygon = function(ids, newRelationId) {
21812 function groupEntities(graph) {
21813 var entities = ids.map(function (id) { return graph.entity(id); });
21818 }, _.groupBy(entities, function(entity) {
21819 if (entity.type === 'way' && entity.isClosed()) {
21820 return 'closedWay';
21821 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
21822 return 'multipolygon';
21829 var action = function(graph) {
21830 var entities = groupEntities(graph);
21832 // An array representing all the polygons that are part of the multipolygon.
21834 // Each element is itself an array of objects with an id property, and has a
21835 // locs property which is an array of the locations forming the polygon.
21836 var polygons = entities.multipolygon.reduce(function(polygons, m) {
21837 return polygons.concat(iD.geo.joinWays(m.members, graph));
21838 }, []).concat(entities.closedWay.map(function(d) {
21839 var member = [{id: d.id}];
21840 member.nodes = graph.childNodes(d);
21844 // contained is an array of arrays of boolean values,
21845 // where contained[j][k] is true iff the jth way is
21846 // contained by the kth way.
21847 var contained = polygons.map(function(w, i) {
21848 return polygons.map(function(d, n) {
21849 if (i === n) return null;
21850 return iD.geo.polygonContainsPolygon(
21851 _.pluck(d.nodes, 'loc'),
21852 _.pluck(w.nodes, 'loc'));
21856 // Sort all polygons as either outer or inner ways
21860 while (polygons.length) {
21861 extractUncontained(polygons);
21862 polygons = polygons.filter(isContained);
21863 contained = contained.filter(isContained).map(filterContained);
21866 function isContained(d, i) {
21867 return _.any(contained[i]);
21870 function filterContained(d) {
21871 return d.filter(isContained);
21874 function extractUncontained(polygons) {
21875 polygons.forEach(function(d, i) {
21876 if (!isContained(d, i)) {
21877 d.forEach(function(member) {
21881 role: outer ? 'outer' : 'inner'
21889 // Move all tags to one relation
21890 var relation = entities.multipolygon[0] ||
21891 iD.Relation({ id: newRelationId, tags: { type: 'multipolygon' }});
21893 entities.multipolygon.slice(1).forEach(function(m) {
21894 relation = relation.mergeTags(m.tags);
21895 graph = graph.remove(m);
21898 entities.closedWay.forEach(function(way) {
21899 function isThisOuter(m) {
21900 return m.id === way.id && m.role !== 'inner';
21902 if (members.some(isThisOuter)) {
21903 relation = relation.mergeTags(way.tags);
21904 graph = graph.replace(way.update({ tags: {} }));
21908 return graph.replace(relation.update({
21910 tags: _.omit(relation.tags, 'area')
21914 action.disabled = function(graph) {
21915 var entities = groupEntities(graph);
21916 if (entities.other.length > 0 ||
21917 entities.closedWay.length + entities.multipolygon.length < 2)
21918 return 'not_eligible';
21919 if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); }))
21920 return 'incomplete_relation';
21925 iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser) {
21926 var option = 'safe', // 'safe', 'force_local', 'force_remote'
21930 return _.isFunction(formatUser) ? formatUser(d) : d;
21934 function mergeLocation(remote, target) {
21935 function pointEqual(a, b) {
21936 var epsilon = 1e-6;
21937 return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon);
21940 if (option === 'force_local' || pointEqual(target.loc, remote.loc)) {
21943 if (option === 'force_remote') {
21944 return target.update({loc: remote.loc});
21947 conflicts.push(t('merge_remote_changes.conflict.location', { user: user(remote.user) }));
21952 function mergeNodes(base, remote, target) {
21953 if (option === 'force_local' || _.isEqual(target.nodes, remote.nodes)) {
21956 if (option === 'force_remote') {
21957 return target.update({nodes: remote.nodes});
21960 var ccount = conflicts.length,
21961 o = base.nodes || [],
21962 a = target.nodes || [],
21963 b = remote.nodes || [],
21965 hunks = Diff3.diff3_merge(a, o, b, true);
21967 for (var i = 0; i < hunks.length; i++) {
21968 var hunk = hunks[i];
21970 nodes.push.apply(nodes, hunk.ok);
21972 // for all conflicts, we can assume c.a !== c.b
21973 // because `diff3_merge` called with `true` option to exclude false conflicts..
21974 var c = hunk.conflict;
21975 if (_.isEqual(c.o, c.a)) { // only changed remotely
21976 nodes.push.apply(nodes, c.b);
21977 } else if (_.isEqual(c.o, c.b)) { // only changed locally
21978 nodes.push.apply(nodes, c.a);
21979 } else { // changed both locally and remotely
21980 conflicts.push(t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) }));
21986 return (conflicts.length === ccount) ? target.update({nodes: nodes}) : target;
21990 function mergeChildren(targetWay, children, updates, graph) {
21991 function isUsed(node, targetWay) {
21992 var parentWays = _.pluck(graph.parentWays(node), 'id');
21993 return node.hasInterestingTags() ||
21994 _.without(parentWays, targetWay.id).length > 0 ||
21995 graph.parentRelations(node).length > 0;
21998 var ccount = conflicts.length;
22000 for (var i = 0; i < children.length; i++) {
22001 var id = children[i],
22002 node = graph.hasEntity(id);
22004 // remove unused childNodes..
22005 if (targetWay.nodes.indexOf(id) === -1) {
22006 if (node && !isUsed(node, targetWay)) {
22007 updates.removeIds.push(id);
22012 // restore used childNodes..
22013 var local = localGraph.hasEntity(id),
22014 remote = remoteGraph.hasEntity(id),
22017 if (option === 'force_remote' && remote && remote.visible) {
22018 updates.replacements.push(remote);
22020 } else if (option === 'force_local' && local) {
22021 target = iD.Entity(local);
22023 target = target.update({ version: remote.version });
22025 updates.replacements.push(target);
22027 } else if (option === 'safe' && local && remote && local.version !== remote.version) {
22028 target = iD.Entity(local, { version: remote.version });
22029 if (remote.visible) {
22030 target = mergeLocation(remote, target);
22032 conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
22035 if (conflicts.length !== ccount) break;
22036 updates.replacements.push(target);
22044 function updateChildren(updates, graph) {
22045 for (var i = 0; i < updates.replacements.length; i++) {
22046 graph = graph.replace(updates.replacements[i]);
22048 if (updates.removeIds.length) {
22049 graph = iD.actions.DeleteMultiple(updates.removeIds)(graph);
22055 function mergeMembers(remote, target) {
22056 if (option === 'force_local' || _.isEqual(target.members, remote.members)) {
22059 if (option === 'force_remote') {
22060 return target.update({members: remote.members});
22063 conflicts.push(t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) }));
22068 function mergeTags(base, remote, target) {
22069 function ignoreKey(k) {
22070 return _.contains(iD.data.discarded, k);
22073 if (option === 'force_local' || _.isEqual(target.tags, remote.tags)) {
22076 if (option === 'force_remote') {
22077 return target.update({tags: remote.tags});
22080 var ccount = conflicts.length,
22081 o = base.tags || {},
22082 a = target.tags || {},
22083 b = remote.tags || {},
22084 keys = _.reject(_.union(_.keys(o), _.keys(a), _.keys(b)), ignoreKey),
22088 for (var i = 0; i < keys.length; i++) {
22091 if (o[k] !== b[k] && a[k] !== b[k]) { // changed remotely..
22092 if (o[k] !== a[k]) { // changed locally..
22093 conflicts.push(t('merge_remote_changes.conflict.tags',
22094 { tag: k, local: a[k], remote: b[k], user: user(remote.user) }));
22096 } else { // unchanged locally, accept remote change..
22097 if (b.hasOwnProperty(k)) {
22107 return (changed && conflicts.length === ccount) ? target.update({tags: tags}) : target;
22111 // `graph.base()` is the common ancestor of the two graphs.
22112 // `localGraph` contains user's edits up to saving
22113 // `remoteGraph` contains remote edits to modified nodes
22114 // `graph` must be a descendent of `localGraph` and may include
22115 // some conflict resolution actions performed on it.
22117 // --- ... --- `localGraph` -- ... -- `graph`
22119 // `graph.base()` --- ... --- `remoteGraph`
22121 var action = function(graph) {
22122 var updates = { replacements: [], removeIds: [] },
22123 base = graph.base().entities[id],
22124 local = localGraph.entity(id),
22125 remote = remoteGraph.entity(id),
22126 target = iD.Entity(local, { version: remote.version });
22129 if (!remote.visible) {
22130 if (option === 'force_remote') {
22131 return iD.actions.DeleteMultiple([id])(graph);
22133 } else if (option === 'force_local') {
22134 if (target.type === 'way') {
22135 target = mergeChildren(target, _.uniq(local.nodes), updates, graph);
22136 graph = updateChildren(updates, graph);
22138 return graph.replace(target);
22141 conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
22142 return graph; // do nothing
22147 if (target.type === 'node') {
22148 target = mergeLocation(remote, target);
22150 } else if (target.type === 'way') {
22151 // pull in any child nodes that may not be present locally..
22152 graph.rebase(remoteGraph.childNodes(remote), [graph], false);
22153 target = mergeNodes(base, remote, target);
22154 target = mergeChildren(target, _.union(local.nodes, remote.nodes), updates, graph);
22156 } else if (target.type === 'relation') {
22157 target = mergeMembers(remote, target);
22160 target = mergeTags(base, remote, target);
22162 if (!conflicts.length) {
22163 graph = updateChildren(updates, graph).replace(target);
22169 action.withOption = function(opt) {
22174 action.conflicts = function() {
22180 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
22181 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
22182 iD.actions.Move = function(moveIds, tryDelta, projection, cache) {
22183 var delta = tryDelta;
22185 function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; }
22186 function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
22188 function setupCache(graph) {
22189 function canMove(nodeId) {
22190 var parents = _.pluck(graph.parentWays(graph.entity(nodeId)), 'id');
22191 if (parents.length < 3) return true;
22193 // Don't move a vertex where >2 ways meet, unless all parentWays are moving too..
22194 var parentsMoving = _.all(parents, function(id) { return cache.moving[id]; });
22195 if (!parentsMoving) delete cache.moving[nodeId];
22197 return parentsMoving;
22200 function cacheEntities(ids) {
22201 _.each(ids, function(id) {
22202 if (cache.moving[id]) return;
22203 cache.moving[id] = true;
22205 var entity = graph.hasEntity(id);
22206 if (!entity) return;
22208 if (entity.type === 'node') {
22209 cache.nodes.push(id);
22210 cache.startLoc[id] = entity.loc;
22211 } else if (entity.type === 'way') {
22212 cache.ways.push(id);
22213 cacheEntities(entity.nodes);
22215 cacheEntities(_.pluck(entity.members, 'id'));
22220 function cacheIntersections(ids) {
22221 function isEndpoint(way, id) { return !way.isClosed() && !!way.affix(id); }
22223 _.each(ids, function(id) {
22224 // consider only intersections with 1 moved and 1 unmoved way.
22225 _.each(graph.childNodes(graph.entity(id)), function(node) {
22226 var parents = graph.parentWays(node);
22227 if (parents.length !== 2) return;
22229 var moved = graph.entity(id),
22230 unmoved = _.find(parents, function(way) { return !cache.moving[way.id]; });
22231 if (!unmoved) return;
22233 // exclude ways that are overly connected..
22234 if (_.intersection(moved.nodes, unmoved.nodes).length > 2) return;
22236 if (moved.isArea() || unmoved.isArea()) return;
22238 cache.intersection[node.id] = {
22241 unmovedId: unmoved.id,
22242 movedIsEP: isEndpoint(moved, node.id),
22243 unmovedIsEP: isEndpoint(unmoved, node.id)
22255 cache.intersection = {};
22256 cache.replacedVertex = {};
22257 cache.startLoc = {};
22261 cacheEntities(moveIds);
22262 cacheIntersections(cache.ways);
22263 cache.nodes = _.filter(cache.nodes, canMove);
22270 // Place a vertex where the moved vertex used to be, to preserve way shape..
22271 function replaceMovedVertex(nodeId, wayId, graph, delta) {
22272 var way = graph.entity(wayId),
22273 moved = graph.entity(nodeId),
22274 movedIndex = way.nodes.indexOf(nodeId),
22275 len, prevIndex, nextIndex;
22277 if (way.isClosed()) {
22278 len = way.nodes.length - 1;
22279 prevIndex = (movedIndex + len - 1) % len;
22280 nextIndex = (movedIndex + len + 1) % len;
22282 len = way.nodes.length;
22283 prevIndex = movedIndex - 1;
22284 nextIndex = movedIndex + 1;
22287 var prev = graph.hasEntity(way.nodes[prevIndex]),
22288 next = graph.hasEntity(way.nodes[nextIndex]);
22290 // Don't add orig vertex at endpoint..
22291 if (!prev || !next) return graph;
22293 var key = wayId + '_' + nodeId,
22294 orig = cache.replacedVertex[key];
22297 cache.replacedVertex[key] = orig;
22298 cache.startLoc[orig.id] = cache.startLoc[nodeId];
22303 start = projection(cache.startLoc[nodeId]);
22304 end = projection.invert(vecAdd(start, delta));
22306 end = cache.startLoc[nodeId];
22308 orig = orig.move(end);
22310 var angle = Math.abs(iD.geo.angle(orig, prev, projection) -
22311 iD.geo.angle(orig, next, projection)) * 180 / Math.PI;
22313 // Don't add orig vertex if it would just make a straight line..
22314 if (angle > 175 && angle < 185) return graph;
22316 // Don't add orig vertex if another point is already nearby (within 10m)
22317 if (iD.geo.sphericalDistance(prev.loc, orig.loc) < 10 ||
22318 iD.geo.sphericalDistance(orig.loc, next.loc) < 10) return graph;
22320 // moving forward or backward along way?
22321 var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection),
22322 p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection),
22323 d1 = iD.geo.pathLength(p1),
22324 d2 = iD.geo.pathLength(p2),
22325 insertAt = (d1 < d2) ? movedIndex : nextIndex;
22327 // moving around closed loop?
22328 if (way.isClosed() && insertAt === 0) insertAt = len;
22330 way = way.addNode(orig.id, insertAt);
22331 return graph.replace(orig).replace(way);
22334 // Reorder nodes around intersections that have moved..
22335 function unZorroIntersection(intersection, graph) {
22336 var vertex = graph.entity(intersection.nodeId),
22337 way1 = graph.entity(intersection.movedId),
22338 way2 = graph.entity(intersection.unmovedId),
22339 isEP1 = intersection.movedIsEP,
22340 isEP2 = intersection.unmovedIsEP;
22342 // don't move the vertex if it is the endpoint of both ways.
22343 if (isEP1 && isEP2) return graph;
22345 var nodes1 = _.without(graph.childNodes(way1), vertex),
22346 nodes2 = _.without(graph.childNodes(way2), vertex);
22348 if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
22349 if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
22351 var edge1 = !isEP1 && iD.geo.chooseEdge(nodes1, projection(vertex.loc), projection),
22352 edge2 = !isEP2 && iD.geo.chooseEdge(nodes2, projection(vertex.loc), projection),
22355 // snap vertex to nearest edge (or some point between them)..
22356 if (!isEP1 && !isEP2) {
22357 var epsilon = 1e-4, maxIter = 10;
22358 for (var i = 0; i < maxIter; i++) {
22359 loc = iD.geo.interp(edge1.loc, edge2.loc, 0.5);
22360 edge1 = iD.geo.chooseEdge(nodes1, projection(loc), projection);
22361 edge2 = iD.geo.chooseEdge(nodes2, projection(loc), projection);
22362 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
22364 } else if (!isEP1) {
22370 graph = graph.replace(vertex.move(loc));
22372 // if zorro happened, reorder nodes..
22373 if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
22374 way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
22375 graph = graph.replace(way1);
22377 if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
22378 way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
22379 graph = graph.replace(way2);
22386 function cleanupIntersections(graph) {
22387 _.each(cache.intersection, function(obj) {
22388 graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, delta);
22389 graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
22390 graph = unZorroIntersection(obj, graph);
22396 // check if moving way endpoint can cross an unmoved way, if so limit delta..
22397 function limitDelta(graph) {
22398 _.each(cache.intersection, function(obj) {
22399 if (!obj.movedIsEP) return;
22401 var node = graph.entity(obj.nodeId),
22402 start = projection(node.loc),
22403 end = vecAdd(start, delta),
22404 movedNodes = graph.childNodes(graph.entity(obj.movedId)),
22405 movedPath = _.map(_.pluck(movedNodes, 'loc'),
22406 function(loc) { return vecAdd(projection(loc), delta); }),
22407 unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId)),
22408 unmovedPath = _.map(_.pluck(unmovedNodes, 'loc'), projection),
22409 hits = iD.geo.pathIntersections(movedPath, unmovedPath);
22411 for (var i = 0; i < hits.length; i++) {
22412 if (_.isEqual(hits[i], end)) continue;
22413 var edge = iD.geo.chooseEdge(unmovedNodes, end, projection);
22414 delta = vecSub(projection(edge.loc), start);
22420 var action = function(graph) {
22421 if (delta[0] === 0 && delta[1] === 0) return graph;
22425 if (!_.isEmpty(cache.intersection)) {
22429 _.each(cache.nodes, function(id) {
22430 var node = graph.entity(id),
22431 start = projection(node.loc),
22432 end = vecAdd(start, delta);
22433 graph = graph.replace(node.move(projection.invert(end)));
22436 if (!_.isEmpty(cache.intersection)) {
22437 graph = cleanupIntersections(graph);
22443 action.disabled = function(graph) {
22444 function incompleteRelation(id) {
22445 var entity = graph.entity(id);
22446 return entity.type === 'relation' && !entity.isComplete(graph);
22449 if (_.any(moveIds, incompleteRelation))
22450 return 'incomplete_relation';
22453 action.delta = function() {
22459 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
22460 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
22461 iD.actions.MoveNode = function(nodeId, loc) {
22462 return function(graph) {
22463 return graph.replace(graph.entity(nodeId).move(loc));
22466 iD.actions.Noop = function() {
22467 return function(graph) {
22472 * Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
22475 iD.actions.Orthogonalize = function(wayId, projection) {
22476 var threshold = 12, // degrees within right or straight to alter
22477 lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180),
22478 upperThreshold = Math.cos(threshold * Math.PI / 180);
22480 var action = function(graph) {
22481 var way = graph.entity(wayId),
22482 nodes = graph.childNodes(way),
22483 points = _.uniq(nodes).map(function(n) { return projection(n.loc); }),
22484 corner = {i: 0, dotp: 1},
22486 i, j, score, motions;
22488 if (nodes.length === 4) {
22489 for (i = 0; i < 1000; i++) {
22490 motions = points.map(calcMotion);
22491 points[corner.i] = addPoints(points[corner.i],motions[corner.i]);
22492 score = corner.dotp;
22493 if (score < epsilon) {
22498 graph = graph.replace(graph.entity(nodes[corner.i].id)
22499 .move(projection.invert(points[corner.i])));
22502 originalPoints = _.clone(points);
22505 for (i = 0; i < 1000; i++) {
22506 motions = points.map(calcMotion);
22507 for (j = 0; j < motions.length; j++) {
22508 points[j] = addPoints(points[j],motions[j]);
22510 var newScore = squareness(points);
22511 if (newScore < score) {
22512 best = _.clone(points);
22515 if (score < epsilon) {
22522 for (i = 0; i < points.length; i++) {
22523 // only move the points that actually moved
22524 if (originalPoints[i][0] !== points[i][0] || originalPoints[i][1] !== points[i][1]) {
22525 graph = graph.replace(graph.entity(nodes[i].id)
22526 .move(projection.invert(points[i])));
22530 // remove empty nodes on straight sections
22531 for (i = 0; i < points.length; i++) {
22532 var node = nodes[i];
22534 if (graph.parentWays(node).length > 1 ||
22535 graph.parentRelations(node).length ||
22536 node.hasInterestingTags()) {
22541 var dotp = normalizedDotProduct(i, points);
22542 if (dotp < -1 + epsilon) {
22543 graph = iD.actions.DeleteNode(nodes[i].id)(graph);
22550 function calcMotion(b, i, array) {
22551 var a = array[(i - 1 + array.length) % array.length],
22552 c = array[(i + 1) % array.length],
22553 p = subtractPoints(a, b),
22554 q = subtractPoints(c, b),
22557 scale = 2 * Math.min(iD.geo.euclideanDistance(p, [0, 0]), iD.geo.euclideanDistance(q, [0, 0]));
22558 p = normalizePoint(p, 1.0);
22559 q = normalizePoint(q, 1.0);
22561 dotp = filterDotProduct(p[0] * q[0] + p[1] * q[1]);
22563 // nasty hack to deal with almost-straight segments (angle is closer to 180 than to 90/270).
22564 if (array.length > 3) {
22565 if (dotp < -0.707106781186547) {
22568 } else if (dotp && Math.abs(dotp) < corner.dotp) {
22570 corner.dotp = Math.abs(dotp);
22573 return normalizePoint(addPoints(p, q), 0.1 * dotp * scale);
22577 function squareness(points) {
22578 return points.reduce(function(sum, val, i, array) {
22579 var dotp = normalizedDotProduct(i, array);
22581 dotp = filterDotProduct(dotp);
22582 return sum + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
22586 function normalizedDotProduct(i, points) {
22587 var a = points[(i - 1 + points.length) % points.length],
22589 c = points[(i + 1) % points.length],
22590 p = subtractPoints(a, b),
22591 q = subtractPoints(c, b);
22593 p = normalizePoint(p, 1.0);
22594 q = normalizePoint(q, 1.0);
22596 return p[0] * q[0] + p[1] * q[1];
22599 function subtractPoints(a, b) {
22600 return [a[0] - b[0], a[1] - b[1]];
22603 function addPoints(a, b) {
22604 return [a[0] + b[0], a[1] + b[1]];
22607 function normalizePoint(point, scale) {
22608 var vector = [0, 0];
22609 var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]);
22610 if (length !== 0) {
22611 vector[0] = point[0] / length;
22612 vector[1] = point[1] / length;
22615 vector[0] *= scale;
22616 vector[1] *= scale;
22621 function filterDotProduct(dotp) {
22622 if (lowerThreshold > Math.abs(dotp) || Math.abs(dotp) > upperThreshold) {
22629 action.disabled = function(graph) {
22630 var way = graph.entity(wayId),
22631 nodes = graph.childNodes(way),
22632 points = _.uniq(nodes).map(function(n) { return projection(n.loc); });
22634 if (squareness(points)) {
22638 return 'not_squarish';
22643 // Create a restriction relation for `turn`, which must have the following structure:
22646 // from: { node: <node ID>, way: <way ID> },
22647 // via: { node: <node ID> },
22648 // to: { node: <node ID>, way: <way ID> },
22649 // restriction: <'no_right_turn', 'no_left_turn', etc.>
22652 // This specifies a restriction of type `restriction` when traveling from
22653 // `from.node` in `from.way` toward `to.node` in `to.way` via `via.node`.
22654 // (The action does not check that these entities form a valid intersection.)
22656 // If `restriction` is not provided, it is automatically determined by
22657 // iD.geo.inferRestriction.
22659 // If necessary, the `from` and `to` ways are split. In these cases, `from.node`
22660 // and `to.node` are used to determine which portion of the split ways become
22661 // members of the restriction.
22663 // For testing convenience, accepts an ID to assign to the new relation.
22664 // Normally, this will be undefined and the relation will automatically
22665 // be assigned a new ID.
22667 iD.actions.RestrictTurn = function(turn, projection, restrictionId) {
22668 return function(graph) {
22669 var from = graph.entity(turn.from.way),
22670 via = graph.entity(turn.via.node),
22671 to = graph.entity(turn.to.way);
22673 function isClosingNode(way, nodeId) {
22674 return nodeId === way.first() && nodeId === way.last();
22677 function split(toOrFrom) {
22678 var newID = toOrFrom.newID || iD.Way().id;
22679 graph = iD.actions.Split(via.id, [newID])
22680 .limitWays([toOrFrom.way])(graph);
22682 var a = graph.entity(newID),
22683 b = graph.entity(toOrFrom.way);
22685 if (a.nodes.indexOf(toOrFrom.node) !== -1) {
22692 if (!from.affix(via.id) || isClosingNode(from, via.id)) {
22693 if (turn.from.node === turn.to.node) {
22695 from = to = split(turn.from)[0];
22696 } else if (turn.from.way === turn.to.way) {
22697 // Straight-on or circular
22698 var s = split(turn.from);
22703 from = split(turn.from)[0];
22707 if (!to.affix(via.id) || isClosingNode(to, via.id)) {
22708 to = split(turn.to)[0];
22711 return graph.replace(iD.Relation({
22714 type: 'restriction',
22715 restriction: turn.restriction ||
22716 iD.geo.inferRestriction(
22724 {id: from.id, type: 'way', role: 'from'},
22725 {id: via.id, type: 'node', role: 'via'},
22726 {id: to.id, type: 'way', role: 'to'}
22732 Order the nodes of a way in reverse order and reverse any direction dependent tags
22733 other than `oneway`. (We assume that correcting a backwards oneway is the primary
22734 reason for reversing a way.)
22736 The following transforms are performed:
22739 *:right=* ⟺ *:left=*
22740 *:forward=* ⟺ *:backward=*
22741 direction=up ⟺ direction=down
22742 incline=up ⟺ incline=down
22746 role=forward ⟺ role=backward
22747 role=north ⟺ role=south
22748 role=east ⟺ role=west
22750 In addition, numeric-valued `incline` tags are negated.
22752 The JOSM implementation was used as a guide, but transformations that were of unclear benefit
22753 or adjusted tags that don't seem to be used in practice were omitted.
22756 http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
22757 http://wiki.openstreetmap.org/wiki/Key:direction#Steps
22758 http://wiki.openstreetmap.org/wiki/Key:incline
22759 http://wiki.openstreetmap.org/wiki/Route#Members
22760 http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
22762 iD.actions.Reverse = function(wayId, options) {
22763 var replacements = [
22764 [/:right$/, ':left'], [/:left$/, ':right'],
22765 [/:forward$/, ':backward'], [/:backward$/, ':forward']
22767 numeric = /^([+\-]?)(?=[\d.])/,
22769 forward: 'backward',
22770 backward: 'forward',
22777 function reverseKey(key) {
22778 for (var i = 0; i < replacements.length; ++i) {
22779 var replacement = replacements[i];
22780 if (replacement[0].test(key)) {
22781 return key.replace(replacement[0], replacement[1]);
22787 function reverseValue(key, value) {
22788 if (key === 'incline' && numeric.test(value)) {
22789 return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
22790 } else if (key === 'incline' || key === 'direction') {
22791 return {up: 'down', down: 'up'}[value] || value;
22792 } else if (options && options.reverseOneway && key === 'oneway') {
22793 return {yes: '-1', '1': '-1', '-1': 'yes'}[value] || value;
22795 return {left: 'right', right: 'left'}[value] || value;
22799 return function(graph) {
22800 var way = graph.entity(wayId),
22801 nodes = way.nodes.slice().reverse(),
22802 tags = {}, key, role;
22804 for (key in way.tags) {
22805 tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
22808 graph.parentRelations(way).forEach(function(relation) {
22809 relation.members.forEach(function(member, index) {
22810 if (member.id === way.id && (role = roleReversals[member.role])) {
22811 relation = relation.updateMember({role: role}, index);
22812 graph = graph.replace(relation);
22817 return graph.replace(way.update({nodes: nodes, tags: tags}));
22820 iD.actions.Revert = function(id) {
22822 var action = function(graph) {
22823 var entity = graph.hasEntity(id),
22824 base = graph.base().entities[id];
22826 if (entity && !base) { // entity will be removed..
22827 if (entity.type === 'node') {
22828 graph.parentWays(entity)
22829 .forEach(function(parent) {
22830 parent = parent.removeNode(id);
22831 graph = graph.replace(parent);
22833 if (parent.isDegenerate()) {
22834 graph = iD.actions.DeleteWay(parent.id)(graph);
22839 graph.parentRelations(entity)
22840 .forEach(function(parent) {
22841 parent = parent.removeMembersWithID(id);
22842 graph = graph.replace(parent);
22844 if (parent.isDegenerate()) {
22845 graph = iD.actions.DeleteRelation(parent.id)(graph);
22850 return graph.revert(id);
22855 iD.actions.RotateWay = function(wayId, pivot, angle, projection) {
22856 return function(graph) {
22857 return graph.update(function(graph) {
22858 var way = graph.entity(wayId);
22860 _.unique(way.nodes).forEach(function(id) {
22862 var node = graph.entity(id),
22863 point = projection(node.loc),
22866 radial[0] = point[0] - pivot[0];
22867 radial[1] = point[1] - pivot[1];
22870 radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + pivot[0],
22871 radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + pivot[1]
22874 graph = graph.replace(node.move(projection.invert(point)));
22881 // Split a way at the given node.
22883 // Optionally, split only the given ways, if multiple ways share
22886 // This is the inverse of `iD.actions.Join`.
22888 // For testing convenience, accepts an ID to assign to the new way.
22889 // Normally, this will be undefined and the way will automatically
22890 // be assigned a new ID.
22893 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
22895 iD.actions.Split = function(nodeId, newWayIds) {
22898 // if the way is closed, we need to search for a partner node
22899 // to split the way at.
22901 // The following looks for a node that is both far away from
22902 // the initial node in terms of way segment length and nearby
22903 // in terms of beeline-distance. This assures that areas get
22904 // split on the most "natural" points (independent of the number
22906 // For example: bone-shaped areas get split across their waist
22907 // line, circles across the diameter.
22908 function splitArea(nodes, idxA, graph) {
22909 var lengths = new Array(nodes.length),
22915 function wrap(index) {
22916 return iD.util.wrap(index, nodes.length);
22919 function dist(nA, nB) {
22920 return iD.geo.sphericalDistance(graph.entity(nA).loc, graph.entity(nB).loc);
22923 // calculate lengths
22925 for (i = wrap(idxA+1); i !== idxA; i = wrap(i+1)) {
22926 length += dist(nodes[i], nodes[wrap(i-1)]);
22927 lengths[i] = length;
22931 for (i = wrap(idxA-1); i !== idxA; i = wrap(i-1)) {
22932 length += dist(nodes[i], nodes[wrap(i+1)]);
22933 if (length < lengths[i])
22934 lengths[i] = length;
22937 // determine best opposite node to split
22938 for (i = 0; i < nodes.length; i++) {
22939 var cost = lengths[i] / dist(nodes[idxA], nodes[i]);
22949 function split(graph, wayA, newWayId) {
22950 var wayB = iD.Way({id: newWayId, tags: wayA.tags}),
22953 isArea = wayA.isArea(),
22954 isOuter = iD.geo.isSimpleMultipolygonOuterMember(wayA, graph);
22956 if (wayA.isClosed()) {
22957 var nodes = wayA.nodes.slice(0, -1),
22958 idxA = _.indexOf(nodes, nodeId),
22959 idxB = splitArea(nodes, idxA, graph);
22962 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
22963 nodesB = nodes.slice(idxB, idxA + 1);
22965 nodesA = nodes.slice(idxA, idxB + 1);
22966 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
22969 var idx = _.indexOf(wayA.nodes, nodeId, 1);
22970 nodesA = wayA.nodes.slice(0, idx + 1);
22971 nodesB = wayA.nodes.slice(idx);
22974 wayA = wayA.update({nodes: nodesA});
22975 wayB = wayB.update({nodes: nodesB});
22977 graph = graph.replace(wayA);
22978 graph = graph.replace(wayB);
22980 graph.parentRelations(wayA).forEach(function(relation) {
22981 if (relation.isRestriction()) {
22982 var via = relation.memberByRole('via');
22983 if (via && wayB.contains(via.id)) {
22984 relation = relation.updateMember({id: wayB.id}, relation.memberById(wayA.id).index);
22985 graph = graph.replace(relation);
22988 if (relation === isOuter) {
22989 graph = graph.replace(relation.mergeTags(wayA.tags));
22990 graph = graph.replace(wayA.update({tags: {}}));
22991 graph = graph.replace(wayB.update({tags: {}}));
22997 role: relation.memberById(wayA.id).role
23000 graph = iD.actions.AddMember(relation.id, member)(graph);
23004 if (!isOuter && isArea) {
23005 var multipolygon = iD.Relation({
23006 tags: _.extend({}, wayA.tags, {type: 'multipolygon'}),
23008 {id: wayA.id, role: 'outer', type: 'way'},
23009 {id: wayB.id, role: 'outer', type: 'way'}
23012 graph = graph.replace(multipolygon);
23013 graph = graph.replace(wayA.update({tags: {}}));
23014 graph = graph.replace(wayB.update({tags: {}}));
23020 var action = function(graph) {
23021 var candidates = action.ways(graph);
23022 for (var i = 0; i < candidates.length; i++) {
23023 graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
23028 action.ways = function(graph) {
23029 var node = graph.entity(nodeId),
23030 parents = graph.parentWays(node),
23031 hasLines = _.any(parents, function(parent) { return parent.geometry(graph) === 'line'; });
23033 return parents.filter(function(parent) {
23034 if (wayIds && wayIds.indexOf(parent.id) === -1)
23037 if (!wayIds && hasLines && parent.geometry(graph) !== 'line')
23040 if (parent.isClosed()) {
23044 for (var i = 1; i < parent.nodes.length - 1; i++) {
23045 if (parent.nodes[i] === nodeId) {
23054 action.disabled = function(graph) {
23055 var candidates = action.ways(graph);
23056 if (candidates.length === 0 || (wayIds && wayIds.length !== candidates.length))
23057 return 'not_eligible';
23060 action.limitWays = function(_) {
23061 if (!arguments.length) return wayIds;
23069 * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
23072 iD.actions.Straighten = function(wayId, projection) {
23073 function positionAlongWay(n, s, e) {
23074 return ((n[0] - s[0]) * (e[0] - s[0]) + (n[1] - s[1]) * (e[1] - s[1]))/
23075 (Math.pow(e[0] - s[0], 2) + Math.pow(e[1] - s[1], 2));
23078 var action = function(graph) {
23079 var way = graph.entity(wayId),
23080 nodes = graph.childNodes(way),
23081 points = nodes.map(function(n) { return projection(n.loc); }),
23082 startPoint = points[0],
23083 endPoint = points[points.length-1],
23087 for (i = 1; i < points.length-1; i++) {
23088 var node = nodes[i],
23091 if (graph.parentWays(node).length > 1 ||
23092 graph.parentRelations(node).length ||
23093 node.hasInterestingTags()) {
23095 var u = positionAlongWay(point, startPoint, endPoint),
23096 p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
23097 p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]);
23099 graph = graph.replace(graph.entity(node.id)
23100 .move(projection.invert([p0, p1])));
23103 if (toDelete.indexOf(node) === -1) {
23104 toDelete.push(node);
23109 for (i = 0; i < toDelete.length; i++) {
23110 graph = iD.actions.DeleteNode(toDelete[i].id)(graph);
23116 action.disabled = function(graph) {
23117 // check way isn't too bendy
23118 var way = graph.entity(wayId),
23119 nodes = graph.childNodes(way),
23120 points = nodes.map(function(n) { return projection(n.loc); }),
23121 startPoint = points[0],
23122 endPoint = points[points.length-1],
23123 threshold = 0.2 * Math.sqrt(Math.pow(startPoint[0] - endPoint[0], 2) + Math.pow(startPoint[1] - endPoint[1], 2)),
23126 if (threshold === 0) {
23127 return 'too_bendy';
23130 for (i = 1; i < points.length-1; i++) {
23131 var point = points[i],
23132 u = positionAlongWay(point, startPoint, endPoint),
23133 p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
23134 p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]),
23135 dist = Math.sqrt(Math.pow(p0 - point[0], 2) + Math.pow(p1 - point[1], 2));
23137 // to bendy if point is off by 20% of total start/end distance in projected space
23138 if (isNaN(dist) || dist > threshold) {
23139 return 'too_bendy';
23146 // Remove the effects of `turn.restriction` on `turn`, which must have the
23147 // following structure:
23150 // from: { node: <node ID>, way: <way ID> },
23151 // via: { node: <node ID> },
23152 // to: { node: <node ID>, way: <way ID> },
23153 // restriction: <relation ID>
23156 // In the simple case, `restriction` is a reference to a `no_*` restriction
23157 // on the turn itself. In this case, it is simply deleted.
23159 // The more complex case is where `restriction` references an `only_*`
23160 // restriction on a different turn in the same intersection. In that case,
23161 // that restriction is also deleted, but at the same time restrictions on
23162 // the turns other than the first two are created.
23164 iD.actions.UnrestrictTurn = function(turn) {
23165 return function(graph) {
23166 return iD.actions.DeleteRelation(turn.restriction)(graph);
23170 iD.behavior.AddWay = function(context) {
23171 var event = d3.dispatch('start', 'startFromWay', 'startFromNode'),
23172 draw = iD.behavior.Draw(context);
23174 var addWay = function(surface) {
23175 draw.on('click', event.start)
23176 .on('clickWay', event.startFromWay)
23177 .on('clickNode', event.startFromNode)
23178 .on('cancel', addWay.cancel)
23179 .on('finish', addWay.cancel);
23182 .dblclickEnable(false);
23184 surface.call(draw);
23187 addWay.off = function(surface) {
23188 surface.call(draw.off);
23191 addWay.cancel = function() {
23192 window.setTimeout(function() {
23193 context.map().dblclickEnable(true);
23196 context.enter(iD.modes.Browse(context));
23199 addWay.tail = function(text) {
23204 return d3.rebind(addWay, event, 'on');
23206 iD.behavior.Breathe = function() {
23207 var duration = 800,
23208 selector = '.selected.shadow, .selected .shadow',
23209 selected = d3.select(null),
23214 function reset(selection) {
23216 .style('stroke-opacity', null)
23217 .style('stroke-width', null)
23218 .style('fill-opacity', null)
23222 function setAnimationParams(transition, fromTo) {
23224 .style('stroke-opacity', function(d) { return params[d.id][fromTo].opacity; })
23225 .style('stroke-width', function(d) { return params[d.id][fromTo].width; })
23226 .style('fill-opacity', function(d) { return params[d.id][fromTo].opacity; })
23227 .style('r', function(d) { return params[d.id][fromTo].width; });
23230 function calcAnimationParams(selection) {
23233 .each(function(d) {
23234 var s = d3.select(this),
23235 tag = s.node().tagName,
23236 p = {'from': {}, 'to': {}},
23239 // determine base opacity and width
23240 if (tag === 'circle') {
23241 opacity = parseFloat(s.style('fill-opacity') || 0.5);
23242 width = parseFloat(s.style('r') || 15.5);
23244 opacity = parseFloat(s.style('stroke-opacity') || 0.7);
23245 width = parseFloat(s.style('stroke-width') || 10);
23248 // calculate from/to interpolation params..
23250 p.from.opacity = opacity * 0.6;
23251 p.to.opacity = opacity * 1.25;
23252 p.from.width = width * 0.9;
23253 p.to.width = width * (tag === 'circle' ? 1.5 : 1.25);
23258 function run(surface, fromTo) {
23259 var toFrom = (fromTo === 'from' ? 'to': 'from'),
23260 currSelected = surface.selectAll(selector),
23261 currClassed = surface.attr('class'),
23264 if (done || currSelected.empty()) {
23265 selected.call(reset);
23269 if (!_.isEqual(currSelected, selected) || currClassed !== classed) {
23270 selected.call(reset);
23271 classed = currClassed;
23272 selected = currSelected.call(calcAnimationParams);
23277 .call(setAnimationParams, fromTo)
23278 .duration(duration)
23279 .each(function() { ++n; })
23280 .each('end', function() {
23281 if (!--n) { // call once
23282 surface.call(run, toFrom);
23287 var breathe = function(surface) {
23289 d3.timer(function() {
23290 if (done) return true;
23292 var currSelected = surface.selectAll(selector);
23293 if (currSelected.empty()) return false;
23295 surface.call(run, 'from');
23300 breathe.off = function() {
23311 iD.behavior.Copy = function(context) {
23312 var keybinding = d3.keybinding('copy');
23314 function groupEntities(ids, graph) {
23315 var entities = ids.map(function (id) { return graph.entity(id); });
23316 return _.extend({relation: [], way: [], node: []},
23317 _.groupBy(entities, function(entity) { return entity.type; }));
23320 function getDescendants(id, graph, descendants) {
23321 var entity = graph.entity(id),
23324 descendants = descendants || {};
23326 if (entity.type === 'relation') {
23327 children = _.pluck(entity.members, 'id');
23328 } else if (entity.type === 'way') {
23329 children = entity.nodes;
23334 for (i = 0; i < children.length; i++) {
23335 if (!descendants[children[i]]) {
23336 descendants[children[i]] = true;
23337 descendants = getDescendants(children[i], graph, descendants);
23341 return descendants;
23344 function doCopy() {
23345 d3.event.preventDefault();
23346 if (context.inIntro()) return;
23348 var graph = context.graph(),
23349 selected = groupEntities(context.selectedIDs(), graph),
23354 for (i = 0; i < selected.relation.length; i++) {
23355 entity = selected.relation[i];
23356 if (!skip[entity.id] && entity.isComplete(graph)) {
23357 canCopy.push(entity.id);
23358 skip = getDescendants(entity.id, graph, skip);
23361 for (i = 0; i < selected.way.length; i++) {
23362 entity = selected.way[i];
23363 if (!skip[entity.id]) {
23364 canCopy.push(entity.id);
23365 skip = getDescendants(entity.id, graph, skip);
23368 for (i = 0; i < selected.node.length; i++) {
23369 entity = selected.node[i];
23370 if (!skip[entity.id]) {
23371 canCopy.push(entity.id);
23375 context.copyIDs(canCopy);
23379 keybinding.on(iD.ui.cmd('⌘C'), doCopy);
23380 d3.select(document).call(keybinding);
23384 copy.off = function() {
23385 d3.select(document).call(keybinding.off);
23391 `iD.behavior.drag` is like `d3.behavior.drag`, with the following differences:
23393 * The `origin` function is expected to return an [x, y] tuple rather than an
23395 * The events are `start`, `move`, and `end`.
23396 (https://github.com/mbostock/d3/issues/563)
23397 * The `start` event is not dispatched until the first cursor movement occurs.
23398 (https://github.com/mbostock/d3/pull/368)
23399 * The `move` event has a `point` and `delta` [x, y] tuple properties rather
23400 than `x`, `y`, `dx`, and `dy` properties.
23401 * The `end` event is not dispatched if no movement occurs.
23402 * An `off` function is available that unbinds the drag's internal event handlers.
23403 * Delegation is supported via the `delegate` function.
23406 iD.behavior.drag = function() {
23407 function d3_eventCancel() {
23408 d3.event.stopPropagation();
23409 d3.event.preventDefault();
23412 var event = d3.dispatch('start', 'move', 'end'),
23416 event_, target, surface;
23418 event.of = function(thiz, argumentz) {
23419 return function(e1) {
23420 var e0 = e1.sourceEvent = d3.event;
23424 event[e1.type].apply(thiz, argumentz);
23431 var d3_event_userSelectProperty = iD.util.prefixCSSProperty('UserSelect'),
23432 d3_event_userSelectSuppress = d3_event_userSelectProperty ?
23434 var selection = d3.selection(),
23435 select = selection.style(d3_event_userSelectProperty);
23436 selection.style(d3_event_userSelectProperty, 'none');
23437 return function () {
23438 selection.style(d3_event_userSelectProperty, select);
23442 var w = d3.select(window).on('selectstart.' + type, d3_eventCancel);
23443 return function () {
23444 w.on('selectstart.' + type, null);
23448 function mousedown() {
23450 event_ = event.of(target, arguments);
23451 var eventTarget = d3.event.target,
23452 touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
23456 selectEnable = d3_event_userSelectSuppress(touchId !== null ? 'drag-' + touchId : 'drag');
23458 var w = d3.select(window)
23459 .on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', dragmove)
23460 .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', dragend, true);
23463 offset = origin.apply(target, arguments);
23464 offset = [offset[0] - origin_[0], offset[1] - origin_[1]];
23469 if (touchId === null) d3.event.stopPropagation();
23472 var p = target.parentNode || surface;
23473 return touchId !== null ? d3.touches(p).filter(function(p) {
23474 return p.identifier === touchId;
23475 })[0] : d3.mouse(p);
23478 function dragmove() {
23481 dx = p[0] - origin_[0],
23482 dy = p[1] - origin_[1];
23484 if (dx === 0 && dy === 0)
23499 point: [p[0] + offset[0], p[1] + offset[1]],
23504 function dragend() {
23511 if (d3.event.target === eventTarget) w.on('click.drag', click, true);
23514 w.on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', null)
23515 .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', null);
23521 w.on('click.drag', null);
23525 function drag(selection) {
23526 var matchesSelector = iD.util.prefixDOMProperty('matchesSelector'),
23527 delegate = mousedown;
23530 delegate = function() {
23532 target = d3.event.target;
23533 for (; target && target !== root; target = target.parentNode) {
23534 if (target[matchesSelector](selector) &&
23535 (!filter || filter(target.__data__))) {
23536 return mousedown.call(target, target.__data__);
23542 selection.on('mousedown.drag' + selector, delegate)
23543 .on('touchstart.drag' + selector, delegate);
23546 drag.off = function(selection) {
23547 selection.on('mousedown.drag' + selector, null)
23548 .on('touchstart.drag' + selector, null);
23551 drag.delegate = function(_) {
23552 if (!arguments.length) return selector;
23557 drag.filter = function(_) {
23558 if (!arguments.length) return origin;
23563 drag.origin = function (_) {
23564 if (!arguments.length) return origin;
23569 drag.cancel = function() {
23571 .on('mousemove.drag', null)
23572 .on('mouseup.drag', null);
23576 drag.target = function() {
23577 if (!arguments.length) return target;
23578 target = arguments[0];
23579 event_ = event.of(target, Array.prototype.slice.call(arguments, 1));
23583 drag.surface = function() {
23584 if (!arguments.length) return surface;
23585 surface = arguments[0];
23589 return d3.rebind(drag, event, 'on');
23591 iD.behavior.Draw = function(context) {
23592 var event = d3.dispatch('move', 'click', 'clickWay',
23593 'clickNode', 'undo', 'cancel', 'finish'),
23594 keybinding = d3.keybinding('draw'),
23595 hover = iD.behavior.Hover(context)
23597 .on('hover', context.ui().sidebar.hover),
23598 tail = iD.behavior.Tail(),
23599 edit = iD.behavior.Edit(context),
23600 closeTolerance = 4,
23604 if (d3.event.altKey) return {};
23605 else return d3.event.target.__data__ || {};
23608 function mousedown() {
23611 var p = context.container().node();
23612 return touchId !== null ? d3.touches(p).filter(function(p) {
23613 return p.identifier === touchId;
23614 })[0] : d3.mouse(p);
23617 var element = d3.select(this),
23618 touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
23622 element.on('mousemove.draw', null);
23624 d3.select(window).on('mouseup.draw', function() {
23625 var t2 = +new Date(),
23627 dist = iD.geo.euclideanDistance(p1, p2);
23629 element.on('mousemove.draw', mousemove);
23630 d3.select(window).on('mouseup.draw', null);
23632 if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) {
23633 // Prevent a quick second click
23634 d3.select(window).on('click.draw-block', function() {
23635 d3.event.stopPropagation();
23638 context.map().dblclickEnable(false);
23640 window.setTimeout(function() {
23641 context.map().dblclickEnable(true);
23642 d3.select(window).on('click.draw-block', null);
23650 function mousemove() {
23651 event.move(datum());
23656 if (d.type === 'way') {
23657 var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection),
23658 edge = [d.nodes[choice.index - 1], d.nodes[choice.index]];
23659 event.clickWay(choice.loc, edge);
23661 } else if (d.type === 'node') {
23662 event.clickNode(d);
23665 event.click(context.map().mouseCoordinates());
23669 function backspace() {
23670 d3.event.preventDefault();
23675 d3.event.preventDefault();
23680 d3.event.preventDefault();
23684 function draw(selection) {
23685 context.install(hover);
23686 context.install(edit);
23688 if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
23689 context.install(tail);
23693 .on('⌫', backspace)
23699 .on('mousedown.draw', mousedown)
23700 .on('mousemove.draw', mousemove);
23702 d3.select(document)
23708 draw.off = function(selection) {
23709 context.ui().sidebar.hover.cancel();
23710 context.uninstall(hover);
23711 context.uninstall(edit);
23713 if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
23714 context.uninstall(tail);
23715 iD.behavior.Draw.usedTails[tail.text()] = true;
23719 .on('mousedown.draw', null)
23720 .on('mousemove.draw', null);
23723 .on('mouseup.draw', null);
23725 d3.select(document)
23726 .call(keybinding.off);
23729 draw.tail = function(_) {
23734 return d3.rebind(draw, event, 'on');
23737 iD.behavior.Draw.usedTails = {};
23738 iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
23739 var way = context.entity(wayId),
23740 isArea = context.geometry(wayId) === 'area',
23742 annotation = t((way.isDegenerate() ?
23743 'operations.start.annotation.' :
23744 'operations.continue.annotation.') + context.geometry(wayId)),
23745 draw = iD.behavior.Draw(context);
23747 var startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0,
23748 start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}),
23749 end = iD.Node({loc: context.map().mouseCoordinates()}),
23751 nodes: typeof index === 'undefined' ? [start.id, end.id] : [end.id, start.id],
23752 tags: _.clone(way.tags)
23755 var f = context[way.isDegenerate() ? 'replace' : 'perform'];
23757 f(iD.actions.AddEntity(end),
23758 iD.actions.AddVertex(wayId, end.id, index));
23760 f(iD.actions.AddEntity(start),
23761 iD.actions.AddEntity(end),
23762 iD.actions.AddEntity(segment));
23765 function move(datum) {
23768 if (datum.type === 'node' && datum.id !== end.id) {
23770 } else if (datum.type === 'way' && datum.id !== segment.id) {
23771 loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc;
23773 loc = context.map().mouseCoordinates();
23776 context.replace(iD.actions.MoveNode(end.id, loc));
23779 function undone() {
23781 context.enter(iD.modes.Browse(context));
23784 function setActiveElements() {
23785 var active = isArea ? [wayId, end.id] : [segment.id, start.id, end.id];
23786 context.surface().selectAll(iD.util.entitySelector(active))
23787 .classed('active', true);
23790 var drawWay = function(surface) {
23791 draw.on('move', move)
23792 .on('click', drawWay.add)
23793 .on('clickWay', drawWay.addWay)
23794 .on('clickNode', drawWay.addNode)
23795 .on('undo', context.undo)
23796 .on('cancel', drawWay.cancel)
23797 .on('finish', drawWay.finish);
23800 .dblclickEnable(false)
23801 .on('drawn.draw', setActiveElements);
23803 setActiveElements();
23805 surface.call(draw);
23808 .on('undone.draw', undone);
23811 drawWay.off = function(surface) {
23816 .on('drawn.draw', null);
23818 surface.call(draw.off)
23819 .selectAll('.active')
23820 .classed('active', false);
23823 .on('undone.draw', null);
23826 function ReplaceTemporaryNode(newNode) {
23827 return function(graph) {
23830 .replace(way.addNode(newNode.id, index))
23835 .replace(graph.entity(wayId).addNode(newNode.id, index))
23843 // Accept the current position of the temporary node and continue drawing.
23844 drawWay.add = function(loc) {
23846 // prevent duplicate nodes
23847 var last = context.hasEntity(way.nodes[way.nodes.length - (isArea ? 2 : 1)]);
23848 if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return;
23850 var newNode = iD.Node({loc: loc});
23853 iD.actions.AddEntity(newNode),
23854 ReplaceTemporaryNode(newNode),
23858 context.enter(mode);
23861 // Connect the way to an existing way.
23862 drawWay.addWay = function(loc, edge) {
23863 var previousEdge = startIndex ?
23864 [way.nodes[startIndex], way.nodes[startIndex - 1]] :
23865 [way.nodes[0], way.nodes[1]];
23867 // Avoid creating duplicate segments
23868 if (!isArea && iD.geo.edgeEqual(edge, previousEdge))
23871 var newNode = iD.Node({ loc: loc });
23874 iD.actions.AddMidpoint({ loc: loc, edge: edge}, newNode),
23875 ReplaceTemporaryNode(newNode),
23879 context.enter(mode);
23882 // Connect the way to an existing node and continue drawing.
23883 drawWay.addNode = function(node) {
23885 // Avoid creating duplicate segments
23886 if (way.areAdjacent(node.id, way.nodes[way.nodes.length - 1])) return;
23889 ReplaceTemporaryNode(node),
23893 context.enter(mode);
23896 // Finish the draw operation, removing the temporary node. If the way has enough
23897 // nodes to be valid, it's selected. Otherwise, return to browse mode.
23898 drawWay.finish = function() {
23902 window.setTimeout(function() {
23903 context.map().dblclickEnable(true);
23906 if (context.hasEntity(wayId)) {
23908 iD.modes.Select(context, [wayId])
23909 .suppressMenu(true)
23910 .newFeature(true));
23912 context.enter(iD.modes.Browse(context));
23916 // Cancel the draw operation and return to browse, deleting everything drawn.
23917 drawWay.cancel = function() {
23919 d3.functor(baseGraph),
23920 t('operations.cancel_draw.annotation'));
23922 window.setTimeout(function() {
23923 context.map().dblclickEnable(true);
23927 context.enter(iD.modes.Browse(context));
23930 drawWay.tail = function(text) {
23937 iD.behavior.Edit = function(context) {
23940 .minzoom(context.minEditableZoom());
23943 edit.off = function() {
23950 iD.behavior.Hash = function(context) {
23951 var s0 = null, // cached location.hash
23952 lat = 90 - 1e-8; // allowable latitude range
23954 var parser = function(map, s) {
23955 var q = iD.util.stringQs(s);
23956 var args = (q.map || '').split('/').map(Number);
23957 if (args.length < 3 || args.some(isNaN)) {
23958 return true; // replace bogus hash
23959 } else if (s !== formatter(map).slice(1)) {
23960 map.centerZoom([args[1],
23961 Math.min(lat, Math.max(-lat, args[2]))], args[0]);
23965 var formatter = function(map) {
23966 var mode = context.mode(),
23967 center = map.center(),
23969 precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
23970 q = _.omit(iD.util.stringQs(location.hash.substring(1)), 'comment'),
23973 if (mode && mode.id === 'browse') {
23976 var selected = context.selectedIDs().filter(function(id) {
23977 return !context.entity(id).isNew();
23979 if (selected.length) {
23980 newParams.id = selected.join(',');
23984 newParams.map = zoom.toFixed(2) +
23985 '/' + center[0].toFixed(precision) +
23986 '/' + center[1].toFixed(precision);
23988 return '#' + iD.util.qsString(_.assign(q, newParams), true);
23991 function update() {
23992 if (context.inIntro()) return;
23993 var s1 = formatter(context.map());
23994 if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map!
23997 var throttledUpdate = _.throttle(update, 500);
23999 function hashchange() {
24000 if (location.hash === s0) return; // ignore spurious hashchange events
24001 if (parser(context.map(), (s0 = location.hash).substring(1))) {
24002 update(); // replace bogus hash
24008 .on('move.hash', throttledUpdate);
24011 .on('enter.hash', throttledUpdate);
24014 .on('hashchange.hash', hashchange);
24016 if (location.hash) {
24017 var q = iD.util.stringQs(location.hash.substring(1));
24018 if (q.id) context.zoomToEntity(q.id.split(',')[0], !q.map);
24019 if (q.comment) context.storage('comment', q.comment);
24021 if (q.map) hash.hadHash = true;
24025 hash.off = function() {
24026 throttledUpdate.cancel();
24029 .on('move.hash', null);
24032 .on('enter.hash', null);
24035 .on('hashchange.hash', null);
24037 location.hash = '';
24043 The hover behavior adds the `.hover` class on mouseover to all elements to which
24044 the identical datum is bound, and removes it on mouseout.
24046 The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
24047 representation may consist of several elements scattered throughout the DOM hierarchy.
24048 Only one of these elements can have the :hover pseudo-class, but all of them will
24049 have the .hover class.
24051 iD.behavior.Hover = function() {
24052 var dispatch = d3.dispatch('hover'),
24057 function keydown() {
24058 if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
24059 dispatch.hover(null);
24060 selection.selectAll('.hover')
24061 .classed('hover-suppressed', true)
24062 .classed('hover', false);
24067 if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
24068 dispatch.hover(target ? target.id : null);
24069 selection.selectAll('.hover-suppressed')
24070 .classed('hover-suppressed', false)
24071 .classed('hover', true);
24075 var hover = function(__) {
24078 function enter(d) {
24079 if (d === target) return;
24083 selection.selectAll('.hover')
24084 .classed('hover', false);
24085 selection.selectAll('.hover-suppressed')
24086 .classed('hover-suppressed', false);
24088 if (target instanceof iD.Entity) {
24089 var selector = '.' + target.id;
24091 if (target.type === 'relation') {
24092 target.members.forEach(function(member) {
24093 selector += ', .' + member.id;
24097 var suppressed = altDisables && d3.event && d3.event.altKey;
24099 selection.selectAll(selector)
24100 .classed(suppressed ? 'hover-suppressed' : 'hover', true);
24102 dispatch.hover(target.id);
24104 dispatch.hover(null);
24110 function mouseover() {
24112 var target = d3.event.target;
24113 enter(target ? target.__data__ : null);
24116 function mouseout() {
24118 var target = d3.event.relatedTarget;
24119 enter(target ? target.__data__ : null);
24122 function mousedown() {
24125 .on('mouseup.hover', mouseup);
24128 function mouseup() {
24133 .on('mouseover.hover', mouseover)
24134 .on('mouseout.hover', mouseout)
24135 .on('mousedown.hover', mousedown)
24136 .on('mouseup.hover', mouseup);
24139 .on('keydown.hover', keydown)
24140 .on('keyup.hover', keyup);
24143 hover.off = function(selection) {
24144 selection.selectAll('.hover')
24145 .classed('hover', false);
24146 selection.selectAll('.hover-suppressed')
24147 .classed('hover-suppressed', false);
24150 .on('mouseover.hover', null)
24151 .on('mouseout.hover', null)
24152 .on('mousedown.hover', null)
24153 .on('mouseup.hover', null);
24156 .on('keydown.hover', null)
24157 .on('keyup.hover', null)
24158 .on('mouseup.hover', null);
24161 hover.altDisables = function(_) {
24162 if (!arguments.length) return altDisables;
24167 return d3.rebind(hover, dispatch, 'on');
24169 iD.behavior.Lasso = function(context) {
24171 var behavior = function(selection) {
24174 function mousedown() {
24175 var button = 0; // left
24176 if (d3.event.button === button && d3.event.shiftKey === true) {
24180 .on('mousemove.lasso', mousemove)
24181 .on('mouseup.lasso', mouseup);
24183 d3.event.stopPropagation();
24187 function mousemove() {
24189 lasso = iD.ui.Lasso(context);
24190 context.surface().call(lasso);
24193 lasso.p(context.mouse());
24196 function normalize(a, b) {
24198 [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
24199 [Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
24202 function lassoed() {
24203 if (!lasso) return [];
24205 var graph = context.graph(),
24206 bounds = lasso.extent().map(context.projection.invert),
24207 extent = iD.geo.Extent(normalize(bounds[0], bounds[1]));
24209 return _.pluck(context.intersects(extent).filter(function(entity) {
24210 return entity.type === 'node' &&
24211 iD.geo.pointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
24212 !context.features().isHidden(entity, graph, entity.geometry(graph));
24216 function mouseup() {
24218 .on('mousemove.lasso', null)
24219 .on('mouseup.lasso', null);
24221 if (!lasso) return;
24223 var ids = lassoed();
24227 context.enter(iD.modes.Select(context, ids));
24232 .on('mousedown.lasso', mousedown);
24235 behavior.off = function(selection) {
24236 selection.on('mousedown.lasso', null);
24241 iD.behavior.Paste = function(context) {
24242 var keybinding = d3.keybinding('paste');
24244 function omitTag(v, k) {
24252 k === 'description' ||
24253 k.indexOf('name') !== -1 ||
24254 k.indexOf('wiki') === 0 ||
24255 k.indexOf('addr:') === 0 ||
24256 k.indexOf('contact:') === 0
24260 function doPaste() {
24261 d3.event.preventDefault();
24262 if (context.inIntro()) return;
24264 var baseGraph = context.graph(),
24265 mouse = context.mouse(),
24266 projection = context.projection,
24267 viewport = iD.geo.Extent(projection.clipExtent()).polygon();
24269 if (!iD.geo.pointInPolygon(mouse, viewport)) return;
24271 var extent = iD.geo.Extent(),
24272 oldIDs = context.copyIDs(),
24273 oldGraph = context.copyGraph(),
24276 if (!oldIDs.length) return;
24278 var action = iD.actions.CopyEntities(oldIDs, oldGraph);
24279 context.perform(action);
24281 var copies = action.copies();
24282 for (var id in copies) {
24283 var oldEntity = oldGraph.entity(id),
24284 newEntity = copies[id];
24286 extent._extend(oldEntity.extent(oldGraph));
24287 newIDs.push(newEntity.id);
24288 context.perform(iD.actions.ChangeTags(newEntity.id, _.omit(newEntity.tags, omitTag)));
24291 // Put pasted objects where mouse pointer is..
24292 var center = projection(extent.center()),
24293 delta = [ mouse[0] - center[0], mouse[1] - center[1] ];
24295 context.perform(iD.actions.Move(newIDs, delta, projection));
24296 context.enter(iD.modes.Move(context, newIDs, baseGraph));
24300 keybinding.on(iD.ui.cmd('⌘V'), doPaste);
24301 d3.select(document).call(keybinding);
24305 paste.off = function() {
24306 d3.select(document).call(keybinding.off);
24311 iD.behavior.Select = function(context) {
24312 function keydown() {
24313 if (d3.event && d3.event.shiftKey) {
24315 .classed('behavior-multiselect', true);
24320 if (!d3.event || !d3.event.shiftKey) {
24322 .classed('behavior-multiselect', false);
24327 var datum = d3.event.target.__data__,
24328 lasso = d3.select('#surface .lasso').node(),
24329 mode = context.mode();
24331 if (!(datum instanceof iD.Entity)) {
24332 if (!d3.event.shiftKey && !lasso && mode.id !== 'browse')
24333 context.enter(iD.modes.Browse(context));
24335 } else if (!d3.event.shiftKey && !lasso) {
24336 // Avoid re-entering Select mode with same entity.
24337 if (context.selectedIDs().length !== 1 || context.selectedIDs()[0] !== datum.id) {
24338 context.enter(iD.modes.Select(context, [datum.id]));
24340 mode.suppressMenu(false).reselect();
24342 } else if (context.selectedIDs().indexOf(datum.id) >= 0) {
24343 var selectedIDs = _.without(context.selectedIDs(), datum.id);
24344 context.enter(selectedIDs.length ?
24345 iD.modes.Select(context, selectedIDs) :
24346 iD.modes.Browse(context));
24349 context.enter(iD.modes.Select(context, context.selectedIDs().concat([datum.id])));
24353 var behavior = function(selection) {
24355 .on('keydown.select', keydown)
24356 .on('keyup.select', keyup);
24358 selection.on('click.select', click);
24363 behavior.off = function(selection) {
24365 .on('keydown.select', null)
24366 .on('keyup.select', null);
24368 selection.on('click.select', null);
24375 iD.behavior.Tail = function() {
24379 tooltipSize = [0, 0],
24380 selectionSize = [0, 0];
24382 function tail(selection) {
24386 .on('resize.tail', function() { selectionSize = selection.dimensions(); });
24389 container.style('display', 'block');
24390 tooltipSize = container.dimensions();
24393 function mousemove() {
24394 if (container.style('display') === 'none') show();
24395 var xoffset = ((d3.event.clientX + tooltipSize[0] + xmargin) > selectionSize[0]) ?
24396 -tooltipSize[0] - xmargin : xmargin;
24397 container.classed('left', xoffset > 0);
24398 iD.util.setTransform(container, d3.event.clientX + xoffset, d3.event.clientY);
24401 function mouseleave() {
24402 if (d3.event.relatedTarget !== container.node()) {
24403 container.style('display', 'none');
24407 function mouseenter() {
24408 if (d3.event.relatedTarget !== container.node()) {
24413 container = d3.select(document.body)
24415 .style('display', 'none')
24416 .attr('class', 'tail tooltip-inner');
24418 container.append('div')
24422 .on('mousemove.tail', mousemove)
24423 .on('mouseenter.tail', mouseenter)
24424 .on('mouseleave.tail', mouseleave);
24427 .on('mousemove.tail', mousemove);
24429 tooltipSize = container.dimensions();
24430 selectionSize = selection.dimensions();
24433 tail.off = function(selection) {
24437 .on('mousemove.tail', null)
24441 .on('mousemove.tail', null)
24442 .on('mouseenter.tail', null)
24443 .on('mouseleave.tail', null);
24446 .on('resize.tail', null);
24449 tail.text = function(_) {
24450 if (!arguments.length) return text;
24458 iD.modes.AddArea = function(context) {
24462 title: t('modes.add_area.title'),
24463 description: t('modes.add_area.description'),
24467 var behavior = iD.behavior.AddWay(context)
24468 .tail(t('modes.add_area.tail'))
24469 .on('start', start)
24470 .on('startFromWay', startFromWay)
24471 .on('startFromNode', startFromNode),
24472 defaultTags = {area: 'yes'};
24474 function start(loc) {
24475 var graph = context.graph(),
24476 node = iD.Node({loc: loc}),
24477 way = iD.Way({tags: defaultTags});
24480 iD.actions.AddEntity(node),
24481 iD.actions.AddEntity(way),
24482 iD.actions.AddVertex(way.id, node.id),
24483 iD.actions.AddVertex(way.id, node.id));
24485 context.enter(iD.modes.DrawArea(context, way.id, graph));
24488 function startFromWay(loc, edge) {
24489 var graph = context.graph(),
24490 node = iD.Node({loc: loc}),
24491 way = iD.Way({tags: defaultTags});
24494 iD.actions.AddEntity(node),
24495 iD.actions.AddEntity(way),
24496 iD.actions.AddVertex(way.id, node.id),
24497 iD.actions.AddVertex(way.id, node.id),
24498 iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24500 context.enter(iD.modes.DrawArea(context, way.id, graph));
24503 function startFromNode(node) {
24504 var graph = context.graph(),
24505 way = iD.Way({tags: defaultTags});
24508 iD.actions.AddEntity(way),
24509 iD.actions.AddVertex(way.id, node.id),
24510 iD.actions.AddVertex(way.id, node.id));
24512 context.enter(iD.modes.DrawArea(context, way.id, graph));
24515 mode.enter = function() {
24516 context.install(behavior);
24519 mode.exit = function() {
24520 context.uninstall(behavior);
24525 iD.modes.AddLine = function(context) {
24529 title: t('modes.add_line.title'),
24530 description: t('modes.add_line.description'),
24534 var behavior = iD.behavior.AddWay(context)
24535 .tail(t('modes.add_line.tail'))
24536 .on('start', start)
24537 .on('startFromWay', startFromWay)
24538 .on('startFromNode', startFromNode);
24540 function start(loc) {
24541 var baseGraph = context.graph(),
24542 node = iD.Node({loc: loc}),
24546 iD.actions.AddEntity(node),
24547 iD.actions.AddEntity(way),
24548 iD.actions.AddVertex(way.id, node.id));
24550 context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24553 function startFromWay(loc, edge) {
24554 var baseGraph = context.graph(),
24555 node = iD.Node({loc: loc}),
24559 iD.actions.AddEntity(node),
24560 iD.actions.AddEntity(way),
24561 iD.actions.AddVertex(way.id, node.id),
24562 iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24564 context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24567 function startFromNode(node) {
24568 var baseGraph = context.graph(),
24572 iD.actions.AddEntity(way),
24573 iD.actions.AddVertex(way.id, node.id));
24575 context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24578 mode.enter = function() {
24579 context.install(behavior);
24582 mode.exit = function() {
24583 context.uninstall(behavior);
24588 iD.modes.AddPoint = function(context) {
24592 title: t('modes.add_point.title'),
24593 description: t('modes.add_point.description'),
24597 var behavior = iD.behavior.Draw(context)
24598 .tail(t('modes.add_point.tail'))
24600 .on('clickWay', addWay)
24601 .on('clickNode', addNode)
24602 .on('cancel', cancel)
24603 .on('finish', cancel);
24605 function add(loc) {
24606 var node = iD.Node({loc: loc});
24609 iD.actions.AddEntity(node),
24610 t('operations.add.annotation.point'));
24613 iD.modes.Select(context, [node.id])
24614 .suppressMenu(true)
24615 .newFeature(true));
24618 function addWay(loc) {
24622 function addNode(node) {
24626 function cancel() {
24627 context.enter(iD.modes.Browse(context));
24630 mode.enter = function() {
24631 context.install(behavior);
24634 mode.exit = function() {
24635 context.uninstall(behavior);
24640 iD.modes.Browse = function(context) {
24644 title: t('modes.browse.title'),
24645 description: t('modes.browse.description')
24649 iD.behavior.Paste(context),
24650 iD.behavior.Hover(context)
24651 .on('hover', context.ui().sidebar.hover),
24652 iD.behavior.Select(context),
24653 iD.behavior.Lasso(context),
24654 iD.modes.DragNode(context).behavior];
24656 mode.enter = function() {
24657 behaviors.forEach(function(behavior) {
24658 context.install(behavior);
24661 // Get focus on the body.
24662 if (document.activeElement && document.activeElement.blur) {
24663 document.activeElement.blur();
24667 context.ui().sidebar.show(sidebar);
24669 context.ui().sidebar.select(null);
24673 mode.exit = function() {
24674 context.ui().sidebar.hover.cancel();
24675 behaviors.forEach(function(behavior) {
24676 context.uninstall(behavior);
24680 context.ui().sidebar.hide();
24684 mode.sidebar = function(_) {
24685 if (!arguments.length) return sidebar;
24692 iD.modes.DragNode = function(context) {
24703 hover = iD.behavior.Hover(context)
24705 .on('hover', context.ui().sidebar.hover),
24706 edit = iD.behavior.Edit(context);
24708 function edge(point, size) {
24709 var pad = [30, 100, 30, 100];
24710 if (point[0] > size[0] - pad[0]) return [-10, 0];
24711 else if (point[0] < pad[2]) return [10, 0];
24712 else if (point[1] > size[1] - pad[1]) return [0, -10];
24713 else if (point[1] < pad[3]) return [0, 10];
24717 function startNudge(nudge) {
24718 if (nudgeInterval) window.clearInterval(nudgeInterval);
24719 nudgeInterval = window.setInterval(function() {
24720 context.pan(nudge);
24724 function stopNudge() {
24725 if (nudgeInterval) window.clearInterval(nudgeInterval);
24726 nudgeInterval = null;
24729 function moveAnnotation(entity) {
24730 return t('operations.move.annotation.' + entity.geometry(context.graph()));
24733 function connectAnnotation(entity) {
24734 return t('operations.connect.annotation.' + entity.geometry(context.graph()));
24737 function origin(entity) {
24738 return context.projection(entity.loc);
24741 function start(entity) {
24742 cancelled = d3.event.sourceEvent.shiftKey ||
24743 context.features().hasHiddenConnections(entity, context.graph());
24745 if (cancelled) return behavior.cancel();
24747 wasMidpoint = entity.type === 'midpoint';
24749 var midpoint = entity;
24750 entity = iD.Node();
24751 context.perform(iD.actions.AddMidpoint(midpoint, entity));
24753 var vertex = context.surface()
24754 .selectAll('.' + entity.id);
24755 behavior.target(vertex.node(), entity);
24759 iD.actions.Noop());
24762 activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
24763 activeIDs.push(entity.id);
24765 context.enter(mode);
24769 if (d3.event.sourceEvent.altKey) {
24773 return d3.event.sourceEvent.target.__data__ || {};
24776 // via https://gist.github.com/shawnbot/4166283
24777 function childOf(p, c) {
24778 if (p === c) return false;
24779 while (c && c !== p) c = c.parentNode;
24783 function move(entity) {
24784 if (cancelled) return;
24785 d3.event.sourceEvent.stopPropagation();
24787 var nudge = childOf(context.container().node(),
24788 d3.event.sourceEvent.toElement) &&
24789 edge(d3.event.point, context.map().dimensions());
24791 if (nudge) startNudge(nudge);
24794 var loc = context.projection.invert(d3.event.point);
24797 if (d.type === 'node' && d.id !== entity.id) {
24799 } else if (d.type === 'way' && !d3.select(d3.event.sourceEvent.target).classed('fill')) {
24800 loc = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection).loc;
24804 iD.actions.MoveNode(entity.id, loc),
24805 moveAnnotation(entity));
24808 function end(entity) {
24809 if (cancelled) return;
24813 if (d.type === 'way') {
24814 var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection);
24816 iD.actions.AddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity),
24817 connectAnnotation(d));
24819 } else if (d.type === 'node' && d.id !== entity.id) {
24821 iD.actions.Connect([d.id, entity.id]),
24822 connectAnnotation(d));
24824 } else if (wasMidpoint) {
24827 t('operations.add.annotation.vertex'));
24832 moveAnnotation(entity));
24835 var reselection = selectedIDs.filter(function(id) {
24836 return context.graph().hasEntity(id);
24839 if (reselection.length) {
24841 iD.modes.Select(context, reselection)
24842 .suppressMenu(true));
24844 context.enter(iD.modes.Browse(context));
24848 function cancel() {
24850 context.enter(iD.modes.Browse(context));
24853 function setActiveElements() {
24854 context.surface().selectAll(iD.util.entitySelector(activeIDs))
24855 .classed('active', true);
24858 var behavior = iD.behavior.drag()
24859 .delegate('g.node, g.point, g.midpoint')
24860 .surface(context.surface().node())
24862 .on('start', start)
24866 mode.enter = function() {
24867 context.install(hover);
24868 context.install(edit);
24871 .on('undone.drag-node', cancel);
24874 .on('drawn.drag-node', setActiveElements);
24876 setActiveElements();
24879 mode.exit = function() {
24880 context.ui().sidebar.hover.cancel();
24881 context.uninstall(hover);
24882 context.uninstall(edit);
24885 .on('undone.drag-node', null);
24888 .on('drawn.drag-node', null);
24891 .selectAll('.active')
24892 .classed('active', false);
24897 mode.selectedIDs = function(_) {
24898 if (!arguments.length) return selectedIDs;
24903 mode.behavior = behavior;
24907 iD.modes.DrawArea = function(context, wayId, baseGraph) {
24915 mode.enter = function() {
24916 var way = context.entity(wayId),
24917 headId = way.nodes[way.nodes.length - 2],
24918 tailId = way.first();
24920 behavior = iD.behavior.DrawWay(context, wayId, -1, mode, baseGraph)
24921 .tail(t('modes.draw_area.tail'));
24923 var addNode = behavior.addNode;
24925 behavior.addNode = function(node) {
24926 if (node.id === headId || node.id === tailId) {
24933 context.install(behavior);
24936 mode.exit = function() {
24937 context.uninstall(behavior);
24940 mode.selectedIDs = function() {
24946 iD.modes.DrawLine = function(context, wayId, baseGraph, affix) {
24954 mode.enter = function() {
24955 var way = context.entity(wayId),
24956 index = (affix === 'prefix') ? 0 : undefined,
24957 headId = (affix === 'prefix') ? way.first() : way.last();
24959 behavior = iD.behavior.DrawWay(context, wayId, index, mode, baseGraph)
24960 .tail(t('modes.draw_line.tail'));
24962 var addNode = behavior.addNode;
24964 behavior.addNode = function(node) {
24965 if (node.id === headId) {
24972 context.install(behavior);
24975 mode.exit = function() {
24976 context.uninstall(behavior);
24979 mode.selectedIDs = function() {
24985 iD.modes.Move = function(context, entityIDs, baseGraph) {
24991 var keybinding = d3.keybinding('move'),
24992 edit = iD.behavior.Edit(context),
24993 annotation = entityIDs.length === 1 ?
24994 t('operations.move.annotation.' + context.geometry(entityIDs[0])) :
24995 t('operations.move.annotation.multiple'),
25000 function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
25002 function edge(point, size) {
25003 var pad = [30, 100, 30, 100];
25004 if (point[0] > size[0] - pad[0]) return [-10, 0];
25005 else if (point[0] < pad[2]) return [10, 0];
25006 else if (point[1] > size[1] - pad[1]) return [0, -10];
25007 else if (point[1] < pad[3]) return [0, 10];
25011 function startNudge(nudge) {
25012 if (nudgeInterval) window.clearInterval(nudgeInterval);
25013 nudgeInterval = window.setInterval(function() {
25014 context.pan(nudge);
25016 var currMouse = context.mouse(),
25017 origMouse = context.projection(origin),
25018 delta = vecSub(vecSub(currMouse, origMouse), nudge),
25019 action = iD.actions.Move(entityIDs, delta, context.projection, cache);
25021 context.overwrite(action, annotation);
25026 function stopNudge() {
25027 if (nudgeInterval) window.clearInterval(nudgeInterval);
25028 nudgeInterval = null;
25032 var currMouse = context.mouse(),
25033 origMouse = context.projection(origin),
25034 delta = vecSub(currMouse, origMouse),
25035 action = iD.actions.Move(entityIDs, delta, context.projection, cache);
25037 context.overwrite(action, annotation);
25039 var nudge = edge(currMouse, context.map().dimensions());
25040 if (nudge) startNudge(nudge);
25044 function finish() {
25045 d3.event.stopPropagation();
25046 context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
25050 function cancel() {
25052 while (context.graph() !== baseGraph) context.pop();
25053 context.enter(iD.modes.Browse(context));
25056 context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
25061 function undone() {
25062 context.enter(iD.modes.Browse(context));
25065 mode.enter = function() {
25066 origin = context.map().mouseCoordinates();
25069 context.install(edit);
25076 .on('mousemove.move', move)
25077 .on('click.move', finish);
25080 .on('undone.move', undone);
25086 d3.select(document)
25090 mode.exit = function() {
25093 context.uninstall(edit);
25096 .on('mousemove.move', null)
25097 .on('click.move', null);
25100 .on('undone.move', null);
25107 iD.modes.RotateWay = function(context, wayId) {
25113 var keybinding = d3.keybinding('rotate-way'),
25114 edit = iD.behavior.Edit(context);
25116 mode.enter = function() {
25117 context.install(edit);
25119 var annotation = t('operations.rotate.annotation.' + context.geometry(wayId)),
25120 way = context.graph().entity(wayId),
25121 nodes = _.uniq(context.graph().childNodes(way)),
25122 points = nodes.map(function(n) { return context.projection(n.loc); }),
25123 pivot = d3.geom.polygon(points).centroid(),
25130 function rotate() {
25132 var mousePoint = context.mouse(),
25133 newAngle = Math.atan2(mousePoint[1] - pivot[1], mousePoint[0] - pivot[0]);
25135 if (typeof angle === 'undefined') angle = newAngle;
25138 iD.actions.RotateWay(wayId, pivot, newAngle - angle, context.projection),
25144 function finish() {
25145 d3.event.stopPropagation();
25146 context.enter(iD.modes.Select(context, [wayId])
25147 .suppressMenu(true));
25150 function cancel() {
25152 context.enter(iD.modes.Select(context, [wayId])
25153 .suppressMenu(true));
25156 function undone() {
25157 context.enter(iD.modes.Browse(context));
25161 .on('mousemove.rotate-way', rotate)
25162 .on('click.rotate-way', finish);
25165 .on('undone.rotate-way', undone);
25171 d3.select(document)
25175 mode.exit = function() {
25176 context.uninstall(edit);
25179 .on('mousemove.rotate-way', null)
25180 .on('click.rotate-way', null);
25183 .on('undone.rotate-way', null);
25190 iD.modes.Save = function(context) {
25191 var ui = iD.ui.Commit(context)
25192 .on('cancel', cancel)
25195 function cancel() {
25196 context.enter(iD.modes.Browse(context));
25199 function save(e, tryAgain) {
25200 function withChildNodes(ids, graph) {
25201 return _.uniq(_.reduce(ids, function(result, id) {
25202 var e = graph.entity(id);
25203 if (e.type === 'way') {
25205 var cn = graph.childNodes(e);
25206 result.push.apply(result, _.pluck(_.filter(cn, 'version'), 'id'));
25208 /* eslint-disable no-console */
25209 if (typeof console !== 'undefined') console.error(err);
25210 /* eslint-enable no-console */
25217 var loading = iD.ui.Loading(context).message(t('save.uploading')).blocking(true),
25218 history = context.history(),
25219 origChanges = history.changes(iD.actions.DiscardTags(history.difference())),
25220 localGraph = context.graph(),
25221 remoteGraph = iD.Graph(history.base(), true),
25222 modified = _.filter(history.difference().summary(), {changeType: 'modified'}),
25223 toCheck = _.pluck(_.pluck(modified, 'entity'), 'id'),
25224 toLoad = withChildNodes(toCheck, localGraph),
25228 if (!tryAgain) history.perform(iD.actions.Noop()); // checkpoint
25229 context.container().call(loading);
25231 if (toCheck.length) {
25232 context.connection().loadMultiple(toLoad, loaded);
25238 // Reload modified entities into an alternate graph and check for conflicts..
25239 function loaded(err, result) {
25240 if (errors.length) return;
25244 msg: err.responseText,
25245 details: [ t('save.status_code', { code: err.status }) ]
25251 _.each(result.data, function(entity) {
25252 remoteGraph.replace(entity);
25253 toLoad = _.without(toLoad, entity.id);
25255 // Because loadMultiple doesn't download /full like loadEntity,
25256 // need to also load children that aren't already being checked..
25257 if (!entity.visible) return;
25258 if (entity.type === 'way') {
25259 loadMore.push.apply(loadMore,
25260 _.difference(entity.nodes, toCheck, toLoad, loadMore));
25261 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
25262 loadMore.push.apply(loadMore,
25263 _.difference(_.pluck(entity.members, 'id'), toCheck, toLoad, loadMore));
25267 if (loadMore.length) {
25268 toLoad.push.apply(toLoad, loadMore);
25269 context.connection().loadMultiple(loadMore, loaded);
25272 if (!toLoad.length) {
25279 function checkConflicts() {
25280 function choice(id, text, action) {
25281 return { id: id, text: text, action: function() { history.replace(action); } };
25283 function formatUser(d) {
25284 return '<a href="' + context.connection().userURL(d) + '" target="_blank">' + d + '</a>';
25286 function entityName(entity) {
25287 return iD.util.displayName(entity) || (iD.util.displayType(entity.id) + ' ' + entity.id);
25290 function compareVersions(local, remote) {
25291 if (local.version !== remote.version) return false;
25293 if (local.type === 'way') {
25294 var children = _.union(local.nodes, remote.nodes);
25296 for (var i = 0; i < children.length; i++) {
25297 var a = localGraph.hasEntity(children[i]),
25298 b = remoteGraph.hasEntity(children[i]);
25300 if (a && b && a.version !== b.version) return false;
25307 _.each(toCheck, function(id) {
25308 var local = localGraph.entity(id),
25309 remote = remoteGraph.entity(id);
25311 if (compareVersions(local, remote)) return;
25313 var action = iD.actions.MergeRemoteChanges,
25314 merge = action(id, localGraph, remoteGraph, formatUser);
25316 history.replace(merge);
25318 var mergeConflicts = merge.conflicts();
25319 if (!mergeConflicts.length) return; // merged safely
25321 var forceLocal = action(id, localGraph, remoteGraph).withOption('force_local'),
25322 forceRemote = action(id, localGraph, remoteGraph).withOption('force_remote'),
25323 keepMine = t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore')),
25324 keepTheirs = t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
25328 name: entityName(local),
25329 details: mergeConflicts,
25332 choice(id, keepMine, forceLocal),
25333 choice(id, keepTheirs, forceRemote)
25342 function finalize() {
25343 if (conflicts.length) {
25344 conflicts.sort(function(a,b) { return b.id.localeCompare(a.id); });
25346 } else if (errors.length) {
25349 var changes = history.changes(iD.actions.DiscardTags(history.difference()));
25350 if (changes.modified.length || changes.created.length || changes.deleted.length) {
25351 context.connection().putChangeset(
25354 history.imageryUsed(),
25355 function(err, changeset_id) {
25358 msg: err.responseText,
25359 details: [ t('save.status_code', { code: err.status }) ]
25363 history.clearSaved();
25364 success(e, changeset_id);
25365 // Add delay to allow for postgres replication #1646 #2678
25366 window.setTimeout(function() {
25372 } else { // changes were insignificant or reverted by user
25381 function showConflicts() {
25382 var selection = context.container()
25383 .select('#sidebar')
25385 .attr('class','sidebar-component');
25389 selection.call(iD.ui.Conflicts(context)
25391 .on('download', function() {
25392 var data = JXON.stringify(context.connection().osmChangeJXON('CHANGEME', origChanges)),
25393 win = window.open('data:text/xml,' + encodeURIComponent(data), '_blank');
25396 .on('cancel', function() {
25398 selection.remove();
25400 .on('save', function() {
25401 for (var i = 0; i < conflicts.length; i++) {
25402 if (conflicts[i].chosen === 1) { // user chose "keep theirs"
25403 var entity = context.hasEntity(conflicts[i].id);
25404 if (entity && entity.type === 'way') {
25405 var children = _.uniq(entity.nodes);
25406 for (var j = 0; j < children.length; j++) {
25407 history.replace(iD.actions.Revert(children[j]));
25410 history.replace(iD.actions.Revert(conflicts[i].id));
25414 selection.remove();
25421 function showErrors() {
25422 var selection = iD.ui.confirm(context.container());
25428 .select('.modal-section.header')
25430 .text(t('save.error'));
25432 addErrors(selection, errors);
25433 selection.okButton();
25437 function addErrors(selection, data) {
25438 var message = selection
25439 .select('.modal-section.message-text');
25441 var items = message
25442 .selectAll('.error-container')
25445 var enter = items.enter()
25447 .attr('class', 'error-container');
25451 .attr('class', 'error-description')
25453 .classed('hide-toggle', true)
25454 .text(function(d) { return d.msg || t('save.unknown_error_details'); })
25455 .on('click', function() {
25456 var error = d3.select(this),
25457 detail = d3.select(this.nextElementSibling),
25458 exp = error.classed('expanded');
25460 detail.style('display', exp ? 'none' : 'block');
25461 error.classed('expanded', !exp);
25463 d3.event.preventDefault();
25466 var details = enter
25468 .attr('class', 'error-detail-container')
25469 .style('display', 'none');
25473 .attr('class', 'error-detail-list')
25475 .data(function(d) { return d.details || []; })
25478 .attr('class', 'error-detail-item')
25479 .text(function(d) { return d; });
25488 function success(e, changeset_id) {
25489 context.enter(iD.modes.Browse(context)
25490 .sidebar(iD.ui.Success(context)
25495 .on('cancel', function() {
25496 context.ui().sidebar.hide();
25504 mode.enter = function() {
25505 context.connection().authenticate(function(err) {
25509 context.ui().sidebar.show(ui);
25514 mode.exit = function() {
25515 context.ui().sidebar.hide();
25520 iD.modes.Select = function(context, selectedIDs) {
25526 var keybinding = d3.keybinding('select'),
25529 iD.behavior.Copy(context),
25530 iD.behavior.Paste(context),
25531 iD.behavior.Breathe(context),
25532 iD.behavior.Hover(context),
25533 iD.behavior.Select(context),
25534 iD.behavior.Lasso(context),
25535 iD.modes.DragNode(context)
25536 .selectedIDs(selectedIDs)
25540 newFeature = false,
25541 suppressMenu = false;
25543 var wrap = context.container()
25544 .select('.inspector-wrap');
25547 function singular() {
25548 if (selectedIDs.length === 1) {
25549 return context.hasEntity(selectedIDs[0]);
25553 function closeMenu() {
25555 context.surface().call(radialMenu.close);
25559 function positionMenu() {
25560 if (suppressMenu || !radialMenu) { return; }
25562 var entity = singular();
25563 if (entity && context.geometry(entity.id) === 'relation') {
25564 suppressMenu = true;
25565 } else if (entity && entity.type === 'node') {
25566 radialMenu.center(context.projection(entity.loc));
25568 var point = context.mouse(),
25569 viewport = iD.geo.Extent(context.projection.clipExtent()).polygon();
25570 if (iD.geo.pointInPolygon(point, viewport)) {
25571 radialMenu.center(point);
25573 suppressMenu = true;
25578 function showMenu() {
25580 if (!suppressMenu && radialMenu) {
25581 context.surface().call(radialMenu);
25585 function toggleMenu() {
25586 if (d3.select('.radial-menu').empty()) {
25593 mode.selectedIDs = function() {
25594 return selectedIDs;
25597 mode.reselect = function() {
25598 var surfaceNode = context.surface().node();
25599 if (surfaceNode.focus) { // FF doesn't support it
25600 surfaceNode.focus();
25607 mode.newFeature = function(_) {
25608 if (!arguments.length) return newFeature;
25613 mode.suppressMenu = function(_) {
25614 if (!arguments.length) return suppressMenu;
25619 mode.enter = function() {
25620 function update() {
25622 if (_.any(selectedIDs, function(id) { return !context.hasEntity(id); })) {
25623 // Exit mode if selected entity gets undone
25624 context.enter(iD.modes.Browse(context));
25628 function dblclick() {
25629 var target = d3.select(d3.event.target),
25630 datum = target.datum();
25632 if (datum instanceof iD.Way && !target.classed('fill')) {
25633 var choice = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection),
25636 var prev = datum.nodes[choice.index - 1],
25637 next = datum.nodes[choice.index];
25640 iD.actions.AddMidpoint({loc: choice.loc, edge: [prev, next]}, node),
25641 t('operations.add.annotation.vertex'));
25643 d3.event.preventDefault();
25644 d3.event.stopPropagation();
25648 function selectElements(drawn) {
25649 var entity = singular();
25650 if (entity && context.geometry(entity.id) === 'relation') {
25651 suppressMenu = true;
25655 var selection = context.surface()
25656 .selectAll(iD.util.entityOrMemberSelector(selectedIDs, context.graph()));
25658 if (selection.empty()) {
25659 if (drawn) { // Exit mode if selected DOM elements have disappeared..
25660 context.enter(iD.modes.Browse(context));
25664 .classed('selected', true);
25669 if (!context.inIntro()) {
25670 context.enter(iD.modes.Browse(context));
25675 behaviors.forEach(function(behavior) {
25676 context.install(behavior);
25679 var operations = _.without(d3.values(iD.operations), iD.operations.Delete)
25680 .map(function(o) { return o(selectedIDs, context); })
25681 .filter(function(o) { return o.available(); });
25683 operations.unshift(iD.operations.Delete(selectedIDs, context));
25686 .on('⎋', esc, true)
25687 .on('space', toggleMenu);
25689 operations.forEach(function(operation) {
25690 operation.keys.forEach(function(key) {
25691 keybinding.on(key, function() {
25692 if (!(context.inIntro() || operation.disabled())) {
25699 d3.select(document)
25702 radialMenu = iD.ui.RadialMenu(context, operations);
25704 context.ui().sidebar
25705 .select(singular() ? singular().id : null, newFeature);
25708 .on('undone.select', update)
25709 .on('redone.select', update);
25712 .on('move.select', closeMenu)
25713 .on('drawn.select', selectElements);
25717 var show = d3.event && !suppressMenu;
25723 timeout = window.setTimeout(function() {
25729 .on('dblclick.select', dblclick);
25732 if (selectedIDs.length > 1) {
25733 var entities = iD.ui.SelectionList(context, selectedIDs);
25734 context.ui().sidebar.show(entities);
25738 mode.exit = function() {
25739 if (timeout) window.clearTimeout(timeout);
25741 if (inspector) wrap.call(inspector.close);
25743 behaviors.forEach(function(behavior) {
25744 context.uninstall(behavior);
25749 radialMenu = undefined;
25752 .on('undone.select', null)
25753 .on('redone.select', null);
25756 .on('dblclick.select', null)
25757 .selectAll('.selected')
25758 .classed('selected', false);
25760 context.map().on('drawn.select', null);
25761 context.ui().sidebar.hide();
25766 iD.operations = {};
25767 iD.operations.Circularize = function(selectedIDs, context) {
25768 var entityId = selectedIDs[0],
25769 entity = context.entity(entityId),
25770 extent = entity.extent(context.graph()),
25771 geometry = context.geometry(entityId),
25772 action = iD.actions.Circularize(entityId, context.projection);
25774 var operation = function() {
25775 var annotation = t('operations.circularize.annotation.' + geometry);
25776 context.perform(action, annotation);
25779 operation.available = function() {
25780 return selectedIDs.length === 1 &&
25781 entity.type === 'way' &&
25782 _.uniq(entity.nodes).length > 1;
25785 operation.disabled = function() {
25787 if (extent.percentContainedIn(context.extent()) < 0.8) {
25788 reason = 'too_large';
25789 } else if (context.hasHiddenConnections(entityId)) {
25790 reason = 'connected_to_hidden';
25792 return action.disabled(context.graph()) || reason;
25795 operation.tooltip = function() {
25796 var disable = operation.disabled();
25798 t('operations.circularize.' + disable) :
25799 t('operations.circularize.description.' + geometry);
25802 operation.id = 'circularize';
25803 operation.keys = [t('operations.circularize.key')];
25804 operation.title = t('operations.circularize.title');
25808 iD.operations.Continue = function(selectedIDs, context) {
25809 var graph = context.graph(),
25810 entities = selectedIDs.map(function(id) { return graph.entity(id); }),
25811 geometries = _.extend({line: [], vertex: []},
25812 _.groupBy(entities, function(entity) { return entity.geometry(graph); })),
25813 vertex = geometries.vertex[0];
25815 function candidateWays() {
25816 return graph.parentWays(vertex).filter(function(parent) {
25817 return parent.geometry(graph) === 'line' &&
25818 parent.affix(vertex.id) &&
25819 (geometries.line.length === 0 || geometries.line[0] === parent);
25823 var operation = function() {
25824 var candidate = candidateWays()[0];
25825 context.enter(iD.modes.DrawLine(
25829 candidate.affix(vertex.id)));
25832 operation.available = function() {
25833 return geometries.vertex.length === 1 && geometries.line.length <= 1 &&
25834 !context.features().hasHiddenConnections(vertex, context.graph());
25837 operation.disabled = function() {
25838 var candidates = candidateWays();
25839 if (candidates.length === 0)
25840 return 'not_eligible';
25841 if (candidates.length > 1)
25845 operation.tooltip = function() {
25846 var disable = operation.disabled();
25848 t('operations.continue.' + disable) :
25849 t('operations.continue.description');
25852 operation.id = 'continue';
25853 operation.keys = [t('operations.continue.key')];
25854 operation.title = t('operations.continue.title');
25858 iD.operations.Delete = function(selectedIDs, context) {
25859 var action = iD.actions.DeleteMultiple(selectedIDs);
25861 var operation = function() {
25865 if (selectedIDs.length > 1) {
25866 annotation = t('operations.delete.annotation.multiple', {n: selectedIDs.length});
25869 var id = selectedIDs[0],
25870 entity = context.entity(id),
25871 geometry = context.geometry(id),
25872 parents = context.graph().parentWays(entity),
25873 parent = parents[0];
25875 annotation = t('operations.delete.annotation.' + geometry);
25877 // Select the next closest node in the way.
25878 if (geometry === 'vertex' && parents.length === 1 && parent.nodes.length > 2) {
25879 var nodes = parent.nodes,
25880 i = nodes.indexOf(id);
25884 } else if (i === nodes.length - 1) {
25887 var a = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc),
25888 b = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
25889 i = a < b ? i - 1 : i + 1;
25892 nextSelectedID = nodes[i];
25896 if (nextSelectedID && context.hasEntity(nextSelectedID)) {
25897 context.enter(iD.modes.Select(context, [nextSelectedID]));
25899 context.enter(iD.modes.Browse(context));
25907 operation.available = function() {
25911 operation.disabled = function() {
25913 if (_.any(selectedIDs, context.hasHiddenConnections)) {
25914 reason = 'connected_to_hidden';
25916 return action.disabled(context.graph()) || reason;
25919 operation.tooltip = function() {
25920 var disable = operation.disabled();
25922 t('operations.delete.' + disable) :
25923 t('operations.delete.description');
25926 operation.id = 'delete';
25927 operation.keys = [iD.ui.cmd('⌘⌫'), iD.ui.cmd('⌘⌦')];
25928 operation.title = t('operations.delete.title');
25932 iD.operations.Disconnect = function(selectedIDs, context) {
25933 var vertices = _.filter(selectedIDs, function vertex(entityId) {
25934 return context.geometry(entityId) === 'vertex';
25937 var entityId = vertices[0],
25938 action = iD.actions.Disconnect(entityId);
25940 if (selectedIDs.length > 1) {
25941 action.limitWays(_.without(selectedIDs, entityId));
25944 var operation = function() {
25945 context.perform(action, t('operations.disconnect.annotation'));
25948 operation.available = function() {
25949 return vertices.length === 1;
25952 operation.disabled = function() {
25954 if (_.any(selectedIDs, context.hasHiddenConnections)) {
25955 reason = 'connected_to_hidden';
25957 return action.disabled(context.graph()) || reason;
25960 operation.tooltip = function() {
25961 var disable = operation.disabled();
25963 t('operations.disconnect.' + disable) :
25964 t('operations.disconnect.description');
25967 operation.id = 'disconnect';
25968 operation.keys = [t('operations.disconnect.key')];
25969 operation.title = t('operations.disconnect.title');
25973 iD.operations.Merge = function(selectedIDs, context) {
25974 var join = iD.actions.Join(selectedIDs),
25975 merge = iD.actions.Merge(selectedIDs),
25976 mergePolygon = iD.actions.MergePolygon(selectedIDs);
25978 var operation = function() {
25979 var annotation = t('operations.merge.annotation', {n: selectedIDs.length}),
25982 if (!join.disabled(context.graph())) {
25984 } else if (!merge.disabled(context.graph())) {
25987 action = mergePolygon;
25990 context.perform(action, annotation);
25991 context.enter(iD.modes.Select(context, selectedIDs.filter(function(id) { return context.hasEntity(id); }))
25992 .suppressMenu(true));
25995 operation.available = function() {
25996 return selectedIDs.length >= 2;
25999 operation.disabled = function() {
26000 return join.disabled(context.graph()) &&
26001 merge.disabled(context.graph()) &&
26002 mergePolygon.disabled(context.graph());
26005 operation.tooltip = function() {
26006 var j = join.disabled(context.graph()),
26007 m = merge.disabled(context.graph()),
26008 p = mergePolygon.disabled(context.graph());
26010 if (j === 'restriction' && m && p)
26011 return t('operations.merge.restriction', {relation: context.presets().item('type/restriction').name()});
26013 if (p === 'incomplete_relation' && j && m)
26014 return t('operations.merge.incomplete_relation');
26017 return t('operations.merge.' + j);
26019 return t('operations.merge.description');
26022 operation.id = 'merge';
26023 operation.keys = [t('operations.merge.key')];
26024 operation.title = t('operations.merge.title');
26028 iD.operations.Move = function(selectedIDs, context) {
26029 var extent = selectedIDs.reduce(function(extent, id) {
26030 return extent.extend(context.entity(id).extent(context.graph()));
26031 }, iD.geo.Extent());
26033 var operation = function() {
26034 context.enter(iD.modes.Move(context, selectedIDs));
26037 operation.available = function() {
26038 return selectedIDs.length > 1 ||
26039 context.entity(selectedIDs[0]).type !== 'node';
26042 operation.disabled = function() {
26044 if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
26045 reason = 'too_large';
26046 } else if (_.any(selectedIDs, context.hasHiddenConnections)) {
26047 reason = 'connected_to_hidden';
26049 return iD.actions.Move(selectedIDs).disabled(context.graph()) || reason;
26052 operation.tooltip = function() {
26053 var disable = operation.disabled();
26055 t('operations.move.' + disable) :
26056 t('operations.move.description');
26059 operation.id = 'move';
26060 operation.keys = [t('operations.move.key')];
26061 operation.title = t('operations.move.title');
26065 iD.operations.Orthogonalize = function(selectedIDs, context) {
26066 var entityId = selectedIDs[0],
26067 entity = context.entity(entityId),
26068 extent = entity.extent(context.graph()),
26069 geometry = context.geometry(entityId),
26070 action = iD.actions.Orthogonalize(entityId, context.projection);
26072 var operation = function() {
26073 var annotation = t('operations.orthogonalize.annotation.' + geometry);
26074 context.perform(action, annotation);
26077 operation.available = function() {
26078 return selectedIDs.length === 1 &&
26079 entity.type === 'way' &&
26080 entity.isClosed() &&
26081 _.uniq(entity.nodes).length > 2;
26084 operation.disabled = function() {
26086 if (extent.percentContainedIn(context.extent()) < 0.8) {
26087 reason = 'too_large';
26088 } else if (context.hasHiddenConnections(entityId)) {
26089 reason = 'connected_to_hidden';
26091 return action.disabled(context.graph()) || reason;
26094 operation.tooltip = function() {
26095 var disable = operation.disabled();
26097 t('operations.orthogonalize.' + disable) :
26098 t('operations.orthogonalize.description.' + geometry);
26101 operation.id = 'orthogonalize';
26102 operation.keys = [t('operations.orthogonalize.key')];
26103 operation.title = t('operations.orthogonalize.title');
26107 iD.operations.Reverse = function(selectedIDs, context) {
26108 var entityId = selectedIDs[0];
26110 var operation = function() {
26112 iD.actions.Reverse(entityId),
26113 t('operations.reverse.annotation'));
26116 operation.available = function() {
26117 return selectedIDs.length === 1 &&
26118 context.geometry(entityId) === 'line';
26121 operation.disabled = function() {
26125 operation.tooltip = function() {
26126 return t('operations.reverse.description');
26129 operation.id = 'reverse';
26130 operation.keys = [t('operations.reverse.key')];
26131 operation.title = t('operations.reverse.title');
26135 iD.operations.Rotate = function(selectedIDs, context) {
26136 var entityId = selectedIDs[0],
26137 entity = context.entity(entityId),
26138 extent = entity.extent(context.graph()),
26139 geometry = context.geometry(entityId);
26141 var operation = function() {
26142 context.enter(iD.modes.RotateWay(context, entityId));
26145 operation.available = function() {
26146 if (selectedIDs.length !== 1 || entity.type !== 'way')
26148 if (geometry === 'area')
26150 if (entity.isClosed() &&
26151 context.graph().parentRelations(entity).some(function(r) { return r.isMultipolygon(); }))
26156 operation.disabled = function() {
26157 if (extent.percentContainedIn(context.extent()) < 0.8) {
26158 return 'too_large';
26159 } else if (context.hasHiddenConnections(entityId)) {
26160 return 'connected_to_hidden';
26166 operation.tooltip = function() {
26167 var disable = operation.disabled();
26169 t('operations.rotate.' + disable) :
26170 t('operations.rotate.description');
26173 operation.id = 'rotate';
26174 operation.keys = [t('operations.rotate.key')];
26175 operation.title = t('operations.rotate.title');
26179 iD.operations.Split = function(selectedIDs, context) {
26180 var vertices = _.filter(selectedIDs, function vertex(entityId) {
26181 return context.geometry(entityId) === 'vertex';
26184 var entityId = vertices[0],
26185 action = iD.actions.Split(entityId);
26187 if (selectedIDs.length > 1) {
26188 action.limitWays(_.without(selectedIDs, entityId));
26191 var operation = function() {
26194 var ways = action.ways(context.graph());
26195 if (ways.length === 1) {
26196 annotation = t('operations.split.annotation.' + context.geometry(ways[0].id));
26198 annotation = t('operations.split.annotation.multiple', {n: ways.length});
26201 var difference = context.perform(action, annotation);
26202 context.enter(iD.modes.Select(context, difference.extantIDs()));
26205 operation.available = function() {
26206 return vertices.length === 1;
26209 operation.disabled = function() {
26211 if (_.any(selectedIDs, context.hasHiddenConnections)) {
26212 reason = 'connected_to_hidden';
26214 return action.disabled(context.graph()) || reason;
26217 operation.tooltip = function() {
26218 var disable = operation.disabled();
26220 return t('operations.split.' + disable);
26223 var ways = action.ways(context.graph());
26224 if (ways.length === 1) {
26225 return t('operations.split.description.' + context.geometry(ways[0].id));
26227 return t('operations.split.description.multiple');
26231 operation.id = 'split';
26232 operation.keys = [t('operations.split.key')];
26233 operation.title = t('operations.split.title');
26237 iD.operations.Straighten = function(selectedIDs, context) {
26238 var entityId = selectedIDs[0],
26239 action = iD.actions.Straighten(entityId, context.projection);
26241 function operation() {
26242 var annotation = t('operations.straighten.annotation');
26243 context.perform(action, annotation);
26246 operation.available = function() {
26247 var entity = context.entity(entityId);
26248 return selectedIDs.length === 1 &&
26249 entity.type === 'way' &&
26250 !entity.isClosed() &&
26251 _.uniq(entity.nodes).length > 2;
26254 operation.disabled = function() {
26256 if (context.hasHiddenConnections(entityId)) {
26257 reason = 'connected_to_hidden';
26259 return action.disabled(context.graph()) || reason;
26262 operation.tooltip = function() {
26263 var disable = operation.disabled();
26265 t('operations.straighten.' + disable) :
26266 t('operations.straighten.description');
26269 operation.id = 'straighten';
26270 operation.keys = [t('operations.straighten.key')];
26271 operation.title = t('operations.straighten.title');
26275 iD.Connection = function(useHttps) {
26276 if (typeof useHttps !== 'boolean') {
26277 useHttps = window.location.protocol === 'https:';
26280 var event = d3.dispatch('authenticating', 'authenticated', 'auth', 'loading', 'loaded'),
26281 protocol = useHttps ? 'https:' : 'http:',
26282 url = protocol + '//www.openstreetmap.org',
26288 url: protocol + '//www.openstreetmap.org',
26289 oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
26290 oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
26291 loading: authenticating,
26292 done: authenticated
26296 memberStr = 'member',
26299 relationStr = 'relation',
26304 connection.changesetURL = function(changesetId) {
26305 return url + '/changeset/' + changesetId;
26308 connection.changesetsURL = function(center, zoom) {
26309 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
26310 return url + '/history#map=' +
26311 Math.floor(zoom) + '/' +
26312 center[1].toFixed(precision) + '/' +
26313 center[0].toFixed(precision);
26316 connection.entityURL = function(entity) {
26317 return url + '/' + entity.type + '/' + entity.osmId();
26320 connection.userURL = function(username) {
26321 return url + '/user/' + username;
26324 connection.loadFromURL = function(url, callback) {
26325 function done(err, dom) {
26326 return callback(err, parse(dom));
26328 return d3.xml(url).get(done);
26331 connection.loadEntity = function(id, callback) {
26332 var type = iD.Entity.id.type(id),
26333 osmID = iD.Entity.id.toOSM(id);
26335 connection.loadFromURL(
26336 url + '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''),
26337 function(err, entities) {
26338 if (callback) callback(err, {data: entities});
26342 connection.loadEntityVersion = function(id, version, callback) {
26343 var type = iD.Entity.id.type(id),
26344 osmID = iD.Entity.id.toOSM(id);
26346 connection.loadFromURL(
26347 url + '/api/0.6/' + type + '/' + osmID + '/' + version,
26348 function(err, entities) {
26349 if (callback) callback(err, {data: entities});
26353 connection.loadMultiple = function(ids, callback) {
26354 _.each(_.groupBy(_.uniq(ids), iD.Entity.id.type), function(v, k) {
26355 var type = k + 's',
26356 osmIDs = _.map(v, iD.Entity.id.toOSM);
26358 _.each(_.chunk(osmIDs, 150), function(arr) {
26359 connection.loadFromURL(
26360 url + '/api/0.6/' + type + '?' + type + '=' + arr.join(),
26361 function(err, entities) {
26362 if (callback) callback(err, {data: entities});
26368 function authenticating() {
26369 event.authenticating();
26372 function authenticated() {
26373 event.authenticated();
26376 function getLoc(attrs) {
26377 var lon = attrs.lon && attrs.lon.value,
26378 lat = attrs.lat && attrs.lat.value;
26379 return [parseFloat(lon), parseFloat(lat)];
26382 function getNodes(obj) {
26383 var elems = obj.getElementsByTagName(ndStr),
26384 nodes = new Array(elems.length);
26385 for (var i = 0, l = elems.length; i < l; i++) {
26386 nodes[i] = 'n' + elems[i].attributes.ref.value;
26391 function getTags(obj) {
26392 var elems = obj.getElementsByTagName(tagStr),
26394 for (var i = 0, l = elems.length; i < l; i++) {
26395 var attrs = elems[i].attributes;
26396 tags[attrs.k.value] = attrs.v.value;
26401 function getMembers(obj) {
26402 var elems = obj.getElementsByTagName(memberStr),
26403 members = new Array(elems.length);
26404 for (var i = 0, l = elems.length; i < l; i++) {
26405 var attrs = elems[i].attributes;
26407 id: attrs.type.value[0] + attrs.ref.value,
26408 type: attrs.type.value,
26409 role: attrs.role.value
26415 function getVisible(attrs) {
26416 return (!attrs.visible || attrs.visible.value !== 'false');
26420 node: function nodeData(obj) {
26421 var attrs = obj.attributes;
26422 return new iD.Node({
26423 id: iD.Entity.id.fromOSM(nodeStr, attrs.id.value),
26424 loc: getLoc(attrs),
26425 version: attrs.version.value,
26426 user: attrs.user && attrs.user.value,
26427 tags: getTags(obj),
26428 visible: getVisible(attrs)
26432 way: function wayData(obj) {
26433 var attrs = obj.attributes;
26434 return new iD.Way({
26435 id: iD.Entity.id.fromOSM(wayStr, attrs.id.value),
26436 version: attrs.version.value,
26437 user: attrs.user && attrs.user.value,
26438 tags: getTags(obj),
26439 nodes: getNodes(obj),
26440 visible: getVisible(attrs)
26444 relation: function relationData(obj) {
26445 var attrs = obj.attributes;
26446 return new iD.Relation({
26447 id: iD.Entity.id.fromOSM(relationStr, attrs.id.value),
26448 version: attrs.version.value,
26449 user: attrs.user && attrs.user.value,
26450 tags: getTags(obj),
26451 members: getMembers(obj),
26452 visible: getVisible(attrs)
26457 function parse(dom) {
26458 if (!dom || !dom.childNodes) return;
26460 var root = dom.childNodes[0],
26461 children = root.childNodes,
26464 for (var i = 0, l = children.length; i < l; i++) {
26465 var child = children[i],
26466 parser = parsers[child.nodeName];
26468 entities.push(parser(child));
26475 connection.authenticated = function() {
26476 return oauth.authenticated();
26479 // Generate Changeset XML. Returns a string.
26480 connection.changesetJXON = function(tags) {
26484 tag: _.map(tags, function(value, key) {
26485 return { '@k': key, '@v': value };
26494 // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
26495 // XML. Returns a string.
26496 connection.osmChangeJXON = function(changeset_id, changes) {
26497 function nest(x, order) {
26499 for (var i = 0; i < x.length; i++) {
26500 var tagName = Object.keys(x[i])[0];
26501 if (!groups[tagName]) groups[tagName] = [];
26502 groups[tagName].push(x[i][tagName]);
26505 order.forEach(function(o) {
26506 if (groups[o]) ordered[o] = groups[o];
26511 function rep(entity) {
26512 return entity.asJXON(changeset_id);
26518 '@generator': 'iD',
26519 'create': nest(changes.created.map(rep), ['node', 'way', 'relation']),
26520 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
26521 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true})
26526 connection.changesetTags = function(comment, imageryUsed) {
26527 var detected = iD.detect(),
26529 created_by: 'iD ' + iD.version,
26530 imagery_used: imageryUsed.join(';').substr(0, 255),
26531 host: (window.location.origin + window.location.pathname).substr(0, 255),
26532 locale: detected.locale
26536 tags.comment = comment.substr(0, 255);
26542 connection.putChangeset = function(changes, comment, imageryUsed, callback) {
26545 path: '/api/0.6/changeset/create',
26546 options: { header: { 'Content-Type': 'text/xml' } },
26547 content: JXON.stringify(connection.changesetJXON(connection.changesetTags(comment, imageryUsed)))
26548 }, function(err, changeset_id) {
26549 if (err) return callback(err);
26552 path: '/api/0.6/changeset/' + changeset_id + '/upload',
26553 options: { header: { 'Content-Type': 'text/xml' } },
26554 content: JXON.stringify(connection.osmChangeJXON(changeset_id, changes))
26556 if (err) return callback(err);
26557 // POST was successful, safe to call the callback.
26558 // Still attempt to close changeset, but ignore response because #2667
26559 // Add delay to allow for postgres replication #1646 #2678
26560 window.setTimeout(function() { callback(null, changeset_id); }, 2500);
26563 path: '/api/0.6/changeset/' + changeset_id + '/close',
26564 options: { header: { 'Content-Type': 'text/xml' } }
26565 }, d3.functor(true));
26570 connection.userDetails = function(callback) {
26572 callback(undefined, userDetails);
26576 function done(err, user_details) {
26577 if (err) return callback(err);
26579 var u = user_details.getElementsByTagName('user')[0],
26580 img = u.getElementsByTagName('img'),
26583 if (img && img[0] && img[0].getAttribute('href')) {
26584 image_url = img[0].getAttribute('href');
26588 display_name: u.attributes.display_name.value,
26589 image_url: image_url,
26590 id: u.attributes.id.value
26593 callback(undefined, userDetails);
26596 oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
26599 connection.userChangesets = function(callback) {
26600 connection.userDetails(function(err, user) {
26601 if (err) return callback(err);
26603 function done(changesets) {
26604 callback(undefined, Array.prototype.map.call(changesets.getElementsByTagName('changeset'),
26605 function (changeset) {
26606 return { tags: getTags(changeset) };
26610 d3.xml(url + '/api/0.6/changesets?user=' + user.id).get()
26612 .on('error', callback);
26616 connection.status = function(callback) {
26617 function done(capabilities) {
26618 var apiStatus = capabilities.getElementsByTagName('status');
26619 callback(undefined, apiStatus[0].getAttribute('api'));
26621 d3.xml(url + '/api/capabilities').get()
26623 .on('error', callback);
26626 function abortRequest(i) { i.abort(); }
26628 connection.tileZoom = function(_) {
26629 if (!arguments.length) return tileZoom;
26634 connection.loadTiles = function(projection, dimensions, callback) {
26638 var s = projection.scale() * 2 * Math.PI,
26639 z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
26640 ts = 256 * Math.pow(2, z - tileZoom),
26642 s / 2 - projection.translate()[0],
26643 s / 2 - projection.translate()[1]];
26645 var tiles = d3.geo.tile()
26646 .scaleExtent([tileZoom, tileZoom])
26649 .translate(projection.translate())()
26650 .map(function(tile) {
26651 var x = tile[0] * ts - origin[0],
26652 y = tile[1] * ts - origin[1];
26655 id: tile.toString(),
26656 extent: iD.geo.Extent(
26657 projection.invert([x, y + ts]),
26658 projection.invert([x + ts, y]))
26662 function bboxUrl(tile) {
26663 return url + '/api/0.6/map?bbox=' + tile.extent.toParam();
26666 _.filter(inflight, function(v, i) {
26667 var wanted = _.find(tiles, function(tile) {
26668 return i === tile.id;
26670 if (!wanted) delete inflight[i];
26672 }).map(abortRequest);
26674 tiles.forEach(function(tile) {
26677 if (loadedTiles[id] || inflight[id]) return;
26679 if (_.isEmpty(inflight)) {
26683 inflight[id] = connection.loadFromURL(bboxUrl(tile), function(err, parsed) {
26684 loadedTiles[id] = true;
26685 delete inflight[id];
26687 if (callback) callback(err, _.extend({data: parsed}, tile));
26689 if (_.isEmpty(inflight)) {
26696 connection.switch = function(options) {
26698 oauth.options(_.extend({
26699 loading: authenticating,
26700 done: authenticated
26703 connection.flush();
26707 connection.toggle = function(_) {
26712 connection.flush = function() {
26713 userDetails = undefined;
26714 _.forEach(inflight, abortRequest);
26720 connection.loadedTiles = function(_) {
26721 if (!arguments.length) return loadedTiles;
26726 connection.logout = function() {
26727 userDetails = undefined;
26733 connection.authenticate = function(callback) {
26734 userDetails = undefined;
26735 function done(err, res) {
26737 if (callback) callback(err, res);
26739 return oauth.authenticate(done);
26742 return d3.rebind(connection, event, 'on');
26745 iD.Difference represents the difference between two graphs.
26746 It knows how to calculate the set of entities that were
26747 created, modified, or deleted, and also contains the logic
26748 for recursively extending a difference to the complete set
26749 of entities that will require a redraw, taking into account
26750 child and parent relationships.
26752 iD.Difference = function(base, head) {
26753 var changes = {}, length = 0;
26755 function changed(h, b) {
26756 return h !== b && !_.isEqual(_.omit(h, 'v'), _.omit(b, 'v'));
26759 _.each(head.entities, function(h, id) {
26760 var b = base.entities[id];
26761 if (changed(h, b)) {
26762 changes[id] = {base: b, head: h};
26767 _.each(base.entities, function(b, id) {
26768 var h = head.entities[id];
26769 if (!changes[id] && changed(h, b)) {
26770 changes[id] = {base: b, head: h};
26775 function addParents(parents, result) {
26776 for (var i = 0; i < parents.length; i++) {
26777 var parent = parents[i];
26779 if (parent.id in result)
26782 result[parent.id] = parent;
26783 addParents(head.parentRelations(parent), result);
26787 var difference = {};
26789 difference.length = function() {
26793 difference.changes = function() {
26797 difference.extantIDs = function() {
26799 _.each(changes, function(change, id) {
26800 if (change.head) result.push(id);
26805 difference.modified = function() {
26807 _.each(changes, function(change) {
26808 if (change.base && change.head) result.push(change.head);
26813 difference.created = function() {
26815 _.each(changes, function(change) {
26816 if (!change.base && change.head) result.push(change.head);
26821 difference.deleted = function() {
26823 _.each(changes, function(change) {
26824 if (change.base && !change.head) result.push(change.base);
26829 difference.summary = function() {
26832 function addEntity(entity, graph, changeType) {
26833 relevant[entity.id] = {
26836 changeType: changeType
26840 function addParents(entity) {
26841 var parents = head.parentWays(entity);
26842 for (var j = parents.length - 1; j >= 0; j--) {
26843 var parent = parents[j];
26844 if (!(parent.id in relevant)) addEntity(parent, head, 'modified');
26848 _.each(changes, function(change) {
26849 if (change.head && change.head.geometry(head) !== 'vertex') {
26850 addEntity(change.head, head, change.base ? 'modified' : 'created');
26852 } else if (change.base && change.base.geometry(base) !== 'vertex') {
26853 addEntity(change.base, base, 'deleted');
26855 } else if (change.base && change.head) { // modified vertex
26856 var moved = !_.isEqual(change.base.loc, change.head.loc),
26857 retagged = !_.isEqual(change.base.tags, change.head.tags);
26860 addParents(change.head);
26863 if (retagged || (moved && change.head.hasInterestingTags())) {
26864 addEntity(change.head, head, 'modified');
26867 } else if (change.head && change.head.hasInterestingTags()) { // created vertex
26868 addEntity(change.head, head, 'created');
26870 } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
26871 addEntity(change.base, base, 'deleted');
26875 return d3.values(relevant);
26878 difference.complete = function(extent) {
26879 var result = {}, id, change;
26881 for (id in changes) {
26882 change = changes[id];
26884 var h = change.head,
26889 (!h || !h.intersects(extent, head)) &&
26890 (!b || !b.intersects(extent, base)))
26895 if (entity.type === 'way') {
26896 var nh = h ? h.nodes : [],
26897 nb = b ? b.nodes : [],
26900 diff = _.difference(nh, nb);
26901 for (i = 0; i < diff.length; i++) {
26902 result[diff[i]] = head.hasEntity(diff[i]);
26905 diff = _.difference(nb, nh);
26906 for (i = 0; i < diff.length; i++) {
26907 result[diff[i]] = head.hasEntity(diff[i]);
26911 addParents(head.parentWays(entity), result);
26912 addParents(head.parentRelations(entity), result);
26920 iD.Entity = function(attrs) {
26921 // For prototypal inheritance.
26922 if (this instanceof iD.Entity) return;
26924 // Create the appropriate subtype.
26925 if (attrs && attrs.type) {
26926 return iD.Entity[attrs.type].apply(this, arguments);
26927 } else if (attrs && attrs.id) {
26928 return iD.Entity[iD.Entity.id.type(attrs.id)].apply(this, arguments);
26931 // Initialize a generic Entity (used only in tests).
26932 return (new iD.Entity()).initialize(arguments);
26935 iD.Entity.id = function(type) {
26936 return iD.Entity.id.fromOSM(type, iD.Entity.id.next[type]--);
26939 iD.Entity.id.next = {node: -1, way: -1, relation: -1};
26941 iD.Entity.id.fromOSM = function(type, id) {
26942 return type[0] + id;
26945 iD.Entity.id.toOSM = function(id) {
26946 return id.slice(1);
26949 iD.Entity.id.type = function(id) {
26950 return {'n': 'node', 'w': 'way', 'r': 'relation'}[id[0]];
26953 // A function suitable for use as the second argument to d3.selection#data().
26954 iD.Entity.key = function(entity) {
26955 return entity.id + 'v' + (entity.v || 0);
26958 iD.Entity.prototype = {
26961 initialize: function(sources) {
26962 for (var i = 0; i < sources.length; ++i) {
26963 var source = sources[i];
26964 for (var prop in source) {
26965 if (Object.prototype.hasOwnProperty.call(source, prop)) {
26966 if (source[prop] === undefined) {
26969 this[prop] = source[prop];
26975 if (!this.id && this.type) {
26976 this.id = iD.Entity.id(this.type);
26978 if (!this.hasOwnProperty('visible')) {
26979 this.visible = true;
26983 Object.freeze(this);
26984 Object.freeze(this.tags);
26986 if (this.loc) Object.freeze(this.loc);
26987 if (this.nodes) Object.freeze(this.nodes);
26988 if (this.members) Object.freeze(this.members);
26994 copy: function(resolver, copies) {
26995 if (copies[this.id])
26996 return copies[this.id];
26998 var copy = iD.Entity(this, {id: undefined, user: undefined, version: undefined});
26999 copies[this.id] = copy;
27004 osmId: function() {
27005 return iD.Entity.id.toOSM(this.id);
27008 isNew: function() {
27009 return this.osmId() < 0;
27012 update: function(attrs) {
27013 return iD.Entity(this, attrs, {v: 1 + (this.v || 0)});
27016 mergeTags: function(tags) {
27017 var merged = _.clone(this.tags), changed = false;
27018 for (var k in tags) {
27019 var t1 = merged[k],
27024 } else if (t1 !== t2) {
27026 merged[k] = _.union(t1.split(/;\s*/), t2.split(/;\s*/)).join(';');
27029 return changed ? this.update({tags: merged}) : this;
27032 intersects: function(extent, resolver) {
27033 return this.extent(resolver).intersects(extent);
27036 isUsed: function(resolver) {
27037 return _.without(Object.keys(this.tags), 'area').length > 0 ||
27038 resolver.parentRelations(this).length > 0;
27041 hasInterestingTags: function() {
27042 return _.keys(this.tags).some(iD.interestingTag);
27045 isHighwayIntersection: function() {
27049 deprecatedTags: function() {
27050 var tags = _.pairs(this.tags);
27051 var deprecated = {};
27053 iD.data.deprecated.forEach(function(d) {
27054 var match = _.pairs(d.old)[0];
27055 tags.forEach(function(t) {
27056 if (t[0] === match[0] &&
27057 (t[1] === match[1] || match[1] === '*')) {
27058 deprecated[t[0]] = t[1];
27066 iD.Graph = function(other, mutable) {
27067 if (!(this instanceof iD.Graph)) return new iD.Graph(other, mutable);
27069 if (other instanceof iD.Graph) {
27070 var base = other.base();
27071 this.entities = _.assign(Object.create(base.entities), other.entities);
27072 this._parentWays = _.assign(Object.create(base.parentWays), other._parentWays);
27073 this._parentRels = _.assign(Object.create(base.parentRels), other._parentRels);
27076 this.entities = Object.create({});
27077 this._parentWays = Object.create({});
27078 this._parentRels = Object.create({});
27079 this.rebase(other || [], [this]);
27082 this.transients = {};
27083 this._childNodes = {};
27084 this.frozen = !mutable;
27087 iD.Graph.prototype = {
27088 hasEntity: function(id) {
27089 return this.entities[id];
27092 entity: function(id) {
27093 var entity = this.entities[id];
27095 throw new Error('entity ' + id + ' not found');
27100 transient: function(entity, key, fn) {
27101 var id = entity.id,
27102 transients = this.transients[id] ||
27103 (this.transients[id] = {});
27105 if (transients[key] !== undefined) {
27106 return transients[key];
27109 transients[key] = fn.call(entity);
27111 return transients[key];
27114 parentWays: function(entity) {
27115 var parents = this._parentWays[entity.id],
27119 for (var i = 0; i < parents.length; i++) {
27120 result.push(this.entity(parents[i]));
27126 isPoi: function(entity) {
27127 var parentWays = this._parentWays[entity.id];
27128 return !parentWays || parentWays.length === 0;
27131 isShared: function(entity) {
27132 var parentWays = this._parentWays[entity.id];
27133 return parentWays && parentWays.length > 1;
27136 parentRelations: function(entity) {
27137 var parents = this._parentRels[entity.id],
27141 for (var i = 0; i < parents.length; i++) {
27142 result.push(this.entity(parents[i]));
27148 childNodes: function(entity) {
27149 if (this._childNodes[entity.id]) return this._childNodes[entity.id];
27150 if (!entity.nodes) return [];
27153 for (var i = 0; i < entity.nodes.length; i++) {
27154 nodes[i] = this.entity(entity.nodes[i]);
27157 if (iD.debug) Object.freeze(nodes);
27159 this._childNodes[entity.id] = nodes;
27160 return this._childNodes[entity.id];
27165 'entities': iD.util.getPrototypeOf(this.entities),
27166 'parentWays': iD.util.getPrototypeOf(this._parentWays),
27167 'parentRels': iD.util.getPrototypeOf(this._parentRels)
27171 // Unlike other graph methods, rebase mutates in place. This is because it
27172 // is used only during the history operation that merges newly downloaded
27173 // data into each state. To external consumers, it should appear as if the
27174 // graph always contained the newly downloaded data.
27175 rebase: function(entities, stack, force) {
27176 var base = this.base(),
27179 for (i = 0; i < entities.length; i++) {
27180 var entity = entities[i];
27182 if (!entity.visible || (!force && base.entities[entity.id]))
27185 // Merging data into the base graph
27186 base.entities[entity.id] = entity;
27187 this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
27189 // Restore provisionally-deleted nodes that are discovered to have an extant parent
27190 if (entity.type === 'way') {
27191 for (j = 0; j < entity.nodes.length; j++) {
27192 id = entity.nodes[j];
27193 for (k = 1; k < stack.length; k++) {
27194 var ents = stack[k].entities;
27195 if (ents.hasOwnProperty(id) && ents[id] === undefined) {
27203 for (i = 0; i < stack.length; i++) {
27204 stack[i]._updateRebased();
27208 _updateRebased: function() {
27209 var base = this.base(),
27210 i, k, child, id, keys;
27212 keys = Object.keys(this._parentWays);
27213 for (i = 0; i < keys.length; i++) {
27215 if (base.parentWays[child]) {
27216 for (k = 0; k < base.parentWays[child].length; k++) {
27217 id = base.parentWays[child][k];
27218 if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentWays[child], id)) {
27219 this._parentWays[child].push(id);
27225 keys = Object.keys(this._parentRels);
27226 for (i = 0; i < keys.length; i++) {
27228 if (base.parentRels[child]) {
27229 for (k = 0; k < base.parentRels[child].length; k++) {
27230 id = base.parentRels[child][k];
27231 if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentRels[child], id)) {
27232 this._parentRels[child].push(id);
27238 this.transients = {};
27240 // this._childNodes is not updated, under the assumption that
27241 // ways are always downloaded with their child nodes.
27244 // Updates calculated properties (parentWays, parentRels) for the specified change
27245 _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
27247 parentWays = parentWays || this._parentWays;
27248 parentRels = parentRels || this._parentRels;
27250 var type = entity && entity.type || oldentity && oldentity.type,
27251 removed, added, ways, rels, i;
27254 if (type === 'way') {
27256 // Update parentWays
27257 if (oldentity && entity) {
27258 removed = _.difference(oldentity.nodes, entity.nodes);
27259 added = _.difference(entity.nodes, oldentity.nodes);
27260 } else if (oldentity) {
27261 removed = oldentity.nodes;
27263 } else if (entity) {
27265 added = entity.nodes;
27267 for (i = 0; i < removed.length; i++) {
27268 parentWays[removed[i]] = _.without(parentWays[removed[i]], oldentity.id);
27270 for (i = 0; i < added.length; i++) {
27271 ways = _.without(parentWays[added[i]], entity.id);
27272 ways.push(entity.id);
27273 parentWays[added[i]] = ways;
27276 } else if (type === 'relation') {
27278 // Update parentRels
27279 if (oldentity && entity) {
27280 removed = _.difference(oldentity.members, entity.members);
27281 added = _.difference(entity.members, oldentity);
27282 } else if (oldentity) {
27283 removed = oldentity.members;
27285 } else if (entity) {
27287 added = entity.members;
27289 for (i = 0; i < removed.length; i++) {
27290 parentRels[removed[i].id] = _.without(parentRels[removed[i].id], oldentity.id);
27292 for (i = 0; i < added.length; i++) {
27293 rels = _.without(parentRels[added[i].id], entity.id);
27294 rels.push(entity.id);
27295 parentRels[added[i].id] = rels;
27300 replace: function(entity) {
27301 if (this.entities[entity.id] === entity)
27304 return this.update(function() {
27305 this._updateCalculated(this.entities[entity.id], entity);
27306 this.entities[entity.id] = entity;
27310 remove: function(entity) {
27311 return this.update(function() {
27312 this._updateCalculated(entity, undefined);
27313 this.entities[entity.id] = undefined;
27317 revert: function(id) {
27318 var baseEntity = this.base().entities[id],
27319 headEntity = this.entities[id];
27321 if (headEntity === baseEntity)
27324 return this.update(function() {
27325 this._updateCalculated(headEntity, baseEntity);
27326 delete this.entities[id];
27330 update: function() {
27331 var graph = this.frozen ? iD.Graph(this, true) : this;
27333 for (var i = 0; i < arguments.length; i++) {
27334 arguments[i].call(graph, graph);
27337 if (this.frozen) graph.frozen = true;
27342 // Obliterates any existing entities
27343 load: function(entities) {
27344 var base = this.base();
27345 this.entities = Object.create(base.entities);
27347 for (var i in entities) {
27348 this.entities[i] = entities[i];
27349 this._updateCalculated(base.entities[i], this.entities[i]);
27355 iD.History = function(context) {
27356 var stack, index, tree,
27357 imageryUsed = ['Bing'],
27358 dispatch = d3.dispatch('change', 'undone', 'redone'),
27359 lock = iD.util.SessionMutex('lock');
27361 function perform(actions) {
27362 actions = Array.prototype.slice.call(actions);
27366 if (!_.isFunction(_.last(actions))) {
27367 annotation = actions.pop();
27370 var graph = stack[index].graph;
27371 for (var i = 0; i < actions.length; i++) {
27372 graph = actions[i](graph);
27377 annotation: annotation,
27378 imageryUsed: imageryUsed
27382 function change(previous) {
27383 var difference = iD.Difference(previous, history.graph());
27384 dispatch.change(difference);
27388 // iD uses namespaced keys so multiple installations do not conflict
27389 function getKey(n) {
27390 return 'iD_' + window.location.origin + '_' + n;
27394 graph: function() {
27395 return stack[index].graph;
27399 return stack[0].graph;
27402 merge: function(entities, extent) {
27403 stack[0].graph.rebase(entities, _.pluck(stack, 'graph'), false);
27404 tree.rebase(entities, false);
27406 dispatch.change(undefined, extent);
27409 perform: function() {
27410 var previous = stack[index].graph;
27412 stack = stack.slice(0, index + 1);
27413 stack.push(perform(arguments));
27416 return change(previous);
27419 replace: function() {
27420 var previous = stack[index].graph;
27422 // assert(index == stack.length - 1)
27423 stack[index] = perform(arguments);
27425 return change(previous);
27429 var previous = stack[index].graph;
27434 return change(previous);
27438 // Same as calling pop and then perform
27439 overwrite: function() {
27440 var previous = stack[index].graph;
27446 stack = stack.slice(0, index + 1);
27447 stack.push(perform(arguments));
27450 return change(previous);
27454 var previous = stack[index].graph;
27456 // Pop to the next annotated state.
27457 while (index > 0) {
27459 if (stack[index].annotation) break;
27463 return change(previous);
27467 var previous = stack[index].graph;
27469 while (index < stack.length - 1) {
27471 if (stack[index].annotation) break;
27475 return change(previous);
27478 undoAnnotation: function() {
27481 if (stack[i].annotation) return stack[i].annotation;
27486 redoAnnotation: function() {
27488 while (i <= stack.length - 1) {
27489 if (stack[i].annotation) return stack[i].annotation;
27494 intersects: function(extent) {
27495 return tree.intersects(extent, stack[index].graph);
27498 difference: function() {
27499 var base = stack[0].graph,
27500 head = stack[index].graph;
27501 return iD.Difference(base, head);
27504 changes: function(action) {
27505 var base = stack[0].graph,
27506 head = stack[index].graph;
27509 head = action(head);
27512 var difference = iD.Difference(base, head);
27515 modified: difference.modified(),
27516 created: difference.created(),
27517 deleted: difference.deleted()
27521 validate: function(changes) {
27522 return _(iD.validations)
27523 .map(function(fn) { return fn()(changes, stack[index].graph); })
27528 hasChanges: function() {
27529 return this.difference().length() > 0;
27532 imageryUsed: function(sources) {
27534 imageryUsed = sources;
27537 return _(stack.slice(1, index + 1))
27538 .pluck('imageryUsed')
27541 .without(undefined, 'Custom')
27546 reset: function() {
27547 stack = [{graph: iD.Graph()}];
27549 tree = iD.Tree(stack[0].graph);
27554 toJSON: function() {
27555 if (!this.hasChanges()) return;
27557 var allEntities = {},
27561 var s = stack.map(function(i) {
27562 var modified = [], deleted = [];
27564 _.forEach(i.graph.entities, function(entity, id) {
27566 var key = iD.Entity.key(entity);
27567 allEntities[key] = entity;
27568 modified.push(key);
27573 // make sure that the originals of changed or deleted entities get merged
27574 // into the base of the stack after restoring the data from JSON.
27575 if (id in base.graph.entities) {
27576 baseEntities[id] = base.graph.entities[id];
27578 // get originals of parent entities too
27579 _.forEach(base.graph._parentWays[id], function(parentId) {
27580 if (parentId in base.graph.entities) {
27581 baseEntities[parentId] = base.graph.entities[parentId];
27588 if (modified.length) x.modified = modified;
27589 if (deleted.length) x.deleted = deleted;
27590 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
27591 if (i.annotation) x.annotation = i.annotation;
27596 return JSON.stringify({
27598 entities: _.values(allEntities),
27599 baseEntities: _.values(baseEntities),
27601 nextIDs: iD.Entity.id.next,
27606 fromJSON: function(json, loadChildNodes) {
27607 var h = JSON.parse(json),
27608 loadComplete = true;
27610 iD.Entity.id.next = h.nextIDs;
27613 if (h.version === 2 || h.version === 3) {
27614 var allEntities = {};
27616 h.entities.forEach(function(entity) {
27617 allEntities[iD.Entity.key(entity)] = iD.Entity(entity);
27620 if (h.version === 3) {
27621 // This merges originals for changed entities into the base of
27622 // the stack even if the current stack doesn't have them (for
27623 // example when iD has been restarted in a different region)
27624 var baseEntities = h.baseEntities.map(function(d) { return iD.Entity(d); });
27625 stack[0].graph.rebase(baseEntities, _.pluck(stack, 'graph'), true);
27626 tree.rebase(baseEntities, true);
27628 // When we restore a modified way, we also need to fetch any missing
27629 // childnodes that would normally have been downloaded with it.. #2142
27630 if (loadChildNodes) {
27631 var missing = _(baseEntities)
27632 .filter('type', 'way')
27636 .reject(function(n) { return stack[0].graph.hasEntity(n); })
27639 if (!_.isEmpty(missing)) {
27640 loadComplete = false;
27641 context.redrawEnable(false);
27643 var loading = iD.ui.Loading(context).blocking(true);
27644 context.container().call(loading);
27646 var childNodesLoaded = function(err, result) {
27648 var visible = _.groupBy(result.data, 'visible');
27649 if (!_.isEmpty(visible.true)) {
27650 missing = _.difference(missing, _.pluck(visible.true, 'id'));
27651 stack[0].graph.rebase(visible.true, _.pluck(stack, 'graph'), true);
27652 tree.rebase(visible.true, true);
27655 // fetch older versions of nodes that were deleted..
27656 _.each(visible.false, function(entity) {
27657 context.connection()
27658 .loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
27662 if (err || _.isEmpty(missing)) {
27664 context.redrawEnable(true);
27669 context.connection().loadMultiple(missing, childNodesLoaded);
27674 stack = h.stack.map(function(d) {
27675 var entities = {}, entity;
27678 d.modified.forEach(function(key) {
27679 entity = allEntities[key];
27680 entities[entity.id] = entity;
27685 d.deleted.forEach(function(id) {
27686 entities[id] = undefined;
27691 graph: iD.Graph(stack[0].graph).load(entities),
27692 annotation: d.annotation,
27693 imageryUsed: d.imageryUsed
27697 } else { // original version
27698 stack = h.stack.map(function(d) {
27701 for (var i in d.entities) {
27702 var entity = d.entities[i];
27703 entities[i] = entity === 'undefined' ? undefined : iD.Entity(entity);
27706 d.graph = iD.Graph(stack[0].graph).load(entities);
27711 if (loadComplete) {
27719 if (lock.locked()) context.storage(getKey('saved_history'), history.toJSON() || null);
27723 clearSaved: function() {
27724 context.debouncedSave.cancel();
27725 if (lock.locked()) context.storage(getKey('saved_history'), null);
27730 return lock.lock();
27733 unlock: function() {
27737 // is iD not open in another window and it detects that
27738 // there's a history stored in localStorage that's recoverable?
27739 restorableChanges: function() {
27740 return lock.locked() && !!context.storage(getKey('saved_history'));
27743 // load history from a version stored in localStorage
27744 restore: function() {
27745 if (!lock.locked()) return;
27747 var json = context.storage(getKey('saved_history'));
27748 if (json) history.fromJSON(json, true);
27757 return d3.rebind(history, dispatch, 'on');
27759 iD.Node = iD.Entity.node = function iD_Node() {
27760 if (!(this instanceof iD_Node)) {
27761 return (new iD_Node()).initialize(arguments);
27762 } else if (arguments.length) {
27763 this.initialize(arguments);
27767 iD.Node.prototype = Object.create(iD.Entity.prototype);
27769 _.extend(iD.Node.prototype, {
27772 extent: function() {
27773 return new iD.geo.Extent(this.loc);
27776 geometry: function(graph) {
27777 return graph.transient(this, 'geometry', function() {
27778 return graph.isPoi(this) ? 'point' : 'vertex';
27782 move: function(loc) {
27783 return this.update({loc: loc});
27786 isIntersection: function(resolver) {
27787 return resolver.transient(this, 'isIntersection', function() {
27788 return resolver.parentWays(this).filter(function(parent) {
27789 return (parent.tags.highway ||
27790 parent.tags.waterway ||
27791 parent.tags.railway ||
27792 parent.tags.aeroway) &&
27793 parent.geometry(resolver) === 'line';
27798 isHighwayIntersection: function(resolver) {
27799 return resolver.transient(this, 'isHighwayIntersection', function() {
27800 return resolver.parentWays(this).filter(function(parent) {
27801 return parent.tags.highway && parent.geometry(resolver) === 'line';
27806 asJXON: function(changeset_id) {
27809 '@id': this.osmId(),
27810 '@lon': this.loc[0],
27811 '@lat': this.loc[1],
27812 '@version': (this.version || 0),
27813 tag: _.map(this.tags, function(v, k) {
27814 return { keyAttributes: { k: k, v: v } };
27818 if (changeset_id) r.node['@changeset'] = changeset_id;
27822 asGeoJSON: function() {
27825 coordinates: this.loc
27829 iD.Relation = iD.Entity.relation = function iD_Relation() {
27830 if (!(this instanceof iD_Relation)) {
27831 return (new iD_Relation()).initialize(arguments);
27832 } else if (arguments.length) {
27833 this.initialize(arguments);
27837 iD.Relation.prototype = Object.create(iD.Entity.prototype);
27839 iD.Relation.creationOrder = function(a, b) {
27840 var aId = parseInt(iD.Entity.id.toOSM(a.id), 10);
27841 var bId = parseInt(iD.Entity.id.toOSM(b.id), 10);
27843 if (aId < 0 || bId < 0) return aId - bId;
27847 _.extend(iD.Relation.prototype, {
27851 copy: function(resolver, copies) {
27852 if (copies[this.id])
27853 return copies[this.id];
27855 var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
27857 var members = this.members.map(function(member) {
27858 return _.extend({}, member, {id: resolver.entity(member.id).copy(resolver, copies).id});
27861 copy = copy.update({members: members});
27862 copies[this.id] = copy;
27867 extent: function(resolver, memo) {
27868 return resolver.transient(this, 'extent', function() {
27869 if (memo && memo[this.id]) return iD.geo.Extent();
27871 memo[this.id] = true;
27873 var extent = iD.geo.Extent();
27874 for (var i = 0; i < this.members.length; i++) {
27875 var member = resolver.hasEntity(this.members[i].id);
27877 extent._extend(member.extent(resolver, memo));
27884 geometry: function(graph) {
27885 return graph.transient(this, 'geometry', function() {
27886 return this.isMultipolygon() ? 'area' : 'relation';
27890 isDegenerate: function() {
27891 return this.members.length === 0;
27894 // Return an array of members, each extended with an 'index' property whose value
27895 // is the member index.
27896 indexedMembers: function() {
27897 var result = new Array(this.members.length);
27898 for (var i = 0; i < this.members.length; i++) {
27899 result[i] = _.extend({}, this.members[i], {index: i});
27904 // Return the first member with the given role. A copy of the member object
27905 // is returned, extended with an 'index' property whose value is the member index.
27906 memberByRole: function(role) {
27907 for (var i = 0; i < this.members.length; i++) {
27908 if (this.members[i].role === role) {
27909 return _.extend({}, this.members[i], {index: i});
27914 // Return the first member with the given id. A copy of the member object
27915 // is returned, extended with an 'index' property whose value is the member index.
27916 memberById: function(id) {
27917 for (var i = 0; i < this.members.length; i++) {
27918 if (this.members[i].id === id) {
27919 return _.extend({}, this.members[i], {index: i});
27924 // Return the first member with the given id and role. A copy of the member object
27925 // is returned, extended with an 'index' property whose value is the member index.
27926 memberByIdAndRole: function(id, role) {
27927 for (var i = 0; i < this.members.length; i++) {
27928 if (this.members[i].id === id && this.members[i].role === role) {
27929 return _.extend({}, this.members[i], {index: i});
27934 addMember: function(member, index) {
27935 var members = this.members.slice();
27936 members.splice(index === undefined ? members.length : index, 0, member);
27937 return this.update({members: members});
27940 updateMember: function(member, index) {
27941 var members = this.members.slice();
27942 members.splice(index, 1, _.extend({}, members[index], member));
27943 return this.update({members: members});
27946 removeMember: function(index) {
27947 var members = this.members.slice();
27948 members.splice(index, 1);
27949 return this.update({members: members});
27952 removeMembersWithID: function(id) {
27953 var members = _.reject(this.members, function(m) { return m.id === id; });
27954 return this.update({members: members});
27957 // Wherever a member appears with id `needle.id`, replace it with a member
27958 // with id `replacement.id`, type `replacement.type`, and the original role,
27959 // unless a member already exists with that id and role. Return an updated
27961 replaceMember: function(needle, replacement) {
27962 if (!this.memberById(needle.id))
27967 for (var i = 0; i < this.members.length; i++) {
27968 var member = this.members[i];
27969 if (member.id !== needle.id) {
27970 members.push(member);
27971 } else if (!this.memberByIdAndRole(replacement.id, member.role)) {
27972 members.push({id: replacement.id, type: replacement.type, role: member.role});
27976 return this.update({members: members});
27979 asJXON: function(changeset_id) {
27982 '@id': this.osmId(),
27983 '@version': this.version || 0,
27984 member: _.map(this.members, function(member) {
27985 return { keyAttributes: { type: member.type, role: member.role, ref: iD.Entity.id.toOSM(member.id) } };
27987 tag: _.map(this.tags, function(v, k) {
27988 return { keyAttributes: { k: k, v: v } };
27992 if (changeset_id) r.relation['@changeset'] = changeset_id;
27996 asGeoJSON: function(resolver) {
27997 return resolver.transient(this, 'GeoJSON', function () {
27998 if (this.isMultipolygon()) {
28000 type: 'MultiPolygon',
28001 coordinates: this.multipolygon(resolver)
28005 type: 'FeatureCollection',
28006 properties: this.tags,
28007 features: this.members.map(function (member) {
28008 return _.extend({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
28015 area: function(resolver) {
28016 return resolver.transient(this, 'area', function() {
28017 return d3.geo.area(this.asGeoJSON(resolver));
28021 isMultipolygon: function() {
28022 return this.tags.type === 'multipolygon';
28025 isComplete: function(resolver) {
28026 for (var i = 0; i < this.members.length; i++) {
28027 if (!resolver.hasEntity(this.members[i].id)) {
28034 isRestriction: function() {
28035 return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
28038 // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
28039 // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
28041 // This corresponds to the structure needed for rendering a multipolygon path using a
28042 // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
28044 // In the case of invalid geometries, this function will still return a result which
28045 // includes the nodes of all way members, but some Nds may be unclosed and some inner
28046 // rings not matched with the intended outer ring.
28048 multipolygon: function(resolver) {
28049 var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
28050 inners = this.members.filter(function(m) { return 'inner' === m.role; });
28052 outers = iD.geo.joinWays(outers, resolver);
28053 inners = iD.geo.joinWays(inners, resolver);
28055 outers = outers.map(function(outer) { return _.pluck(outer.nodes, 'loc'); });
28056 inners = inners.map(function(inner) { return _.pluck(inner.nodes, 'loc'); });
28058 var result = outers.map(function(o) {
28059 // Heuristic for detecting counterclockwise winding order. Assumes
28060 // that OpenStreetMap polygons are not hemisphere-spanning.
28061 return [d3.geo.area({type: 'Polygon', coordinates: [o]}) > 2 * Math.PI ? o.reverse() : o];
28064 function findOuter(inner) {
28067 for (o = 0; o < outers.length; o++) {
28069 if (iD.geo.polygonContainsPolygon(outer, inner))
28073 for (o = 0; o < outers.length; o++) {
28075 if (iD.geo.polygonIntersectsPolygon(outer, inner))
28080 for (var i = 0; i < inners.length; i++) {
28081 var inner = inners[i];
28083 if (d3.geo.area({type: 'Polygon', coordinates: [inner]}) < 2 * Math.PI) {
28084 inner = inner.reverse();
28087 var o = findOuter(inners[i]);
28088 if (o !== undefined)
28089 result[o].push(inners[i]);
28091 result.push([inners[i]]); // Invalid geometry
28099 'chair_lift': true,
28100 'mixed_lift': true,
28105 'magic_carpet': true,
28110 'motorway_link': true
28116 'piste:halfpipe': true
28140 iD.interestingTag = function (key) {
28141 return key !== 'attribution' &&
28142 key !== 'created_by' &&
28143 key !== 'source' &&
28145 key.indexOf('tiger:') !== 0;
28148 iD.Tree = function(head) {
28149 var rtree = rbush(),
28152 function entityRectangle(entity) {
28153 var rect = entity.extent(head).rectangle();
28154 rect.id = entity.id;
28155 rectangles[entity.id] = rect;
28159 function updateParents(entity, insertions, memo) {
28160 head.parentWays(entity).forEach(function(way) {
28161 if (rectangles[way.id]) {
28162 rtree.remove(rectangles[way.id]);
28163 insertions[way.id] = way;
28165 updateParents(way, insertions, memo);
28168 head.parentRelations(entity).forEach(function(relation) {
28169 if (memo[entity.id]) return;
28170 memo[entity.id] = true;
28171 if (rectangles[relation.id]) {
28172 rtree.remove(rectangles[relation.id]);
28173 insertions[relation.id] = relation;
28175 updateParents(relation, insertions, memo);
28181 tree.rebase = function(entities, force) {
28182 var insertions = {};
28184 for (var i = 0; i < entities.length; i++) {
28185 var entity = entities[i];
28187 if (!entity.visible)
28190 if (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id]) {
28193 } else if (rectangles[entity.id]) {
28194 rtree.remove(rectangles[entity.id]);
28198 insertions[entity.id] = entity;
28199 updateParents(entity, insertions, {});
28202 rtree.load(_.map(insertions, entityRectangle));
28207 tree.intersects = function(extent, graph) {
28208 if (graph !== head) {
28209 var diff = iD.Difference(head, graph),
28214 diff.deleted().forEach(function(entity) {
28215 rtree.remove(rectangles[entity.id]);
28216 delete rectangles[entity.id];
28219 diff.modified().forEach(function(entity) {
28220 rtree.remove(rectangles[entity.id]);
28221 insertions[entity.id] = entity;
28222 updateParents(entity, insertions, {});
28225 diff.created().forEach(function(entity) {
28226 insertions[entity.id] = entity;
28229 rtree.load(_.map(insertions, entityRectangle));
28232 return rtree.search(extent.rectangle()).map(function(rect) {
28233 return head.entity(rect.id);
28239 iD.Way = iD.Entity.way = function iD_Way() {
28240 if (!(this instanceof iD_Way)) {
28241 return (new iD_Way()).initialize(arguments);
28242 } else if (arguments.length) {
28243 this.initialize(arguments);
28247 iD.Way.prototype = Object.create(iD.Entity.prototype);
28249 _.extend(iD.Way.prototype, {
28253 copy: function(resolver, copies) {
28254 if (copies[this.id])
28255 return copies[this.id];
28257 var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
28259 var nodes = this.nodes.map(function(id) {
28260 return resolver.entity(id).copy(resolver, copies).id;
28263 copy = copy.update({nodes: nodes});
28264 copies[this.id] = copy;
28269 extent: function(resolver) {
28270 return resolver.transient(this, 'extent', function() {
28271 var extent = iD.geo.Extent();
28272 for (var i = 0; i < this.nodes.length; i++) {
28273 var node = resolver.hasEntity(this.nodes[i]);
28275 extent._extend(node.extent());
28282 first: function() {
28283 return this.nodes[0];
28287 return this.nodes[this.nodes.length - 1];
28290 contains: function(node) {
28291 return this.nodes.indexOf(node) >= 0;
28294 affix: function(node) {
28295 if (this.nodes[0] === node) return 'prefix';
28296 if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
28299 layer: function() {
28300 // explicit layer tag, clamp between -10, 10..
28301 if (this.tags.layer !== undefined) {
28302 return Math.max(-10, Math.min(+(this.tags.layer), 10));
28305 // implied layer tag..
28306 if (this.tags.location === 'overground') return 1;
28307 if (this.tags.location === 'underground') return -1;
28308 if (this.tags.location === 'underwater') return -10;
28310 if (this.tags.power === 'line') return 10;
28311 if (this.tags.power === 'minor_line') return 10;
28312 if (this.tags.aerialway) return 10;
28313 if (this.tags.bridge) return 1;
28314 if (this.tags.cutting) return -1;
28315 if (this.tags.tunnel) return -1;
28316 if (this.tags.waterway) return -1;
28317 if (this.tags.man_made === 'pipeline') return -10;
28318 if (this.tags.boundary) return -10;
28322 isOneWay: function() {
28323 // explicit oneway tag..
28324 if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; }
28325 if (['no', '0'].indexOf(this.tags.oneway) !== -1) { return false; }
28327 // implied oneway tag..
28328 for (var key in this.tags) {
28329 if (key in iD.oneWayTags && (this.tags[key] in iD.oneWayTags[key]))
28335 isClosed: function() {
28336 return this.nodes.length > 0 && this.first() === this.last();
28339 isConvex: function(resolver) {
28340 if (!this.isClosed() || this.isDegenerate()) return null;
28342 var nodes = _.uniq(resolver.childNodes(this)),
28343 coords = _.pluck(nodes, 'loc'),
28344 curr = 0, prev = 0;
28346 for (var i = 0; i < coords.length; i++) {
28347 var o = coords[(i+1) % coords.length],
28349 b = coords[(i+2) % coords.length],
28350 res = iD.geo.cross(o, a, b);
28352 curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
28355 } else if (prev && curr !== prev) {
28363 isArea: function() {
28364 if (this.tags.area === 'yes')
28366 if (!this.isClosed() || this.tags.area === 'no')
28368 for (var key in this.tags)
28369 if (key in iD.areaKeys && !(this.tags[key] in iD.areaKeys[key]))
28374 isDegenerate: function() {
28375 return _.uniq(this.nodes).length < (this.isArea() ? 3 : 2);
28378 areAdjacent: function(n1, n2) {
28379 for (var i = 0; i < this.nodes.length; i++) {
28380 if (this.nodes[i] === n1) {
28381 if (this.nodes[i - 1] === n2) return true;
28382 if (this.nodes[i + 1] === n2) return true;
28388 geometry: function(graph) {
28389 return graph.transient(this, 'geometry', function() {
28390 return this.isArea() ? 'area' : 'line';
28394 addNode: function(id, index) {
28395 var nodes = this.nodes.slice();
28396 nodes.splice(index === undefined ? nodes.length : index, 0, id);
28397 return this.update({nodes: nodes});
28400 updateNode: function(id, index) {
28401 var nodes = this.nodes.slice();
28402 nodes.splice(index, 1, id);
28403 return this.update({nodes: nodes});
28406 replaceNode: function(needle, replacement) {
28407 if (this.nodes.indexOf(needle) < 0)
28410 var nodes = this.nodes.slice();
28411 for (var i = 0; i < nodes.length; i++) {
28412 if (nodes[i] === needle) {
28413 nodes[i] = replacement;
28416 return this.update({nodes: nodes});
28419 removeNode: function(id) {
28422 for (var i = 0; i < this.nodes.length; i++) {
28423 var node = this.nodes[i];
28424 if (node !== id && nodes[nodes.length - 1] !== node) {
28429 // Preserve circularity
28430 if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] !== nodes[0]) {
28431 nodes.push(nodes[0]);
28434 return this.update({nodes: nodes});
28437 asJXON: function(changeset_id) {
28440 '@id': this.osmId(),
28441 '@version': this.version || 0,
28442 nd: _.map(this.nodes, function(id) {
28443 return { keyAttributes: { ref: iD.Entity.id.toOSM(id) } };
28445 tag: _.map(this.tags, function(v, k) {
28446 return { keyAttributes: { k: k, v: v } };
28450 if (changeset_id) r.way['@changeset'] = changeset_id;
28454 asGeoJSON: function(resolver) {
28455 return resolver.transient(this, 'GeoJSON', function() {
28456 var coordinates = _.pluck(resolver.childNodes(this), 'loc');
28457 if (this.isArea() && this.isClosed()) {
28460 coordinates: [coordinates]
28464 type: 'LineString',
28465 coordinates: coordinates
28471 area: function(resolver) {
28472 return resolver.transient(this, 'area', function() {
28473 var nodes = resolver.childNodes(this);
28477 coordinates: [_.pluck(nodes, 'loc')]
28480 if (!this.isClosed() && nodes.length) {
28481 json.coordinates[0].push(nodes[0].loc);
28484 var area = d3.geo.area(json);
28486 // Heuristic for detecting counterclockwise winding order. Assumes
28487 // that OpenStreetMap polygons are not hemisphere-spanning.
28488 if (area > 2 * Math.PI) {
28489 json.coordinates[0] = json.coordinates[0].reverse();
28490 area = d3.geo.area(json);
28493 return isNaN(area) ? 0 : area;
28497 iD.Background = function(context) {
28498 var dispatch = d3.dispatch('change'),
28499 baseLayer = iD.TileLayer().projection(context.projection),
28500 overlayLayers = [],
28504 function findSource(id) {
28505 return _.find(backgroundSources, function(d) {
28506 return d.id && d.id === id;
28511 function background(selection) {
28512 var base = selection.selectAll('.layer-background')
28516 .insert('div', '.layer-data')
28517 .attr('class', 'layer layer-background');
28519 base.call(baseLayer);
28521 var overlays = selection.selectAll('.layer-overlay')
28522 .data(overlayLayers, function(d) { return d.source().name(); });
28525 .insert('div', '.layer-data')
28526 .attr('class', 'layer layer-overlay');
28528 overlays.each(function(layer) {
28529 d3.select(this).call(layer);
28537 background.updateImagery = function() {
28538 var b = background.baseLayerSource(),
28539 o = overlayLayers.map(function (d) { return d.source().id; }).join(','),
28540 meters = iD.geo.offsetToMeters(b.offset()),
28542 x = +meters[0].toFixed(2),
28543 y = +meters[1].toFixed(2),
28544 q = iD.util.stringQs(location.hash.substring(1));
28547 if (id === 'custom') {
28548 id = 'custom:' + b.template;
28554 delete q.background;
28563 if (Math.abs(x) > epsilon || Math.abs(y) > epsilon) {
28564 q.offset = x + ',' + y;
28569 location.replace('#' + iD.util.qsString(q, true));
28571 var imageryUsed = [b.imageryUsed()];
28573 overlayLayers.forEach(function (d) {
28574 var source = d.source();
28575 if (!source.isLocatorOverlay()) {
28576 imageryUsed.push(source.imageryUsed());
28580 var gpx = context.layers().layer('gpx');
28581 if (gpx && gpx.enabled() && gpx.hasGpx()) {
28582 imageryUsed.push('Local GPX');
28585 context.history().imageryUsed(imageryUsed);
28588 background.sources = function(extent) {
28589 return backgroundSources.filter(function(source) {
28590 return source.intersects(extent);
28594 background.dimensions = function(_) {
28595 baseLayer.dimensions(_);
28597 overlayLayers.forEach(function(layer) {
28598 layer.dimensions(_);
28602 background.baseLayerSource = function(d) {
28603 if (!arguments.length) return baseLayer.source();
28604 baseLayer.source(d);
28606 background.updateImagery();
28610 background.bing = function() {
28611 background.baseLayerSource(findSource('Bing'));
28614 background.showsLayer = function(d) {
28615 return d === baseLayer.source() ||
28616 (d.id === 'custom' && baseLayer.source().id === 'custom') ||
28617 overlayLayers.some(function(l) { return l.source() === d; });
28620 background.overlayLayerSources = function() {
28621 return overlayLayers.map(function (l) { return l.source(); });
28624 background.toggleOverlayLayer = function(d) {
28627 for (var i = 0; i < overlayLayers.length; i++) {
28628 layer = overlayLayers[i];
28629 if (layer.source() === d) {
28630 overlayLayers.splice(i, 1);
28632 background.updateImagery();
28637 layer = iD.TileLayer()
28639 .projection(context.projection)
28640 .dimensions(baseLayer.dimensions());
28642 overlayLayers.push(layer);
28644 background.updateImagery();
28647 background.nudge = function(d, zoom) {
28648 baseLayer.source().nudge(d, zoom);
28650 background.updateImagery();
28654 background.offset = function(d) {
28655 if (!arguments.length) return baseLayer.source().offset();
28656 baseLayer.source().offset(d);
28658 background.updateImagery();
28662 background.load = function(imagery) {
28663 function parseMap(qmap) {
28664 if (!qmap) return false;
28665 var args = qmap.split('/').map(Number);
28666 if (args.length < 3 || args.some(isNaN)) return false;
28667 return iD.geo.Extent([args[1], args[2]]);
28670 var q = iD.util.stringQs(location.hash.substring(1)),
28671 chosen = q.background || q.layer,
28672 extent = parseMap(q.map),
28675 backgroundSources = imagery.map(function(source) {
28676 if (source.type === 'bing') {
28677 return iD.BackgroundSource.Bing(source, dispatch);
28679 return iD.BackgroundSource(source);
28683 backgroundSources.unshift(iD.BackgroundSource.None());
28685 if (!chosen && extent) {
28686 best = _.find(this.sources(extent), function(s) { return s.best(); });
28689 if (chosen && chosen.indexOf('custom:') === 0) {
28690 background.baseLayerSource(iD.BackgroundSource.Custom(chosen.replace(/^custom:/, '')));
28692 background.baseLayerSource(findSource(chosen) || best || findSource('Bing') || backgroundSources[1] || backgroundSources[0]);
28695 var locator = _.find(backgroundSources, function(d) {
28696 return d.overlay && d.default;
28700 background.toggleOverlayLayer(locator);
28703 var overlays = (q.overlays || '').split(',');
28704 overlays.forEach(function(overlay) {
28705 overlay = findSource(overlay);
28707 background.toggleOverlayLayer(overlay);
28712 var gpx = context.layers().layer('gpx');
28719 var offset = q.offset.replace(/;/g, ',').split(',').map(function(n) {
28720 return !isNaN(n) && n;
28723 if (offset.length === 2) {
28724 background.offset(iD.geo.metersToOffset(offset));
28729 return d3.rebind(background, dispatch, 'on');
28731 iD.BackgroundSource = function(data) {
28732 var source = _.clone(data),
28734 name = source.name,
28735 best = !!source.best;
28737 source.scaleExtent = data.scaleExtent || [0, 20];
28738 source.overzoom = data.overzoom !== false;
28740 source.offset = function(_) {
28741 if (!arguments.length) return offset;
28746 source.nudge = function(_, zoomlevel) {
28747 offset[0] += _[0] / Math.pow(2, zoomlevel);
28748 offset[1] += _[1] / Math.pow(2, zoomlevel);
28752 source.name = function() {
28756 source.best = function() {
28760 source.imageryUsed = function() {
28761 return source.id || name;
28764 source.url = function(coord) {
28765 return data.template
28766 .replace('{x}', coord[0])
28767 .replace('{y}', coord[1])
28768 // TMS-flipped y coordinate
28769 .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
28770 .replace(/\{z(oom)?\}/, coord[2])
28771 .replace(/\{switch:([^}]+)\}/, function(s, r) {
28772 var subdomains = r.split(',');
28773 return subdomains[(coord[0] + coord[1]) % subdomains.length];
28775 .replace('{u}', function() {
28777 for (var zoom = coord[2]; zoom > 0; zoom--) {
28779 var mask = 1 << (zoom - 1);
28780 if ((coord[0] & mask) !== 0) b++;
28781 if ((coord[1] & mask) !== 0) b += 2;
28788 source.intersects = function(extent) {
28789 extent = extent.polygon();
28790 return !data.polygon || data.polygon.some(function(polygon) {
28791 return iD.geo.polygonIntersectsPolygon(polygon, extent, true);
28795 source.validZoom = function(z) {
28796 return source.scaleExtent[0] <= z &&
28797 (source.overzoom || source.scaleExtent[1] > z);
28800 source.isLocatorOverlay = function() {
28801 return name === 'Locator Overlay';
28804 source.copyrightNotices = function() {};
28809 iD.BackgroundSource.Bing = function(data, dispatch) {
28810 // http://msdn.microsoft.com/en-us/library/ff701716.aspx
28811 // http://msdn.microsoft.com/en-us/library/ff701701.aspx
28813 data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
28815 var bing = iD.BackgroundSource(data),
28816 key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU', // Same as P2 and JOSM
28817 url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' +
28818 key + '&jsonp={callback}',
28821 d3.jsonp(url, function(json) {
28822 providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
28824 attribution: provider.attribution,
28825 areas: provider.coverageAreas.map(function(area) {
28827 zoom: [area.zoomMin, area.zoomMax],
28828 extent: iD.geo.Extent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
28836 bing.copyrightNotices = function(zoom, extent) {
28837 zoom = Math.min(zoom, 21);
28838 return providers.filter(function(provider) {
28839 return _.any(provider.areas, function(area) {
28840 return extent.intersects(area.extent) &&
28841 area.zoom[0] <= zoom &&
28842 area.zoom[1] >= zoom;
28844 }).map(function(provider) {
28845 return provider.attribution;
28849 bing.logo = 'bing_maps.png';
28850 bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
28855 iD.BackgroundSource.None = function() {
28856 var source = iD.BackgroundSource({id: 'none', template: ''});
28858 source.name = function() {
28859 return t('background.none');
28862 source.imageryUsed = function() {
28869 iD.BackgroundSource.Custom = function(template) {
28870 var source = iD.BackgroundSource({id: 'custom', template: template});
28872 source.name = function() {
28873 return t('background.custom');
28876 source.imageryUsed = function() {
28877 return 'Custom (' + template + ')';
28882 iD.Features = function(context) {
28883 var traffic_roads = {
28885 'motorway_link': true,
28887 'trunk_link': true,
28889 'primary_link': true,
28891 'secondary_link': true,
28893 'tertiary_link': true,
28894 'residential': true,
28895 'unclassified': true,
28896 'living_street': true
28899 var service_roads = {
28911 'pedestrian': true,
28915 var past_futures = {
28917 'construction': true,
28919 'dismantled': true,
28922 'demolished': true,
28923 'obliterated': true
28926 var dispatch = d3.dispatch('change', 'redraw'),
28934 function update() {
28935 _hidden = features.hidden();
28940 function defineFeature(k, filter, max) {
28944 enabled: true, // whether the user wants it enabled..
28946 currentMax: (max || Infinity),
28947 defaultMax: (max || Infinity),
28948 enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
28949 disable: function() { this.enabled = false; this.currentMax = 0; },
28950 hidden: function() { return !context.editable() || this.count > this.currentMax * _cullFactor; },
28951 autoHidden: function() { return this.hidden() && this.currentMax > 0; }
28956 defineFeature('points', function isPoint(entity, resolver, geometry) {
28957 return geometry === 'point';
28960 defineFeature('traffic_roads', function isTrafficRoad(entity) {
28961 return traffic_roads[entity.tags.highway];
28964 defineFeature('service_roads', function isServiceRoad(entity) {
28965 return service_roads[entity.tags.highway];
28968 defineFeature('paths', function isPath(entity) {
28969 return paths[entity.tags.highway];
28972 defineFeature('buildings', function isBuilding(entity) {
28974 !!entity.tags['building:part'] ||
28975 (!!entity.tags.building && entity.tags.building !== 'no') ||
28976 entity.tags.amenity === 'shelter' ||
28977 entity.tags.parking === 'multi-storey' ||
28978 entity.tags.parking === 'sheds' ||
28979 entity.tags.parking === 'carports' ||
28980 entity.tags.parking === 'garage_boxes'
28984 defineFeature('landuse', function isLanduse(entity, resolver, geometry) {
28985 return geometry === 'area' &&
28986 !_features.buildings.filter(entity) &&
28987 !_features.water.filter(entity);
28990 defineFeature('boundaries', function isBoundary(entity) {
28991 return !!entity.tags.boundary;
28994 defineFeature('water', function isWater(entity) {
28996 !!entity.tags.waterway ||
28997 entity.tags.natural === 'water' ||
28998 entity.tags.natural === 'coastline' ||
28999 entity.tags.natural === 'bay' ||
29000 entity.tags.landuse === 'pond' ||
29001 entity.tags.landuse === 'basin' ||
29002 entity.tags.landuse === 'reservoir' ||
29003 entity.tags.landuse === 'salt_pond'
29007 defineFeature('rail', function isRail(entity) {
29009 !!entity.tags.railway ||
29010 entity.tags.landuse === 'railway'
29012 traffic_roads[entity.tags.highway] ||
29013 service_roads[entity.tags.highway] ||
29014 paths[entity.tags.highway]
29018 defineFeature('power', function isPower(entity) {
29019 return !!entity.tags.power;
29022 // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
29023 defineFeature('past_future', function isPastFuture(entity) {
29025 traffic_roads[entity.tags.highway] ||
29026 service_roads[entity.tags.highway] ||
29027 paths[entity.tags.highway]
29028 ) { return false; }
29030 var strings = Object.keys(entity.tags);
29032 for (var i = 0; i < strings.length; i++) {
29033 var s = strings[i];
29034 if (past_futures[s] || past_futures[entity.tags[s]]) { return true; }
29039 // Lines or areas that don't match another feature filter.
29040 // IMPORTANT: The 'others' feature must be the last one defined,
29041 // so that code in getMatches can skip this test if `hasMatch = true`
29042 defineFeature('others', function isOther(entity, resolver, geometry) {
29043 return (geometry === 'line' || geometry === 'area');
29047 function features() {}
29049 features.features = function() {
29053 features.keys = function() {
29057 features.enabled = function(k) {
29058 if (!arguments.length) {
29059 return _.filter(_keys, function(k) { return _features[k].enabled; });
29061 return _features[k] && _features[k].enabled;
29064 features.disabled = function(k) {
29065 if (!arguments.length) {
29066 return _.reject(_keys, function(k) { return _features[k].enabled; });
29068 return _features[k] && !_features[k].enabled;
29071 features.hidden = function(k) {
29072 if (!arguments.length) {
29073 return _.filter(_keys, function(k) { return _features[k].hidden(); });
29075 return _features[k] && _features[k].hidden();
29078 features.autoHidden = function(k) {
29079 if (!arguments.length) {
29080 return _.filter(_keys, function(k) { return _features[k].autoHidden(); });
29082 return _features[k] && _features[k].autoHidden();
29085 features.enable = function(k) {
29086 if (_features[k] && !_features[k].enabled) {
29087 _features[k].enable();
29092 features.disable = function(k) {
29093 if (_features[k] && _features[k].enabled) {
29094 _features[k].disable();
29099 features.toggle = function(k) {
29100 if (_features[k]) {
29101 (function(f) { return f.enabled ? f.disable() : f.enable(); }(_features[k]));
29106 features.resetStats = function() {
29107 _.each(_features, function(f) { f.count = 0; });
29111 features.gatherStats = function(d, resolver, dimensions) {
29112 var needsRedraw = false,
29113 type = _.groupBy(d, function(ent) { return ent.type; }),
29114 entities = [].concat(type.relation || [], type.way || [], type.node || []),
29115 currHidden, geometry, matches;
29117 _.each(_features, function(f) { f.count = 0; });
29119 // adjust the threshold for point/building culling based on viewport size..
29120 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
29121 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
29123 for (var i = 0; i < entities.length; i++) {
29124 geometry = entities[i].geometry(resolver);
29125 if (!(geometry === 'vertex' || geometry === 'relation')) {
29126 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
29127 for (var j = 0; j < matches.length; j++) {
29128 _features[matches[j]].count++;
29133 currHidden = features.hidden();
29134 if (currHidden !== _hidden) {
29135 _hidden = currHidden;
29136 needsRedraw = true;
29140 return needsRedraw;
29143 features.stats = function() {
29144 _.each(_keys, function(k) { _stats[k] = _features[k].count; });
29148 features.clear = function(d) {
29149 for (var i = 0; i < d.length; i++) {
29150 features.clearEntity(d[i]);
29154 features.clearEntity = function(entity) {
29155 delete _cache[iD.Entity.key(entity)];
29158 features.reset = function() {
29162 features.getMatches = function(entity, resolver, geometry) {
29163 if (geometry === 'vertex' || geometry === 'relation') return {};
29165 var ent = iD.Entity.key(entity);
29166 if (!_cache[ent]) {
29170 if (!_cache[ent].matches) {
29174 for (var i = 0; i < _keys.length; i++) {
29175 if (_keys[i] === 'others') {
29176 if (hasMatch) continue;
29178 // Multipolygon members:
29180 // 1. is a way that hasn't matched other "interesting" feature rules,
29181 // 2. and it belongs to a single parent multipolygon relation
29182 // ...then match whatever feature rules the parent multipolygon has matched.
29183 // see #2548, #2887
29186 // For this to work, getMatches must be called on relations before ways.
29188 if (entity.type === 'way') {
29189 var parents = features.getParents(entity, resolver, geometry);
29190 if (parents.length === 1 && parents[0].isMultipolygon()) {
29191 var pkey = iD.Entity.key(parents[0]);
29192 if (_cache[pkey] && _cache[pkey].matches) {
29193 matches = _.clone(_cache[pkey].matches);
29200 if (_features[_keys[i]].filter(entity, resolver, geometry)) {
29201 matches[_keys[i]] = hasMatch = true;
29204 _cache[ent].matches = matches;
29207 return _cache[ent].matches;
29210 features.getParents = function(entity, resolver, geometry) {
29211 if (geometry === 'point') return [];
29213 var ent = iD.Entity.key(entity);
29214 if (!_cache[ent]) {
29218 if (!_cache[ent].parents) {
29220 if (geometry === 'vertex') {
29221 parents = resolver.parentWays(entity);
29222 } else { // 'line', 'area', 'relation'
29223 parents = resolver.parentRelations(entity);
29225 _cache[ent].parents = parents;
29227 return _cache[ent].parents;
29230 features.isHiddenFeature = function(entity, resolver, geometry) {
29231 if (!_hidden.length) return false;
29232 if (!entity.version) return false;
29234 var matches = features.getMatches(entity, resolver, geometry);
29236 for (var i = 0; i < _hidden.length; i++) {
29237 if (matches[_hidden[i]]) return true;
29242 features.isHiddenChild = function(entity, resolver, geometry) {
29243 if (!_hidden.length) return false;
29244 if (!entity.version || geometry === 'point') return false;
29246 var parents = features.getParents(entity, resolver, geometry);
29247 if (!parents.length) return false;
29249 for (var i = 0; i < parents.length; i++) {
29250 if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
29257 features.hasHiddenConnections = function(entity, resolver) {
29258 if (!_hidden.length) return false;
29259 var childNodes, connections;
29261 if (entity.type === 'midpoint') {
29262 childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
29265 childNodes = entity.nodes ? resolver.childNodes(entity) : [];
29266 connections = features.getParents(entity, resolver, entity.geometry(resolver));
29269 // gather ways connected to child nodes..
29270 connections = _.reduce(childNodes, function(result, e) {
29271 return resolver.isShared(e) ? _.union(result, resolver.parentWays(e)) : result;
29274 return connections.length ? _.any(connections, function(e) {
29275 return features.isHidden(e, resolver, e.geometry(resolver));
29279 features.isHidden = function(entity, resolver, geometry) {
29280 if (!_hidden.length) return false;
29281 if (!entity.version) return false;
29283 var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
29284 return fn(entity, resolver, geometry);
29287 features.filter = function(d, resolver) {
29288 if (!_hidden.length) return d;
29291 for (var i = 0; i < d.length; i++) {
29293 if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
29294 result.push(entity);
29300 return d3.rebind(features, dispatch, 'on');
29302 iD.Map = function(context) {
29303 var dimensions = [1, 1],
29304 dispatch = d3.dispatch('move', 'drawn'),
29305 projection = context.projection,
29306 zoom = d3.behavior.zoom()
29307 .translate(projection.translate())
29308 .scale(projection.scale() * 2 * Math.PI)
29309 .scaleExtent([1024, 256 * Math.pow(2, 24)])
29310 .on('zoom', zoomPan),
29311 dblclickEnabled = true,
29312 redrawEnabled = true,
29314 transformed = false,
29316 drawLayers = iD.svg.Layers(projection, context),
29317 drawPoints = iD.svg.Points(projection, context),
29318 drawVertices = iD.svg.Vertices(projection, context),
29319 drawLines = iD.svg.Lines(projection),
29320 drawAreas = iD.svg.Areas(projection),
29321 drawMidpoints = iD.svg.Midpoints(projection, context),
29322 drawLabels = iD.svg.Labels(projection, context),
29329 function map(selection) {
29331 .on('change.map', redraw);
29332 context.background()
29333 .on('change.map', redraw);
29335 .on('redraw.map', redraw);
29337 .on('change.map', function() {
29338 context.background().updateImagery();
29343 .on('dblclick.map', dblClick)
29346 supersurface = selection.append('div')
29347 .attr('id', 'supersurface')
29348 .call(iD.util.setTransform, 0, 0);
29350 // Need a wrapper div because Opera can't cope with an absolutely positioned
29351 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
29352 wrapper = supersurface
29354 .attr('class', 'layer layer-data');
29356 map.surface = surface = wrapper
29358 .selectAll('.surface')
29359 .attr('id', 'surface');
29362 .on('mousedown.zoom', function() {
29363 if (d3.event.button === 2) {
29364 d3.event.stopPropagation();
29367 .on('mouseup.zoom', function() {
29368 if (resetTransform()) redraw();
29370 .on('mousemove.map', function() {
29371 mousemove = d3.event;
29373 .on('mouseover.vertices', function() {
29374 if (map.editable() && !transformed) {
29375 var hover = d3.event.target.__data__;
29376 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29377 dispatch.drawn({full: false});
29380 .on('mouseout.vertices', function() {
29381 if (map.editable() && !transformed) {
29382 var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__;
29383 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29384 dispatch.drawn({full: false});
29390 .call(context.background());
29393 context.on('enter.map', function() {
29394 if (map.editable() && !transformed) {
29395 var all = context.intersects(map.extent()),
29396 filter = d3.functor(true),
29397 graph = context.graph();
29399 all = context.features().filter(all, graph);
29401 .call(drawVertices, graph, all, filter, map.extent(), map.zoom())
29402 .call(drawMidpoints, graph, all, filter, map.trimmedExtent());
29403 dispatch.drawn({full: false});
29407 map.dimensions(selection.dimensions());
29409 drawLabels.supersurface(supersurface);
29412 function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }
29414 function drawVector(difference, extent) {
29415 var graph = context.graph(),
29416 features = context.features(),
29417 all = context.intersects(map.extent()),
29421 var complete = difference.complete(map.extent());
29422 data = _.compact(_.values(complete));
29423 filter = function(d) { return d.id in complete; };
29424 features.clear(data);
29427 // force a full redraw if gatherStats detects that a feature
29428 // should be auto-hidden (e.g. points or buildings)..
29429 if (features.gatherStats(all, graph, dimensions)) {
29430 extent = undefined;
29434 data = context.intersects(map.extent().intersection(extent));
29435 var set = d3.set(_.pluck(data, 'id'));
29436 filter = function(d) { return set.has(d.id); };
29440 filter = d3.functor(true);
29444 data = features.filter(data, graph);
29447 .call(drawVertices, graph, data, filter, map.extent(), map.zoom())
29448 .call(drawLines, graph, data, filter)
29449 .call(drawAreas, graph, data, filter)
29450 .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
29451 .call(drawLabels, graph, data, filter, dimensions, !difference && !extent)
29452 .call(drawPoints, graph, data, filter);
29454 dispatch.drawn({full: true});
29457 function editOff() {
29458 context.features().resetStats();
29459 surface.selectAll('.layer-osm *').remove();
29460 dispatch.drawn({full: true});
29463 function dblClick() {
29464 if (!dblclickEnabled) {
29465 d3.event.preventDefault();
29466 d3.event.stopImmediatePropagation();
29470 function zoomPan() {
29471 if (Math.log(d3.event.scale) / Math.LN2 - 8 < minzoom) {
29472 surface.interrupt();
29473 iD.ui.flash(context.container())
29474 .select('.content')
29475 .text(t('cannot_zoom'));
29476 setZoom(context.minEditableZoom(), true);
29478 dispatch.move(map);
29483 .translate(d3.event.translate)
29484 .scale(d3.event.scale / (2 * Math.PI));
29486 var scale = d3.event.scale / transformStart[0],
29487 tX = (d3.event.translate[0] / scale - transformStart[1][0]) * scale,
29488 tY = (d3.event.translate[1] / scale - transformStart[1][1]) * scale;
29490 transformed = true;
29491 iD.util.setTransform(supersurface, tX, tY, scale);
29494 dispatch.move(map);
29497 function resetTransform() {
29498 if (!transformed) return false;
29500 surface.selectAll('.radial-menu').interrupt().remove();
29501 iD.util.setTransform(supersurface, 0, 0);
29502 transformed = false;
29506 function redraw(difference, extent) {
29507 if (!surface || !redrawEnabled) return;
29509 clearTimeout(timeoutId);
29511 // If we are in the middle of a zoom/pan, we can't do differenced redraws.
29512 // It would result in artifacts where differenced entities are redrawn with
29513 // one transform and unchanged entities with another.
29514 if (resetTransform()) {
29515 difference = extent = undefined;
29518 var zoom = String(~~map.zoom());
29519 if (surface.attr('data-zoom') !== zoom) {
29520 surface.attr('data-zoom', zoom)
29521 .classed('low-zoom', zoom <= 16);
29525 supersurface.call(context.background());
29529 if (map.editable()) {
29530 context.loadTiles(projection, dimensions);
29531 drawVector(difference, extent);
29540 projection.scale() * 2 * Math.PI,
29541 projection.translate().slice()];
29547 function queueRedraw() {
29548 clearTimeout(timeoutId);
29549 timeoutId = setTimeout(function() { redraw(); }, 300);
29552 function pointLocation(p) {
29553 var translate = projection.translate(),
29554 scale = projection.scale() * 2 * Math.PI;
29555 return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
29558 function locationPoint(l) {
29559 var translate = projection.translate(),
29560 scale = projection.scale() * 2 * Math.PI;
29561 return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
29564 map.mouse = function() {
29565 var e = mousemove || d3.event, s;
29566 while ((s = e.sourceEvent)) e = s;
29570 map.mouseCoordinates = function() {
29571 return projection.invert(map.mouse());
29574 map.dblclickEnable = function(_) {
29575 if (!arguments.length) return dblclickEnabled;
29576 dblclickEnabled = _;
29580 map.redrawEnable = function(_) {
29581 if (!arguments.length) return redrawEnabled;
29586 function interpolateZoom(_) {
29587 var k = projection.scale(),
29588 t = projection.translate();
29590 surface.node().__chart__ = {
29597 projection.scale(k).translate(t); // undo setZoom projection changes
29599 zoom.event(surface.transition());
29602 function setZoom(_, force) {
29603 if (_ === map.zoom() && !force)
29605 var scale = 256 * Math.pow(2, _),
29606 center = pxCenter(),
29607 l = pointLocation(center);
29608 scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
29609 projection.scale(scale / (2 * Math.PI));
29611 var t = projection.translate();
29612 l = locationPoint(l);
29613 t[0] += center[0] - l[0];
29614 t[1] += center[1] - l[1];
29615 projection.translate(t);
29616 zoom.translate(projection.translate());
29620 function setCenter(_) {
29621 var c = map.center();
29622 if (_[0] === c[0] && _[1] === c[1])
29624 var t = projection.translate(),
29626 ll = projection(_);
29627 projection.translate([
29628 t[0] - ll[0] + pxC[0],
29629 t[1] - ll[1] + pxC[1]]);
29630 zoom.translate(projection.translate());
29634 map.pan = function(d) {
29635 var t = projection.translate();
29638 projection.translate(t);
29639 zoom.translate(projection.translate());
29640 dispatch.move(map);
29644 map.dimensions = function(_) {
29645 if (!arguments.length) return dimensions;
29646 var center = map.center();
29648 drawLayers.dimensions(dimensions);
29649 context.background().dimensions(dimensions);
29650 projection.clipExtent([[0, 0], dimensions]);
29651 mouse = iD.util.fastMouse(supersurface.node());
29656 function zoomIn(integer) {
29657 interpolateZoom(~~map.zoom() + integer);
29660 function zoomOut(integer) {
29661 interpolateZoom(~~map.zoom() - integer);
29664 map.zoomIn = function() { zoomIn(1); };
29665 map.zoomInFurther = function() { zoomIn(4); };
29667 map.zoomOut = function() { zoomOut(1); };
29668 map.zoomOutFurther = function() { zoomOut(4); };
29670 map.center = function(loc) {
29671 if (!arguments.length) {
29672 return projection.invert(pxCenter());
29675 if (setCenter(loc)) {
29676 dispatch.move(map);
29682 map.zoom = function(z) {
29683 if (!arguments.length) {
29684 return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
29688 surface.interrupt();
29689 iD.ui.flash(context.container())
29690 .select('.content')
29691 .text(t('cannot_zoom'));
29692 z = context.minEditableZoom();
29696 dispatch.move(map);
29702 map.zoomTo = function(entity, zoomLimits) {
29703 var extent = entity.extent(context.graph());
29704 if (!isFinite(extent.area())) return;
29706 var zoom = map.trimmedExtentZoom(extent);
29707 zoomLimits = zoomLimits || [context.minEditableZoom(), 20];
29708 map.centerZoom(extent.center(), Math.min(Math.max(zoom, zoomLimits[0]), zoomLimits[1]));
29711 map.centerZoom = function(loc, z) {
29712 var centered = setCenter(loc),
29713 zoomed = setZoom(z);
29715 if (centered || zoomed) {
29716 dispatch.move(map);
29722 map.centerEase = function(loc) {
29723 var from = map.center().slice(),
29727 surface.one('mousedown.ease', function() {
29731 d3.timer(function() {
29732 if (stop) return true;
29733 map.center(iD.geo.interp(from, loc, (t += 1) / 10));
29739 map.extent = function(_) {
29740 if (!arguments.length) {
29741 return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
29742 projection.invert([dimensions[0], 0]));
29744 var extent = iD.geo.Extent(_);
29745 map.centerZoom(extent.center(), map.extentZoom(extent));
29749 map.trimmedExtent = function(_) {
29750 if (!arguments.length) {
29751 var headerY = 60, footerY = 30, pad = 10;
29752 return new iD.geo.Extent(projection.invert([pad, dimensions[1] - footerY - pad]),
29753 projection.invert([dimensions[0] - pad, headerY + pad]));
29755 var extent = iD.geo.Extent(_);
29756 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
29760 function calcZoom(extent, dim) {
29761 var tl = projection([extent[0][0], extent[1][1]]),
29762 br = projection([extent[1][0], extent[0][1]]);
29764 // Calculate maximum zoom that fits extent
29765 var hFactor = (br[0] - tl[0]) / dim[0],
29766 vFactor = (br[1] - tl[1]) / dim[1],
29767 hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
29768 vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
29769 newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
29774 map.extentZoom = function(_) {
29775 return calcZoom(iD.geo.Extent(_), dimensions);
29778 map.trimmedExtentZoom = function(_) {
29779 var trimY = 120, trimX = 40,
29780 trimmed = [dimensions[0] - trimX, dimensions[1] - trimY];
29781 return calcZoom(iD.geo.Extent(_), trimmed);
29784 map.editable = function() {
29785 return map.zoom() >= context.minEditableZoom();
29788 map.minzoom = function(_) {
29789 if (!arguments.length) return minzoom;
29794 map.layers = drawLayers;
29796 return d3.rebind(map, dispatch, 'on');
29798 iD.TileLayer = function() {
29799 var tileSize = 256,
29800 tile = d3.geo.tile(),
29805 transformProp = iD.util.prefixCSSProperty('Transform'),
29806 source = d3.functor('');
29809 // blacklist overlay tiles around Null Island..
29810 function nearNullIsland(x, y, z) {
29812 var center = Math.pow(2, z - 1),
29813 width = Math.pow(2, z - 6),
29814 min = center - (width / 2),
29815 max = center + (width / 2) - 1;
29816 return x >= min && x <= max && y >= min && y <= max;
29821 function tileSizeAtZoom(d, z) {
29822 var epsilon = 0.002;
29823 return ((tileSize * Math.pow(2, z - d[2])) / tileSize) + epsilon;
29826 function atZoom(t, distance) {
29827 var power = Math.pow(2, distance);
29829 Math.floor(t[0] * power),
29830 Math.floor(t[1] * power),
29834 function lookUp(d) {
29835 for (var up = -1; up > -d[2]; up--) {
29836 var tile = atZoom(d, up);
29837 if (cache[source.url(tile)] !== false) {
29843 function uniqueBy(a, n) {
29844 var o = [], seen = {};
29845 for (var i = 0; i < a.length; i++) {
29846 if (seen[a[i][n]] === undefined) {
29848 seen[a[i][n]] = true;
29854 function addSource(d) {
29855 d.push(source.url(d));
29859 // Update tiles based on current state of `projection`.
29860 function background(selection) {
29861 tile.scale(projection.scale() * 2 * Math.PI)
29862 .translate(projection.translate());
29865 projection.scale() * Math.PI - projection.translate()[0],
29866 projection.scale() * Math.PI - projection.translate()[1]];
29868 z = Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.log(2) - 8, 0);
29873 // Derive the tiles onscreen, remove those offscreen and position them.
29874 // Important that this part not depend on `projection` because it's
29875 // rentered when tiles load/error (see #644).
29876 function render(selection) {
29879 if (source.validZoom(z)) {
29880 tile().forEach(function(d) {
29882 if (d[3] === '') return;
29883 if (typeof d[3] !== 'string') return; // Workaround for chrome crash https://github.com/openstreetmap/iD/issues/2295
29885 if (cache[d[3]] === false && lookUp(d)) {
29886 requests.push(addSource(lookUp(d)));
29890 requests = uniqueBy(requests, 3).filter(function(r) {
29891 if (!!source.overlay && nearNullIsland(r[0], r[1], r[2])) {
29894 // don't re-request tiles which have failed in the past
29895 return cache[r[3]] !== false;
29899 var pixelOffset = [
29900 source.offset()[0] * Math.pow(2, z),
29901 source.offset()[1] * Math.pow(2, z)
29905 cache[d[3]] = true;
29909 .classed('tile-loaded', true);
29913 function error(d) {
29914 cache[d[3]] = false;
29922 function imageTransform(d) {
29923 var _ts = tileSize * Math.pow(2, z - d[2]);
29924 var scale = tileSizeAtZoom(d, z);
29925 return 'translate(' +
29926 ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0]) + 'px,' +
29927 ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1]) + 'px)' +
29928 'scale(' + scale + ',' + scale + ')';
29931 var image = selection
29933 .data(requests, function(d) { return d[3]; });
29936 .style(transformProp, imageTransform)
29937 .classed('tile-removing', true)
29939 var tile = d3.select(this);
29940 window.setTimeout(function() {
29941 if (tile.classed('tile-removing')) {
29947 image.enter().append('img')
29948 .attr('class', 'tile')
29949 .attr('src', function(d) { return d[3]; })
29950 .on('error', error)
29954 .style(transformProp, imageTransform)
29955 .classed('tile-removing', false);
29958 background.projection = function(_) {
29959 if (!arguments.length) return projection;
29964 background.dimensions = function(_) {
29965 if (!arguments.length) return tile.size();
29970 background.source = function(_) {
29971 if (!arguments.length) return source;
29974 tile.scaleExtent(source.scaleExtent);
29981 PointTransform: function(projection) {
29982 return function(entity) {
29983 // http://jsperf.com/short-array-join
29984 var pt = projection(entity.loc);
29985 return 'translate(' + pt[0] + ',' + pt[1] + ')';
29989 Path: function(projection, graph, polygon) {
29991 clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
29992 project = projection.stream,
29993 path = d3.geo.path()
29994 .projection({stream: function(output) { return polygon ? project(output) : project(clip(output)); }});
29996 return function(entity) {
29997 if (entity.id in cache) {
29998 return cache[entity.id];
30000 return cache[entity.id] = path(entity.asGeoJSON(graph));
30005 OneWaySegments: function(projection, graph, dt) {
30006 return function(entity) {
30012 clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
30013 coordinates = graph.childNodes(entity).map(function(n) {
30017 if (entity.tags.oneway === '-1') coordinates.reverse();
30020 type: 'LineString',
30021 coordinates: coordinates
30022 }, projection.stream(clip({
30023 lineStart: function() {},
30024 lineEnd: function() {
30027 point: function(x, y) {
30031 var span = iD.geo.euclideanDistance(a, b) - offset;
30034 var angle = Math.atan2(b[1] - a[1], b[0] - a[0]),
30035 dx = dt * Math.cos(angle),
30036 dy = dt * Math.sin(angle),
30037 p = [a[0] + offset * Math.cos(angle),
30038 a[1] + offset * Math.sin(angle)];
30040 var segment = 'M' + a[0] + ',' + a[1] +
30041 'L' + p[0] + ',' + p[1];
30043 for (span -= dt; span >= 0; span -= dt) {
30046 segment += 'L' + p[0] + ',' + p[1];
30049 segment += 'L' + b[0] + ',' + b[1];
30050 segments.push({id: entity.id, index: i, d: segment});
30065 RelationMemberTags: function(graph) {
30066 return function(entity) {
30067 var tags = entity.tags;
30068 graph.parentRelations(entity).forEach(function(relation) {
30069 var type = relation.tags.type;
30070 if (type === 'multipolygon' || type === 'boundary') {
30071 tags = _.extend({}, relation.tags, tags);
30078 iD.svg.Areas = function(projection) {
30079 // Patterns only work in Firefox when set directly on element.
30080 // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
30082 wetland: 'wetland',
30085 construction: 'construction',
30086 military: 'construction',
30087 cemetery: 'cemetery',
30088 grave_yard: 'cemetery',
30091 farmland: 'farmland',
30095 var patternKeys = ['landuse', 'natural', 'amenity'];
30097 function setPattern(d) {
30098 for (var i = 0; i < patternKeys.length; i++) {
30099 if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) {
30100 this.style.fill = this.style.stroke = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")';
30104 this.style.fill = this.style.stroke = '';
30107 return function drawAreas(surface, graph, entities, filter) {
30108 var path = iD.svg.Path(projection, graph, true),
30112 for (var i = 0; i < entities.length; i++) {
30113 var entity = entities[i];
30114 if (entity.geometry(graph) !== 'area') continue;
30116 multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph);
30117 if (multipolygon) {
30118 areas[multipolygon.id] = {
30119 entity: multipolygon.mergeTags(entity.tags),
30120 area: Math.abs(entity.area(graph))
30122 } else if (!areas[entity.id]) {
30123 areas[entity.id] = {
30125 area: Math.abs(entity.area(graph))
30130 areas = d3.values(areas).filter(function hasPath(a) { return path(a.entity); });
30131 areas.sort(function areaSort(a, b) { return b.area - a.area; });
30132 areas = _.pluck(areas, 'entity');
30134 var strokes = areas.filter(function(area) {
30135 return area.type === 'way';
30145 var clipPaths = surface.selectAll('defs').selectAll('.clipPath')
30147 .data(data.clip, iD.Entity.key);
30150 .append('clipPath')
30151 .attr('class', 'clipPath')
30152 .attr('id', function(entity) { return entity.id + '-clippath'; })
30155 clipPaths.selectAll('path')
30161 var areagroup = surface
30162 .selectAll('.layer-areas')
30163 .selectAll('g.areagroup')
30164 .data(['fill', 'shadow', 'stroke']);
30168 .attr('class', function(d) { return 'layer areagroup area-' + d; });
30170 var paths = areagroup
30173 .data(function(layer) { return data[layer]; }, iD.Entity.key);
30175 // Remove exiting areas first, so they aren't included in the `fills`
30176 // array used for sorting below (https://github.com/openstreetmap/iD/issues/1903).
30180 var fills = surface.selectAll('.area-fill path.area')[0];
30182 var bisect = d3.bisector(function(node) {
30183 return -node.__data__.area(graph);
30186 function sortedByArea(entity) {
30187 if (this.__data__ === 'fill') {
30188 return fills[bisect(fills, -entity.area(graph))];
30193 .insert('path', sortedByArea)
30194 .each(function(entity) {
30195 var layer = this.parentNode.__data__;
30197 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
30199 if (layer === 'fill') {
30200 this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)');
30201 setPattern.apply(this, arguments);
30204 .call(iD.svg.TagClasses());
30211 A standalone SVG element that contains only a `defs` sub-element. To be
30212 used once globally, since defs IDs must be unique within a document.
30214 iD.svg.Defs = function(context) {
30216 function SVGSpriteDefinition(id, href) {
30217 return function(defs) {
30218 d3.xml(href, 'image/svg+xml', function(err, svg) {
30220 defs.node().appendChild(
30221 d3.select(svg.documentElement).attr('id', id).node()
30227 return function drawDefs(selection) {
30228 var defs = selection.append('defs');
30231 defs.append('marker')
30233 id: 'oneway-marker',
30234 viewBox: '0 0 10 10',
30239 markerUnits: 'strokeWidth',
30243 .attr('class', 'oneway')
30244 .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')
30245 .attr('stroke', 'none')
30246 .attr('fill', '#000')
30247 .attr('opacity', '0.5');
30250 var patterns = defs.selectAll('pattern')
30252 // pattern name, pattern image name
30253 ['wetland', 'wetland'],
30254 ['construction', 'construction'],
30255 ['cemetery', 'cemetery'],
30256 ['orchard', 'orchard'],
30257 ['farmland', 'farmland'],
30266 return 'pattern-' + d[0];
30270 patternUnits: 'userSpaceOnUse'
30273 patterns.append('rect')
30279 'class': function (d) {
30280 return 'pattern-color-' + d[0];
30284 patterns.append('image')
30291 .attr('xlink:href', function (d) {
30292 return context.imagePath('pattern/' + d[1] + '.png');
30297 .data([12, 18, 20, 32, 45])
30298 .enter().append('clipPath')
30299 .attr('id', function (d) {
30300 return 'clip-square-' + d;
30305 .attr('width', function (d) {
30308 .attr('height', function (d) {
30312 defs.call(SVGSpriteDefinition(
30314 context.imagePath('iD-sprite.svg')));
30316 defs.call(SVGSpriteDefinition(
30318 context.imagePath('maki-sprite.svg')));
30321 iD.svg.Gpx = function(projection, context, dispatch) {
30322 var showLabels = true,
30326 if (iD.svg.Gpx.initialized) return; // run once
30328 iD.svg.Gpx.geojson = {};
30329 iD.svg.Gpx.enabled = true;
30332 d3.event.stopPropagation();
30333 d3.event.preventDefault();
30334 d3.event.dataTransfer.dropEffect = 'copy';
30338 .attr('dropzone', 'copy')
30339 .on('drop.localgpx', function() {
30340 d3.event.stopPropagation();
30341 d3.event.preventDefault();
30342 if (!iD.detect().filedrop) return;
30343 drawGpx.files(d3.event.dataTransfer.files);
30345 .on('dragenter.localgpx', over)
30346 .on('dragexit.localgpx', over)
30347 .on('dragover.localgpx', over);
30349 iD.svg.Gpx.initialized = true;
30353 function drawGpx(surface) {
30354 var geojson = iD.svg.Gpx.geojson,
30355 enabled = iD.svg.Gpx.enabled;
30357 layer = surface.selectAll('.layer-gpx')
30358 .data(enabled ? [0] : []);
30362 .attr('class', 'layer-gpx');
30374 .attr('class', 'gpx');
30379 var path = d3.geo.path()
30380 .projection(projection);
30386 var labels = layer.selectAll('text')
30387 .data(showLabels && geojson.features ? geojson.features : []);
30391 .attr('class', 'gpx');
30397 .text(function(d) {
30398 return d.properties.desc || d.properties.name;
30400 .attr('x', function(d) {
30401 var centroid = path.centroid(d);
30402 return centroid[0] + 7;
30404 .attr('y', function(d) {
30405 var centroid = path.centroid(d);
30406 return centroid[1];
30411 function toDom(x) {
30412 return (new DOMParser()).parseFromString(x, 'text/xml');
30415 drawGpx.showLabels = function(_) {
30416 if (!arguments.length) return showLabels;
30421 drawGpx.enabled = function(_) {
30422 if (!arguments.length) return iD.svg.Gpx.enabled;
30423 iD.svg.Gpx.enabled = _;
30428 drawGpx.hasGpx = function() {
30429 var geojson = iD.svg.Gpx.geojson;
30430 return (!(_.isEmpty(geojson) || _.isEmpty(geojson.features)));
30433 drawGpx.geojson = function(gj) {
30434 if (!arguments.length) return iD.svg.Gpx.geojson;
30435 if (_.isEmpty(gj) || _.isEmpty(gj.features)) return this;
30436 iD.svg.Gpx.geojson = gj;
30441 drawGpx.url = function(url) {
30442 d3.text(url, function(err, data) {
30444 drawGpx.geojson(toGeoJSON.gpx(toDom(data)));
30450 drawGpx.files = function(fileList) {
30451 if (!fileList.length) return this;
30452 var f = fileList[0],
30453 reader = new FileReader();
30455 reader.onload = function(e) {
30456 drawGpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom();
30459 reader.readAsText(f);
30463 drawGpx.fitZoom = function() {
30464 if (!this.hasGpx()) return this;
30465 var geojson = iD.svg.Gpx.geojson;
30467 var map = context.map(),
30468 viewport = map.trimmedExtent().polygon(),
30469 coords = _.reduce(geojson.features, function(coords, feature) {
30470 var c = feature.geometry.coordinates;
30471 return _.union(coords, feature.geometry.type === 'Point' ? [c] : c);
30474 if (!iD.geo.polygonIntersectsPolygon(viewport, coords, true)) {
30475 var extent = iD.geo.Extent(d3.geo.bounds(geojson));
30476 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
30485 iD.svg.Icon = function(name, svgklass, useklass) {
30486 return function drawIcon(selection) {
30487 selection.selectAll('svg')
30491 .attr('class', 'icon ' + (svgklass || ''))
30493 .attr('xlink:href', name)
30494 .attr('class', useklass);
30497 iD.svg.Labels = function(projection, context) {
30498 var path = d3.geo.path().projection(projection);
30500 // Replace with dict and iterate over entities tags instead?
30501 var label_stack = [
30502 ['line', 'aeroway'],
30503 ['line', 'highway'],
30504 ['line', 'railway'],
30505 ['line', 'waterway'],
30506 ['area', 'aeroway'],
30507 ['area', 'amenity'],
30508 ['area', 'building'],
30509 ['area', 'historic'],
30510 ['area', 'leisure'],
30511 ['area', 'man_made'],
30512 ['area', 'natural'],
30514 ['area', 'tourism'],
30515 ['point', 'aeroway'],
30516 ['point', 'amenity'],
30517 ['point', 'building'],
30518 ['point', 'historic'],
30519 ['point', 'leisure'],
30520 ['point', 'man_made'],
30521 ['point', 'natural'],
30523 ['point', 'tourism'],
30529 var default_size = 12;
30531 var font_sizes = label_stack.map(function(d) {
30532 var style = iD.util.getStyle('text.' + d[0] + '.tag-' + d[1]),
30533 m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30534 if (m) return parseInt(m[1], 10);
30536 style = iD.util.getStyle('text.' + d[0]);
30537 m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30538 if (m) return parseInt(m[1], 10);
30540 return default_size;
30545 var pointOffsets = [
30546 [15, -11, 'start'], // right
30547 [10, -11, 'start'], // unused right now
30551 var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25,
30552 75, 20, 80, 15, 95, 10, 90, 5, 95];
30555 var noIcons = ['building', 'landuse', 'natural'];
30556 function blacklisted(preset) {
30557 return _.any(noIcons, function(s) {
30558 return preset.id.indexOf(s) >= 0;
30562 function get(array, prop) {
30563 return function(d, i) { return array[i][prop]; };
30566 var textWidthCache = {};
30568 function textWidth(text, size, elem) {
30569 var c = textWidthCache[size];
30570 if (!c) c = textWidthCache[size] = {};
30576 c[text] = elem.getComputedTextLength();
30580 var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
30581 if (str === null) {
30582 return size / 3 * 2 * text.length;
30584 return size / 3 * (2 * text.length + str.length);
30589 function drawLineLabels(group, entities, filter, classes, labels) {
30590 var texts = group.selectAll('text.' + classes)
30592 .data(entities, iD.Entity.key);
30596 .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
30597 .append('textPath')
30598 .attr('class', 'textpath');
30601 texts.selectAll('.textpath')
30603 .data(entities, iD.Entity.key)
30605 'startOffset': '50%',
30606 'xlink:href': function(d) { return '#labelpath-' + d.id; }
30608 .text(iD.util.displayName);
30610 texts.exit().remove();
30613 function drawLinePaths(group, entities, filter, classes, labels) {
30614 var halos = group.selectAll('path')
30616 .data(entities, iD.Entity.key);
30620 .style('stroke-width', get(labels, 'font-size'))
30621 .attr('id', function(d) { return 'labelpath-' + d.id; })
30622 .attr('class', classes);
30624 halos.attr('d', get(labels, 'lineString'));
30626 halos.exit().remove();
30629 function drawPointLabels(group, entities, filter, classes, labels) {
30630 var texts = group.selectAll('text.' + classes)
30632 .data(entities, iD.Entity.key);
30636 .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; });
30638 texts.attr('x', get(labels, 'x'))
30639 .attr('y', get(labels, 'y'))
30640 .style('text-anchor', get(labels, 'textAnchor'))
30641 .text(iD.util.displayName)
30642 .each(function(d, i) { textWidth(iD.util.displayName(d), labels[i].height, this); });
30644 texts.exit().remove();
30648 function drawAreaLabels(group, entities, filter, classes, labels) {
30649 entities = entities.filter(hasText);
30650 labels = labels.filter(hasText);
30651 return drawPointLabels(group, entities, filter, classes, labels);
30653 function hasText(d, i) {
30654 return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
30658 function drawAreaIcons(group, entities, filter, classes, labels) {
30659 var icons = group.selectAll('use')
30661 .data(entities, iD.Entity.key);
30665 .attr('class', 'icon areaicon')
30666 .attr('width', '18px')
30667 .attr('height', '18px');
30669 icons.attr('transform', get(labels, 'transform'))
30670 .attr('xlink:href', function(d) {
30671 var icon = context.presets().match(d, context.graph()).icon;
30672 return '#' + icon + (icon === 'hairdresser' ? '-24': '-18'); // workaround: maki hairdresser-18 broken?
30676 icons.exit().remove();
30679 function reverse(p) {
30680 var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
30681 return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
30684 function lineString(nodes) {
30685 return 'M' + nodes.join('L');
30688 function subpath(nodes, from, to) {
30689 function segmentLength(i) {
30690 var dx = nodes[i][0] - nodes[i + 1][0];
30691 var dy = nodes[i][1] - nodes[i + 1][1];
30692 return Math.sqrt(dx * dx + dy * dy);
30696 start, end, i0, i1;
30697 for (var i = 0; i < nodes.length - 1; i++) {
30698 var current = segmentLength(i);
30700 if (!start && sofar + current >= from) {
30701 portion = (from - sofar) / current;
30703 nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30704 nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30708 if (!end && sofar + current >= to) {
30709 portion = (to - sofar) / current;
30711 nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30712 nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30719 var ret = nodes.slice(i0, i1);
30720 ret.unshift(start);
30726 function hideOnMouseover() {
30727 var layers = d3.select(this)
30728 .selectAll('.layer-label, .layer-halo');
30730 layers.selectAll('.proximate')
30731 .classed('proximate', false);
30733 var mouse = context.mouse(),
30735 rect = [mouse[0] - pad, mouse[1] - pad, mouse[0] + pad, mouse[1] + pad],
30736 ids = _.pluck(rtree.search(rect), 'id');
30738 if (!ids.length) return;
30739 layers.selectAll('.' + ids.join(', .'))
30740 .classed('proximate', true);
30743 var rtree = rbush(),
30746 function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
30747 var hidePoints = !surface.selectAll('.node.point').node();
30749 var labelable = [], i, k, entity;
30750 for (i = 0; i < label_stack.length; i++) labelable.push([]);
30756 for (i = 0; i < entities.length; i++) {
30757 rtree.remove(rectangles[entities[i].id]);
30761 // Split entities into groups specified by label_stack
30762 for (i = 0; i < entities.length; i++) {
30763 entity = entities[i];
30764 var geometry = entity.geometry(graph);
30766 if (geometry === 'vertex')
30768 if (hidePoints && geometry === 'point')
30771 var preset = geometry === 'area' && context.presets().match(entity, graph),
30772 icon = preset && !blacklisted(preset) && preset.icon;
30774 if (!icon && !iD.util.displayName(entity))
30777 for (k = 0; k < label_stack.length; k++) {
30778 if (geometry === label_stack[k][0] && entity.tags[label_stack[k][1]]) {
30779 labelable[k].push(entity);
30797 // Try and find a valid label for labellable entities
30798 for (k = 0; k < labelable.length; k++) {
30799 var font_size = font_sizes[k];
30800 for (i = 0; i < labelable[k].length; i++) {
30801 entity = labelable[k][i];
30802 var name = iD.util.displayName(entity),
30803 width = name && textWidth(name, font_size),
30805 if (entity.geometry(graph) === 'point') {
30806 p = getPointLabel(entity, width, font_size);
30807 } else if (entity.geometry(graph) === 'line') {
30808 p = getLineLabel(entity, width, font_size);
30809 } else if (entity.geometry(graph) === 'area') {
30810 p = getAreaLabel(entity, width, font_size);
30813 p.classes = entity.geometry(graph) + ' tag-' + label_stack[k][1];
30814 positions[entity.geometry(graph)].push(p);
30815 labelled[entity.geometry(graph)].push(entity);
30820 function getPointLabel(entity, width, height) {
30821 var coord = projection(entity.loc),
30823 offset = pointOffsets[0],
30827 x: coord[0] + offset[0],
30828 y: coord[1] + offset[1],
30829 textAnchor: offset[2]
30831 var rect = [p.x - m, p.y - m, p.x + width + m, p.y + height + m];
30832 if (tryInsert(rect, entity.id)) return p;
30836 function getLineLabel(entity, width, height) {
30837 var nodes = _.pluck(graph.childNodes(entity), 'loc').map(projection),
30838 length = iD.geo.pathLength(nodes);
30839 if (length < width + 20) return;
30841 for (var i = 0; i < lineOffsets.length; i++) {
30842 var offset = lineOffsets[i],
30843 middle = offset / 100 * length,
30844 start = middle - width/2;
30845 if (start < 0 || start + width > length) continue;
30846 var sub = subpath(nodes, start, start + width),
30847 rev = reverse(sub),
30849 Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
30850 Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
30851 Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
30852 Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
30854 if (rev) sub = sub.reverse();
30855 if (tryInsert(rect, entity.id)) return {
30856 'font-size': height + 2,
30857 lineString: lineString(sub),
30858 startOffset: offset + '%'
30863 function getAreaLabel(entity, width, height) {
30864 var centroid = path.centroid(entity.asGeoJSON(graph, true)),
30865 extent = entity.extent(graph),
30866 entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
30869 if (isNaN(centroid[0]) || entitywidth < 20) return;
30871 var iconX = centroid[0] - (iconSize/2),
30872 iconY = centroid[1] - (iconSize/2),
30873 textOffset = iconSize + 5;
30876 transform: 'translate(' + iconX + ',' + iconY + ')'
30879 if (width && entitywidth >= width + 20) {
30881 p.y = centroid[1] + textOffset;
30882 p.textAnchor = 'middle';
30884 rect = [p.x - width/2, p.y, p.x + width/2, p.y + height + textOffset];
30886 rect = [iconX, iconY, iconX + iconSize, iconY + iconSize];
30889 if (tryInsert(rect, entity.id)) return p;
30893 function tryInsert(rect, id) {
30894 // Check that label is visible
30895 if (rect[0] < 0 || rect[1] < 0 || rect[2] > dimensions[0] ||
30896 rect[3] > dimensions[1]) return false;
30897 var v = rtree.search(rect).length === 0;
30900 rtree.insert(rect);
30901 rectangles[id] = rect;
30906 var label = surface.selectAll('.layer-label'),
30907 halo = surface.selectAll('.layer-halo');
30910 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
30911 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
30914 drawLinePaths(halo, labelled.line, filter, '', positions.line);
30915 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
30916 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
30919 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
30920 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
30921 drawAreaIcons(label, labelled.area, filter, 'arealabel-icon', positions.area);
30924 var showDebug = context.debugCollision();
30925 var debug = label.selectAll('.layer-label-debug')
30926 .data(showDebug ? [true] : []);
30930 .attr('class', 'layer-label-debug');
30936 var gj = rtree.all().map(function(d) {
30937 return { type: 'Polygon', coordinates: [[
30946 var debugboxes = debug.selectAll('.bbox').data(gj);
30950 .attr('class', 'bbox');
30956 .attr('d', d3.geo.path().projection(null));
30960 drawLabels.supersurface = function(supersurface) {
30962 .on('mousemove.hidelabels', hideOnMouseover)
30963 .on('mousedown.hidelabels', function () {
30964 supersurface.on('mousemove.hidelabels', null);
30966 .on('mouseup.hidelabels', function () {
30967 supersurface.on('mousemove.hidelabels', hideOnMouseover);
30973 iD.svg.Layers = function(projection, context) {
30974 var dispatch = d3.dispatch('change'),
30975 svg = d3.select(null),
30977 { id: 'osm', layer: iD.svg.Osm(projection, context, dispatch) },
30978 { id: 'gpx', layer: iD.svg.Gpx(projection, context, dispatch) },
30979 { id: 'mapillary-images', layer: iD.svg.MapillaryImages(projection, context, dispatch) },
30980 { id: 'mapillary-signs', layer: iD.svg.MapillarySigns(projection, context, dispatch) }
30984 function drawLayers(selection) {
30985 svg = selection.selectAll('.surface')
30990 .attr('class', 'surface')
30993 var groups = svg.selectAll('.data-layer')
30998 .attr('class', function(d) { return 'data-layer data-layer-' + d.id; });
31001 .each(function(d) { d3.select(this).call(d.layer); });
31007 drawLayers.all = function() {
31011 drawLayers.layer = function(id) {
31012 var obj = _.find(layers, 'id', id);
31013 return obj && obj.layer;
31016 drawLayers.only = function(what) {
31017 var arr = [].concat(what);
31018 drawLayers.remove(_.difference(_.pluck(layers, 'id'), arr));
31022 drawLayers.remove = function(what) {
31023 var arr = [].concat(what);
31024 arr.forEach(function(id) {
31025 layers = _.reject(layers, 'id', id);
31031 drawLayers.add = function(what) {
31032 var arr = [].concat(what);
31033 arr.forEach(function(obj) {
31034 if ('id' in obj && 'layer' in obj) {
31042 drawLayers.dimensions = function(_) {
31043 if (!arguments.length) return svg.dimensions();
31045 layers.forEach(function(obj) {
31046 if (obj.layer.dimensions) {
31047 obj.layer.dimensions(_);
31054 return d3.rebind(drawLayers, dispatch, 'on');
31056 iD.svg.Lines = function(projection) {
31058 var highway_stack = {
31073 function waystack(a, b) {
31074 var as = 0, bs = 0;
31076 if (a.tags.highway) { as -= highway_stack[a.tags.highway]; }
31077 if (b.tags.highway) { bs -= highway_stack[b.tags.highway]; }
31081 return function drawLines(surface, graph, entities, filter) {
31082 var ways = [], pathdata = {}, onewaydata = {},
31083 getPath = iD.svg.Path(projection, graph);
31085 for (var i = 0; i < entities.length; i++) {
31086 var entity = entities[i],
31087 outer = iD.geo.simpleMultipolygonOuterMember(entity, graph);
31089 ways.push(entity.mergeTags(outer.tags));
31090 } else if (entity.geometry(graph) === 'line') {
31095 ways = ways.filter(getPath);
31097 pathdata = _.groupBy(ways, function(way) { return way.layer(); });
31099 _.forOwn(pathdata, function(v, k) {
31100 onewaydata[k] = _(v)
31101 .filter(function(d) { return d.isOneWay(); })
31102 .map(iD.svg.OneWaySegments(projection, graph, 35))
31107 var layergroup = surface
31108 .selectAll('.layer-lines')
31109 .selectAll('g.layergroup')
31110 .data(d3.range(-10, 11));
31114 .attr('class', function(d) { return 'layer layergroup layer' + String(d); });
31117 var linegroup = layergroup
31118 .selectAll('g.linegroup')
31119 .data(['shadow', 'casing', 'stroke']);
31123 .attr('class', function(d) { return 'layer linegroup line-' + d; });
31126 var lines = linegroup
31130 function() { return pathdata[this.parentNode.parentNode.__data__] || []; },
31134 // Optimization: call simple TagClasses only on enter selection. This
31135 // works because iD.Entity.key is defined to include the entity v attribute.
31138 .attr('class', function(d) { return 'way line ' + this.parentNode.__data__ + ' ' + d.id; })
31139 .call(iD.svg.TagClasses());
31143 .attr('d', getPath)
31144 .call(iD.svg.TagClasses().tags(iD.svg.RelationMemberTags(graph)));
31150 var onewaygroup = layergroup
31151 .selectAll('g.onewaygroup')
31154 onewaygroup.enter()
31156 .attr('class', 'layer onewaygroup');
31159 var oneways = onewaygroup
31163 function() { return onewaydata[this.parentNode.parentNode.__data__] || []; },
31164 function(d) { return [d.id, d.index]; }
31169 .attr('class', 'oneway')
31170 .attr('marker-mid', 'url(#oneway-marker)');
31173 .attr('d', function(d) { return d.d; });
31175 if (iD.detect().ie) {
31176 oneways.each(function() { this.parentNode.insertBefore(this, this); });
31184 iD.svg.MapillaryImages = function(projection, context, dispatch) {
31185 var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
31187 layer = d3.select(null),
31192 if (iD.svg.MapillaryImages.initialized) return; // run once
31193 iD.svg.MapillaryImages.enabled = false;
31194 iD.svg.MapillaryImages.initialized = true;
31197 function getMapillary() {
31198 if (iD.services.mapillary && !_mapillary) {
31199 _mapillary = iD.services.mapillary().on('loadedImages', debouncedRedraw);
31200 } else if (!iD.services.mapillary && _mapillary) {
31206 function showThumbnail(image) {
31207 var mapillary = getMapillary();
31208 if (!mapillary) return;
31210 var thumb = mapillary.selectedThumbnail(),
31211 posX = projection(image.loc)[0],
31212 width = layer.dimensions()[0],
31213 position = (posX < width / 2) ? 'right' : 'left';
31216 d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31217 .classed('selected', function(d) { return d.key === thumb.key; });
31220 mapillary.showThumbnail(image.key, position);
31223 function hideThumbnail() {
31224 d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31225 .classed('selected', false);
31227 var mapillary = getMapillary();
31229 mapillary.hideThumbnail();
31233 function showLayer() {
31236 .style('opacity', 0)
31239 .style('opacity', 1)
31240 .each('end', debouncedRedraw);
31243 function hideLayer() {
31244 debouncedRedraw.cancel();
31249 .style('opacity', 0)
31250 .each('end', editOff);
31253 function editOn() {
31254 layer.style('display', 'block');
31257 function editOff() {
31258 layer.selectAll('.viewfield-group').remove();
31259 layer.style('display', 'none');
31262 function transform(d) {
31263 var t = iD.svg.PointTransform(projection)(d);
31264 if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
31268 function update() {
31269 var mapillary = getMapillary(),
31270 data = (mapillary ? mapillary.images(projection, layer.dimensions()) : []);
31272 var markers = layer.selectAll('.viewfield-group')
31273 .data(data, function(d) { return d.key; });
31276 var enter = markers.enter()
31278 .attr('class', 'viewfield-group');
31280 enter.append('path')
31281 .attr('class', 'viewfield')
31282 .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
31283 .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');
31285 enter.append('circle')
31296 .attr('transform', transform);
31299 function drawImages(selection) {
31300 var enabled = iD.svg.MapillaryImages.enabled,
31301 mapillary = getMapillary();
31303 layer = selection.selectAll('.layer-mapillary-images')
31304 .data(mapillary ? [0] : []);
31308 .attr('class', 'layer-mapillary-images')
31309 .style('display', enabled ? 'block' : 'none')
31310 .on('click', function() { // deselect/select
31311 var mapillary = getMapillary();
31312 if (!mapillary) return;
31313 var d = d3.event.target.__data__,
31314 thumb = mapillary.selectedThumbnail();
31315 if (thumb && thumb.key === d.key) {
31318 mapillary.selectedThumbnail(d);
31319 context.map().centerEase(d.loc);
31323 .on('mouseover', function() {
31324 var mapillary = getMapillary();
31325 if (!mapillary) return;
31326 showThumbnail(d3.event.target.__data__);
31328 .on('mouseout', function() {
31329 var mapillary = getMapillary();
31330 if (!mapillary) return;
31331 var thumb = mapillary.selectedThumbnail();
31333 showThumbnail(thumb);
31343 if (mapillary && ~~context.map().zoom() >= minZoom) {
31346 mapillary.loadImages(projection, layer.dimensions());
31353 drawImages.enabled = function(_) {
31354 if (!arguments.length) return iD.svg.MapillaryImages.enabled;
31355 iD.svg.MapillaryImages.enabled = _;
31356 if (iD.svg.MapillaryImages.enabled) {
31365 drawImages.supported = function() {
31366 return !!getMapillary();
31369 drawImages.dimensions = function(_) {
31370 if (!arguments.length) return layer.dimensions();
31371 layer.dimensions(_);
31378 iD.svg.MapillarySigns = function(projection, context, dispatch) {
31379 var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
31381 layer = d3.select(null),
31386 if (iD.svg.MapillarySigns.initialized) return; // run once
31387 iD.svg.MapillarySigns.enabled = false;
31388 iD.svg.MapillarySigns.initialized = true;
31391 function getMapillary() {
31392 if (iD.services.mapillary && !_mapillary) {
31393 _mapillary = iD.services.mapillary().on('loadedSigns', debouncedRedraw);
31394 } else if (!iD.services.mapillary && _mapillary) {
31400 function showThumbnail(image) {
31401 var mapillary = getMapillary();
31402 if (!mapillary) return;
31404 var thumb = mapillary.selectedThumbnail(),
31405 posX = projection(image.loc)[0],
31406 width = layer.dimensions()[0],
31407 position = (posX < width / 2) ? 'right' : 'left';
31410 d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31411 .classed('selected', function(d) { return d.key === thumb.key; });
31414 mapillary.showThumbnail(image.key, position);
31417 function hideThumbnail() {
31418 d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31419 .classed('selected', false);
31421 var mapillary = getMapillary();
31423 mapillary.hideThumbnail();
31427 function showLayer() {
31432 function hideLayer() {
31433 debouncedRedraw.cancel();
31438 function editOn() {
31439 layer.style('display', 'block');
31442 function editOff() {
31443 layer.selectAll('.icon-sign').remove();
31444 layer.style('display', 'none');
31447 function update() {
31448 var mapillary = getMapillary(),
31449 data = (mapillary ? mapillary.signs(projection, layer.dimensions()) : []);
31451 var signs = layer.selectAll('.icon-sign')
31452 .data(data, function(d) { return d.key; });
31455 var enter = signs.enter()
31456 .append('foreignObject')
31457 .attr('class', 'icon-sign')
31458 .attr('width', '32px') // for Firefox
31459 .attr('height', '32px'); // for Firefox
31462 .append('xhtml:body')
31463 .html(mapillary.signHTML);
31466 .on('click', function(d) { // deselect/select
31467 var mapillary = getMapillary();
31468 if (!mapillary) return;
31469 var thumb = mapillary.selectedThumbnail();
31470 if (thumb && thumb.key === d.key) {
31473 mapillary.selectedThumbnail(d);
31474 context.map().centerEase(d.loc);
31478 .on('mouseover', showThumbnail)
31479 .on('mouseout', function() {
31480 var mapillary = getMapillary();
31481 if (!mapillary) return;
31482 var thumb = mapillary.selectedThumbnail();
31484 showThumbnail(thumb);
31496 .attr('transform', iD.svg.PointTransform(projection));
31499 function drawSigns(selection) {
31500 var enabled = iD.svg.MapillarySigns.enabled,
31501 mapillary = getMapillary();
31503 layer = selection.selectAll('.layer-mapillary-signs')
31504 .data(mapillary ? [0] : []);
31508 .attr('class', 'layer-mapillary-signs')
31509 .style('display', enabled ? 'block' : 'none')
31510 .attr('transform', 'translate(-16, -16)'); // center signs on loc
31516 if (mapillary && ~~context.map().zoom() >= minZoom) {
31519 mapillary.loadSigns(context, projection, layer.dimensions());
31526 drawSigns.enabled = function(_) {
31527 if (!arguments.length) return iD.svg.MapillarySigns.enabled;
31528 iD.svg.MapillarySigns.enabled = _;
31529 if (iD.svg.MapillarySigns.enabled) {
31538 drawSigns.supported = function() {
31539 var mapillary = getMapillary();
31540 return (mapillary && mapillary.signsSupported());
31543 drawSigns.dimensions = function(_) {
31544 if (!arguments.length) return layer.dimensions();
31545 layer.dimensions(_);
31552 iD.svg.Midpoints = function(projection, context) {
31553 return function drawMidpoints(surface, graph, entities, filter, extent) {
31554 var poly = extent.polygon(),
31557 for (var i = 0; i < entities.length; i++) {
31558 var entity = entities[i];
31560 if (entity.type !== 'way')
31562 if (!filter(entity))
31564 if (context.selectedIDs().indexOf(entity.id) < 0)
31567 var nodes = graph.childNodes(entity);
31568 for (var j = 0; j < nodes.length - 1; j++) {
31572 id = [a.id, b.id].sort().join('-');
31574 if (midpoints[id]) {
31575 midpoints[id].parents.push(entity);
31577 if (iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
31578 var point = iD.geo.interp(a.loc, b.loc, 0.5),
31581 if (extent.intersects(point)) {
31584 for (var k = 0; k < 4; k++) {
31585 point = iD.geo.lineIntersection([a.loc, b.loc], [poly[k], poly[k+1]]);
31587 iD.geo.euclideanDistance(projection(a.loc), projection(point)) > 20 &&
31588 iD.geo.euclideanDistance(projection(b.loc), projection(point)) > 20)
31601 edge: [a.id, b.id],
31610 function midpointFilter(d) {
31611 if (midpoints[d.id])
31614 for (var i = 0; i < d.parents.length; i++)
31615 if (filter(d.parents[i]))
31621 var groups = surface.selectAll('.layer-hit').selectAll('g.midpoint')
31622 .filter(midpointFilter)
31623 .data(_.values(midpoints), function(d) { return d.id; });
31625 var enter = groups.enter()
31626 .insert('g', ':first-child')
31627 .attr('class', 'midpoint');
31629 enter.append('polygon')
31630 .attr('points', '-6,8 10,0 -6,-8')
31631 .attr('class', 'shadow');
31633 enter.append('polygon')
31634 .attr('points', '-3,4 5,0 -3,-4')
31635 .attr('class', 'fill');
31638 .attr('transform', function(d) {
31639 var translate = iD.svg.PointTransform(projection),
31640 a = context.entity(d.edge[0]),
31641 b = context.entity(d.edge[1]),
31642 angle = Math.round(iD.geo.angle(a, b, projection) * (180 / Math.PI));
31643 return translate(d) + ' rotate(' + angle + ')';
31645 .call(iD.svg.TagClasses().tags(
31646 function(d) { return d.parents[0].tags; }
31649 // Propagate data bindings.
31650 groups.select('polygon.shadow');
31651 groups.select('polygon.fill');
31657 iD.svg.Osm = function() {
31658 return function drawOsm(selection) {
31659 var layers = selection.selectAll('.layer-osm')
31660 .data(['areas', 'lines', 'hit', 'halo', 'label']);
31662 layers.enter().append('g')
31663 .attr('class', function(d) { return 'layer-osm layer-' + d; });
31666 iD.svg.Points = function(projection, context) {
31667 function markerPath(selection, klass) {
31669 .attr('class', klass)
31670 .attr('transform', 'translate(-8, -23)')
31671 .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');
31674 function sortY(a, b) {
31675 return b.loc[1] - a.loc[1];
31678 return function drawPoints(surface, graph, entities, filter) {
31679 var wireframe = surface.classed('fill-wireframe'),
31680 points = wireframe ? [] : _.filter(entities, function(e) {
31681 return e.geometry(graph) === 'point';
31684 points.sort(sortY);
31686 var groups = surface.selectAll('.layer-hit').selectAll('g.point')
31688 .data(points, iD.Entity.key);
31690 var group = groups.enter()
31692 .attr('class', function(d) { return 'node point ' + d.id; })
31695 group.append('path')
31696 .call(markerPath, 'shadow');
31698 group.append('path')
31699 .call(markerPath, 'stroke');
31701 group.append('use')
31702 .attr('transform', 'translate(-6, -20)')
31703 .attr('class', 'icon')
31704 .attr('width', '12px')
31705 .attr('height', '12px');
31707 groups.attr('transform', iD.svg.PointTransform(projection))
31708 .call(iD.svg.TagClasses());
31710 // Selecting the following implicitly
31711 // sets the data (point entity) on the element
31712 groups.select('.shadow');
31713 groups.select('.stroke');
31714 groups.select('.icon')
31715 .attr('xlink:href', function(entity) {
31716 var preset = context.presets().match(entity, graph);
31717 return preset.icon ? '#' + preset.icon + '-12' : '';
31724 iD.svg.TagClasses = function() {
31726 'building', 'highway', 'railway', 'waterway', 'aeroway',
31727 'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse',
31731 'proposed', 'construction', 'disused', 'abandoned', 'dismantled',
31732 'razed', 'demolished', 'obliterated'
31735 'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
31736 'surface', 'tracktype', 'crossing'
31738 tagClassRe = /^tag-/,
31739 tags = function(entity) { return entity.tags; };
31742 var tagClasses = function(selection) {
31743 selection.each(function tagClassesEach(entity) {
31744 var value = this.className,
31745 classes, primary, status;
31747 if (value.baseVal !== undefined) value = value.baseVal;
31749 classes = value.trim().split(/\s+/).filter(function(name) {
31750 return name.length && !tagClassRe.test(name);
31753 var t = tags(entity), i, k, v;
31755 // pick at most one primary classification tag..
31756 for (i = 0; i < primaries.length; i++) {
31759 if (!v || v === 'no') continue;
31762 if (statuses.indexOf(v) !== -1) { // e.g. `railway=abandoned`
31764 classes += ' tag-' + k;
31766 classes += ' tag-' + k + ' tag-' + k + '-' + v;
31772 // add at most one status tag, only if relates to primary tag..
31774 for (i = 0; i < statuses.length; i++) {
31777 if (!v || v === 'no') continue;
31779 if (v === 'yes') { // e.g. `railway=rail + abandoned=yes`
31782 else if (primary && primary === v) { // e.g. `railway=rail + abandoned=railway`
31784 } else if (!primary && primaries.indexOf(v) !== -1) { // e.g. `abandoned=railway`
31787 classes += ' tag-' + v;
31788 } // else ignore e.g. `highway=path + abandoned=railway`
31795 classes += ' tag-status tag-status-' + status;
31798 // add any secondary (structure) tags
31799 for (i = 0; i < secondaries.length; i++) {
31800 k = secondaries[i];
31802 if (!v || v === 'no') continue;
31803 classes += ' tag-' + k + ' tag-' + k + '-' + v;
31806 // For highways, look for surface tagging..
31807 if (primary === 'highway') {
31808 var paved = (t.highway !== 'track');
31811 if (k in iD.pavedTags) {
31812 paved = !!iD.pavedTags[k][v];
31817 classes += ' tag-unpaved';
31821 classes = classes.trim();
31823 if (classes !== value) {
31824 d3.select(this).attr('class', classes);
31829 tagClasses.tags = function(_) {
31830 if (!arguments.length) return tags;
31837 iD.svg.Turns = function(projection) {
31838 return function drawTurns(surface, graph, turns) {
31839 function key(turn) {
31840 return [turn.from.node + turn.via.node + turn.to.node].join('-');
31843 function icon(turn) {
31844 var u = turn.u ? '-u' : '';
31845 if (!turn.restriction)
31846 return '#turn-yes' + u;
31847 var restriction = graph.entity(turn.restriction).tags.restriction;
31849 (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u;
31852 var groups = surface.selectAll('.layer-hit').selectAll('g.turn')
31856 var enter = groups.enter().append('g')
31857 .attr('class', 'turn');
31859 var nEnter = enter.filter(function (turn) { return !turn.u; });
31861 nEnter.append('rect')
31862 .attr('transform', 'translate(-22, -12)')
31863 .attr('width', '44')
31864 .attr('height', '24');
31866 nEnter.append('use')
31867 .attr('transform', 'translate(-22, -12)')
31868 .attr('width', '44')
31869 .attr('height', '24');
31872 var uEnter = enter.filter(function (turn) { return turn.u; });
31874 uEnter.append('circle')
31877 uEnter.append('use')
31878 .attr('transform', 'translate(-16, -16)')
31879 .attr('width', '32')
31880 .attr('height', '32');
31885 .attr('transform', function (turn) {
31886 var v = graph.entity(turn.via.node),
31887 t = graph.entity(turn.to.node),
31888 a = iD.geo.angle(v, t, projection),
31889 p = projection(v.loc),
31890 r = turn.u ? 0 : 60;
31892 return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' +
31893 'rotate(' + a * 180 / Math.PI + ')';
31896 groups.select('use')
31897 .attr('xlink:href', icon);
31899 groups.select('rect');
31900 groups.select('circle');
31910 iD.svg.Vertices = function(projection, context) {
31912 // z16-, z17, z18+, tagged
31913 shadow: [6, 7.5, 7.5, 11.5],
31914 stroke: [2.5, 3.5, 3.5, 7],
31915 fill: [1, 1.5, 1.5, 1.5]
31920 function siblingAndChildVertices(ids, graph, extent) {
31923 function addChildVertices(entity) {
31924 if (!context.features().isHiddenFeature(entity, graph, entity.geometry(graph))) {
31926 if (entity.type === 'way') {
31927 for (i = 0; i < entity.nodes.length; i++) {
31928 addChildVertices(graph.entity(entity.nodes[i]));
31930 } else if (entity.type === 'relation') {
31931 for (i = 0; i < entity.members.length; i++) {
31932 var member = context.hasEntity(entity.members[i].id);
31934 addChildVertices(member);
31937 } else if (entity.intersects(extent, graph)) {
31938 vertices[entity.id] = entity;
31943 ids.forEach(function(id) {
31944 var entity = context.hasEntity(id);
31945 if (entity && entity.type === 'node') {
31946 vertices[entity.id] = entity;
31947 context.graph().parentWays(entity).forEach(function(entity) {
31948 addChildVertices(entity);
31950 } else if (entity) {
31951 addChildVertices(entity);
31958 function draw(selection, vertices, klass, graph, zoom) {
31960 z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
31962 var groups = selection
31963 .data(vertices, iD.Entity.key);
31965 function icon(entity) {
31966 if (entity.id in icons) return icons[entity.id];
31968 entity.hasInterestingTags() &&
31969 context.presets().match(entity, graph).icon;
31970 return icons[entity.id];
31973 function setClass(klass) {
31974 return function(entity) {
31975 this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id);
31979 function setAttributes(selection) {
31980 ['shadow','stroke','fill'].forEach(function(klass) {
31981 var rads = radiuses[klass];
31982 selection.selectAll('.' + klass)
31983 .each(function(entity) {
31984 var i = z && icon(entity),
31986 r = rads[i ? 3 : z];
31987 this.setAttribute('cx', c);
31988 this.setAttribute('cy', -c);
31989 this.setAttribute('r', r);
31990 if (i && klass === 'fill') {
31991 this.setAttribute('visibility', 'hidden');
31993 this.removeAttribute('visibility');
31998 selection.selectAll('use')
32001 this.removeAttribute('visibility');
32003 this.setAttribute('visibility', 'hidden');
32008 var enter = groups.enter()
32010 .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; });
32012 enter.append('circle')
32013 .each(setClass('shadow'));
32015 enter.append('circle')
32016 .each(setClass('stroke'));
32018 // Vertices with icons get a `use`.
32019 enter.filter(function(d) { return icon(d); })
32021 .attr('transform', 'translate(-6, -6)')
32022 .attr('xlink:href', function(d) { return '#' + icon(d) + '-12'; })
32023 .attr('width', '12px')
32024 .attr('height', '12px')
32025 .each(setClass('icon'));
32027 // Vertices with tags get a fill.
32028 enter.filter(function(d) { return d.hasInterestingTags(); })
32030 .each(setClass('fill'));
32033 .attr('transform', iD.svg.PointTransform(projection))
32034 .classed('shared', function(entity) { return graph.isShared(entity); })
32035 .call(setAttributes);
32041 function drawVertices(surface, graph, entities, filter, extent, zoom) {
32042 var selected = siblingAndChildVertices(context.selectedIDs(), graph, extent),
32043 wireframe = surface.classed('fill-wireframe'),
32046 for (var i = 0; i < entities.length; i++) {
32047 var entity = entities[i],
32048 geometry = entity.geometry(graph);
32050 if (wireframe && geometry === 'point') {
32051 vertices.push(entity);
32055 if (geometry !== 'vertex')
32058 if (entity.id in selected ||
32059 entity.hasInterestingTags() ||
32060 entity.isIntersection(graph)) {
32061 vertices.push(entity);
32065 surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-persistent')
32067 .call(draw, vertices, 'vertex-persistent', graph, zoom);
32069 drawHover(surface, graph, extent, zoom);
32072 function drawHover(surface, graph, extent, zoom) {
32073 var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent) : {};
32075 surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-hover')
32076 .call(draw, d3.values(hovered), 'vertex-hover', graph, zoom);
32079 drawVertices.drawHover = function(surface, graph, target, extent, zoom) {
32080 if (target === hover) return;
32082 drawHover(surface, graph, extent, zoom);
32085 return drawVertices;
32087 iD.ui = function(context) {
32088 function render(container) {
32089 var map = context.map();
32091 if (iD.detect().opera) container.classed('opera', true);
32093 var hash = iD.behavior.Hash(context);
32097 if (!hash.hadHash) {
32098 map.centerZoom([0, 0], 2);
32101 container.append('svg')
32102 .attr('id', 'defs')
32103 .call(iD.svg.Defs(context));
32105 container.append('div')
32106 .attr('id', 'sidebar')
32107 .attr('class', 'col4')
32110 var content = container.append('div')
32111 .attr('id', 'content');
32113 var bar = content.append('div')
32115 .attr('class', 'fillD');
32117 content.append('div')
32122 .call(iD.ui.MapInMap(context));
32124 content.append('div')
32125 .call(iD.ui.Info(context));
32128 .attr('class', 'spacer col4');
32130 var limiter = bar.append('div')
32131 .attr('class', 'limiter');
32133 limiter.append('div')
32134 .attr('class', 'button-wrap joined col3')
32135 .call(iD.ui.Modes(context), limiter);
32137 limiter.append('div')
32138 .attr('class', 'button-wrap joined col1')
32139 .call(iD.ui.UndoRedo(context));
32141 limiter.append('div')
32142 .attr('class', 'button-wrap col1')
32143 .call(iD.ui.Save(context));
32146 .attr('class', 'full-screen')
32147 .call(iD.ui.FullScreen(context));
32150 .attr('class', 'spinner')
32151 .call(iD.ui.Spinner(context));
32153 var controls = bar.append('div')
32154 .attr('class', 'map-controls');
32156 controls.append('div')
32157 .attr('class', 'map-control zoombuttons')
32158 .call(iD.ui.Zoom(context));
32160 controls.append('div')
32161 .attr('class', 'map-control geolocate-control')
32162 .call(iD.ui.Geolocate(context));
32164 controls.append('div')
32165 .attr('class', 'map-control background-control')
32166 .call(iD.ui.Background(context));
32168 controls.append('div')
32169 .attr('class', 'map-control map-data-control')
32170 .call(iD.ui.MapData(context));
32172 controls.append('div')
32173 .attr('class', 'map-control help-control')
32174 .call(iD.ui.Help(context));
32176 var about = content.append('div')
32177 .attr('id', 'about');
32179 about.append('div')
32180 .attr('id', 'attrib')
32181 .call(iD.ui.Attribution(context));
32183 var footer = about.append('div')
32184 .attr('id', 'footer')
32185 .attr('class', 'fillD');
32187 footer.append('div')
32188 .attr('class', 'api-status')
32189 .call(iD.ui.Status(context));
32191 footer.append('div')
32192 .attr('id', 'scale-block')
32193 .call(iD.ui.Scale(context));
32195 var aboutList = footer.append('div')
32196 .attr('id', 'info-block')
32198 .attr('id', 'about-list');
32200 if (!context.embed()) {
32201 aboutList.call(iD.ui.Account(context));
32204 aboutList.append('li')
32206 .attr('target', '_blank')
32207 .attr('tabindex', -1)
32208 .attr('href', 'https://github.com/openstreetmap/iD')
32211 var issueLinks = aboutList.append('li');
32213 issueLinks.append('a')
32214 .attr('target', '_blank')
32215 .attr('tabindex', -1)
32216 .attr('href', 'https://github.com/openstreetmap/iD/issues')
32217 .call(iD.svg.Icon('#icon-bug', 'light'))
32218 .call(bootstrap.tooltip()
32219 .title(t('report_a_bug'))
32223 issueLinks.append('a')
32224 .attr('target', '_blank')
32225 .attr('tabindex', -1)
32226 .attr('href', 'https://github.com/openstreetmap/iD/blob/master/CONTRIBUTING.md#translating')
32227 .call(iD.svg.Icon('#icon-translate', 'light'))
32228 .call(bootstrap.tooltip()
32229 .title(t('help_translate'))
32233 aboutList.append('li')
32234 .attr('class', 'feature-warning')
32235 .attr('tabindex', -1)
32236 .call(iD.ui.FeatureInfo(context));
32238 aboutList.append('li')
32239 .attr('class', 'user-list')
32240 .attr('tabindex', -1)
32241 .call(iD.ui.Contributors(context));
32243 window.onbeforeunload = function() {
32244 return context.save();
32247 window.onunload = function() {
32248 context.history().unlock();
32251 var mapDimensions = map.dimensions();
32253 d3.select(window).on('resize.editor', function() {
32254 mapDimensions = content.dimensions(null);
32255 map.dimensions(mapDimensions);
32259 return function() {
32260 d3.event.preventDefault();
32261 if (!context.inIntro()) context.pan(d);
32268 var keybinding = d3.keybinding('main')
32269 .on('⌫', function() { d3.event.preventDefault(); })
32270 .on('←', pan([pa, 0]))
32271 .on('↑', pan([0, pa]))
32272 .on('→', pan([-pa, 0]))
32273 .on('↓', pan([0, -pa]))
32274 .on('⇧←', pan([mapDimensions[0], 0]))
32275 .on('⇧↑', pan([0, mapDimensions[1]]))
32276 .on('⇧→', pan([-mapDimensions[0], 0]))
32277 .on('⇧↓', pan([0, -mapDimensions[1]]))
32278 .on(iD.ui.cmd('⌘←'), pan([mapDimensions[0], 0]))
32279 .on(iD.ui.cmd('⌘↑'), pan([0, mapDimensions[1]]))
32280 .on(iD.ui.cmd('⌘→'), pan([-mapDimensions[0], 0]))
32281 .on(iD.ui.cmd('⌘↓'), pan([0, -mapDimensions[1]]));
32283 d3.select(document)
32286 context.enter(iD.modes.Browse(context));
32288 context.container()
32289 .call(iD.ui.Splash(context))
32290 .call(iD.ui.Restore(context));
32292 var authenticating = iD.ui.Loading(context)
32293 .message(t('loading_auth'));
32295 context.connection()
32296 .on('authenticating.ui', function() {
32297 context.container()
32298 .call(authenticating);
32300 .on('authenticated.ui', function() {
32301 authenticating.close();
32305 function ui(container) {
32306 context.container(container);
32307 context.loadLocale(function() {
32312 ui.sidebar = iD.ui.Sidebar(context);
32317 iD.ui.tooltipHtml = function(text, key) {
32318 var s = '<span>' + text + '</span>';
32320 s += '<div class="keyhint-wrap">' +
32321 '<span> ' + (t('tooltip_keyhint')) + ' </span>' +
32322 '<span class="keyhint"> ' + key + '</span></div>';
32326 iD.ui.Account = function(context) {
32327 var connection = context.connection();
32329 function update(selection) {
32330 if (!connection.authenticated()) {
32331 selection.selectAll('#userLink, #logoutLink')
32332 .classed('hide', true);
32336 connection.userDetails(function(err, details) {
32337 var userLink = selection.select('#userLink'),
32338 logoutLink = selection.select('#logoutLink');
32341 logoutLink.html('');
32345 selection.selectAll('#userLink, #logoutLink')
32346 .classed('hide', false);
32349 userLink.append('a')
32350 .attr('href', connection.userURL(details.display_name))
32351 .attr('target', '_blank');
32353 // Add thumbnail or dont
32354 if (details.image_url) {
32355 userLink.append('img')
32356 .attr('class', 'icon pre-text user-icon')
32357 .attr('src', details.image_url);
32360 .call(iD.svg.Icon('#icon-avatar', 'pre-text light'));
32364 userLink.append('span')
32365 .attr('class', 'label')
32366 .text(details.display_name);
32368 logoutLink.append('a')
32369 .attr('class', 'logout')
32372 .on('click.logout', function() {
32373 d3.event.preventDefault();
32374 connection.logout();
32379 return function(selection) {
32380 selection.append('li')
32381 .attr('id', 'logoutLink')
32382 .classed('hide', true);
32384 selection.append('li')
32385 .attr('id', 'userLink')
32386 .classed('hide', true);
32388 connection.on('auth.account', function() { update(selection); });
32392 iD.ui.Attribution = function(context) {
32395 function attribution(data, klass) {
32396 var div = selection.selectAll('.' + klass)
32401 .attr('class', klass);
32403 var background = div.selectAll('.attribution')
32404 .data(data, function(d) { return d.name(); });
32408 .attr('class', 'attribution')
32409 .each(function(d) {
32410 if (d.terms_html) {
32412 .html(d.terms_html);
32416 var source = d.terms_text || d.id || d.name();
32419 source = '<img class="source-image" src="' + context.imagePath(d.logo) + '">';
32425 .attr('href', d.terms_url)
32426 .attr('target', '_blank')
32437 var copyright = background.selectAll('.copyright-notice')
32438 .data(function(d) {
32439 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
32440 return notice ? [notice] : [];
32445 .attr('class', 'copyright-notice');
32447 copyright.text(String);
32453 function update() {
32454 attribution([context.background().baseLayerSource()], 'base-layer-attribution');
32455 attribution(context.background().overlayLayerSources().filter(function (s) {
32456 return s.validZoom(context.map().zoom());
32457 }), 'overlay-layer-attribution');
32460 return function(select) {
32461 selection = select;
32463 context.background()
32464 .on('change.attribution', update);
32467 .on('move.attribution', _.throttle(update, 400, {leading: false}));
32472 iD.ui.Background = function(context) {
32474 opacities = [1, 0.75, 0.5, 0.25],
32476 ['right', [0.5, 0]],
32477 ['top', [0, -0.5]],
32478 ['left', [-0.5, 0]],
32479 ['bottom', [0, 0.5]]],
32480 opacityDefault = (context.storage('background-opacity') !== null) ?
32481 (+context.storage('background-opacity')) : 1.0,
32482 customTemplate = context.storage('background-custom-template') || '';
32484 // Can be 0 from <1.3.0 use or due to issue #1923.
32485 if (opacityDefault === 0) opacityDefault = 1.0;
32487 function background(selection) {
32489 function sortSources(a, b) {
32490 return a.best() ? -1
32492 : a.id === 'none' ? 1
32493 : b.id === 'none' ? -1
32494 : d3.ascending(a, b);
32497 function setOpacity(d) {
32498 var bg = context.container().selectAll('.layer-background')
32500 .style('opacity', d)
32501 .attr('data-opacity', d);
32503 if (!iD.detect().opera) {
32504 iD.util.setTransform(bg, 0, 0);
32507 opacityList.selectAll('li')
32508 .classed('active', function(_) { return _ === d; });
32510 context.storage('background-opacity', d);
32513 function selectLayer() {
32514 function active(d) {
32515 return context.background().showsLayer(d);
32518 content.selectAll('.layer, .custom_layer')
32519 .classed('active', active)
32520 .selectAll('input')
32521 .property('checked', active);
32524 function clickSetSource(d) {
32525 d3.event.preventDefault();
32526 context.background().baseLayerSource(d);
32530 function editCustom() {
32531 d3.event.preventDefault();
32532 var template = window.prompt(t('background.custom_prompt'), customTemplate);
32534 template.indexOf('google.com') !== -1 ||
32535 template.indexOf('googleapis.com') !== -1 ||
32536 template.indexOf('google.ru') !== -1) {
32540 setCustom(template);
32543 function setCustom(template) {
32544 context.background().baseLayerSource(iD.BackgroundSource.Custom(template));
32546 context.storage('background-custom-template', template);
32549 function clickSetOverlay(d) {
32550 d3.event.preventDefault();
32551 context.background().toggleOverlayLayer(d);
32555 function drawList(layerList, type, change, filter) {
32556 var sources = context.background()
32557 .sources(context.map().extent())
32560 var layerLinks = layerList.selectAll('li.layer')
32561 .data(sources, function(d) { return d.name(); })
32562 .sort(sortSources);
32564 var enter = layerLinks.enter()
32565 .insert('li', '.custom_layer')
32566 .attr('class', 'layer')
32567 .classed('best', function(d) { return d.best(); });
32569 // only set tooltips for layers with tooltips
32570 enter.filter(function(d) { return d.description; })
32571 .call(bootstrap.tooltip()
32572 .title(function(d) { return d.description; })
32573 .placement('top'));
32575 enter.filter(function(d) { return d.best(); })
32577 .attr('class', 'best')
32578 .call(bootstrap.tooltip()
32579 .title(t('background.best_imagery'))
32580 .placement('left'))
32584 var label = enter.append('label');
32586 label.append('input')
32587 .attr('type', type)
32588 .attr('name', 'layers')
32589 .on('change', change);
32591 label.append('span')
32592 .text(function(d) { return d.name(); });
32597 layerList.style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
32600 function update() {
32601 backgroundList.call(drawList, 'radio', clickSetSource, function(d) { return !d.overlay; });
32602 overlayList.call(drawList, 'checkbox', clickSetOverlay, function(d) { return d.overlay; });
32606 var source = context.background().baseLayerSource();
32607 if (source.id === 'custom') {
32608 customTemplate = source.template;
32614 function updateOffsetVal() {
32615 var meters = iD.geo.offsetToMeters(context.background().offset()),
32616 x = +meters[0].toFixed(2),
32617 y = +meters[1].toFixed(2);
32619 d3.selectAll('.nudge-inner-rect')
32621 .classed('error', false)
32622 .property('value', x + ', ' + y);
32624 d3.selectAll('.nudge-reset')
32625 .classed('disabled', function() {
32626 return (x === 0 && y === 0);
32630 function resetOffset() {
32631 context.background().offset([0, 0]);
32635 function nudge(d) {
32636 context.background().nudge(d, context.map().zoom());
32640 function buttonOffset(d) {
32641 var timeout = window.setTimeout(function() {
32642 interval = window.setInterval(nudge.bind(null, d), 100);
32646 d3.select(window).on('mouseup', function() {
32647 window.clearInterval(interval);
32648 window.clearTimeout(timeout);
32649 d3.select(window).on('mouseup', null);
32655 function inputOffset() {
32656 var input = d3.select(this);
32657 var d = input.node().value;
32659 if (d === '') return resetOffset();
32661 d = d.replace(/;/g, ',').split(',').map(function(n) {
32662 // if n is NaN, it will always get mapped to false.
32663 return !isNaN(n) && n;
32666 if (d.length !== 2 || !d[0] || !d[1]) {
32667 input.classed('error', true);
32671 context.background().offset(iD.geo.metersToOffset(d));
32675 function dragOffset() {
32676 var origin = [d3.event.clientX, d3.event.clientY];
32679 .on('mousemove.offset', function() {
32680 var latest = [d3.event.clientX, d3.event.clientY];
32682 -(origin[0] - latest[0]) / 4,
32683 -(origin[1] - latest[1]) / 4
32689 .on('mouseup.offset', function() {
32691 .on('mousemove.offset', null)
32692 .on('mouseup.offset', null);
32695 d3.event.preventDefault();
32702 function toggle() {
32703 if (d3.event) d3.event.preventDefault();
32704 tooltip.hide(button);
32705 setVisible(!button.classed('active'));
32708 function setVisible(show) {
32709 if (show !== shown) {
32710 button.classed('active', show);
32714 selection.on('mousedown.background-inside', function() {
32715 return d3.event.stopPropagation();
32717 content.style('display', 'block')
32718 .style('right', '-300px')
32721 .style('right', '0px');
32723 content.style('display', 'block')
32724 .style('right', '0px')
32727 .style('right', '-300px')
32728 .each('end', function() {
32729 d3.select(this).style('display', 'none');
32731 selection.on('mousedown.background-inside', null);
32737 var content = selection.append('div')
32738 .attr('class', 'fillL map-overlay col3 content hide'),
32739 tooltip = bootstrap.tooltip()
32742 .title(iD.ui.tooltipHtml(t('background.description'), key)),
32743 button = selection.append('button')
32744 .attr('tabindex', -1)
32745 .on('click', toggle)
32746 .call(iD.svg.Icon('#icon-layers', 'light'))
32751 /* opacity switcher */
32753 var opa = content.append('div')
32754 .attr('class', 'opacity-options-wrapper');
32757 .text(t('background.title'));
32759 var opacityList = opa.append('ul')
32760 .attr('class', 'opacity-options');
32762 opacityList.selectAll('div.opacity')
32766 .attr('data-original-title', function(d) {
32767 return t('background.percent_brightness', { opacity: (d * 100) });
32769 .on('click.set-opacity', setOpacity)
32770 .html('<div class="select-box"></div>')
32771 .call(bootstrap.tooltip()
32772 .placement('left'))
32774 .attr('class', 'opacity')
32775 .style('opacity', function(d) { return 1.25 - d; });
32778 /* background switcher */
32780 var backgroundList = content.append('ul')
32781 .attr('class', 'layer-list');
32783 var custom = backgroundList.append('li')
32784 .attr('class', 'custom_layer')
32785 .datum(iD.BackgroundSource.Custom());
32787 custom.append('button')
32788 .attr('class', 'layer-browse')
32789 .call(bootstrap.tooltip()
32790 .title(t('background.custom_button'))
32791 .placement('left'))
32792 .on('click', editCustom)
32793 .call(iD.svg.Icon('#icon-search'));
32795 var label = custom.append('label');
32797 label.append('input')
32798 .attr('type', 'radio')
32799 .attr('name', 'layers')
32800 .on('change', function () {
32801 if (customTemplate) {
32802 setCustom(customTemplate);
32808 label.append('span')
32809 .text(t('background.custom'));
32811 content.append('div')
32812 .attr('class', 'imagery-faq')
32814 .attr('target', '_blank')
32815 .attr('tabindex', -1)
32816 .call(iD.svg.Icon('#icon-out-link', 'inline'))
32817 .attr('href', 'https://github.com/openstreetmap/iD/blob/master/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
32819 .text(t('background.imagery_source_faq'));
32821 var overlayList = content.append('ul')
32822 .attr('class', 'layer-list');
32824 var controls = content.append('div')
32825 .attr('class', 'controls-list');
32828 /* minimap toggle */
32830 var minimapLabel = controls
32832 .call(bootstrap.tooltip()
32834 .title(iD.ui.tooltipHtml(t('background.minimap.tooltip'), '/'))
32838 minimapLabel.classed('minimap-toggle', true)
32840 .attr('type', 'checkbox')
32841 .on('change', function() {
32842 iD.ui.MapInMap.toggle();
32843 d3.event.preventDefault();
32846 minimapLabel.append('span')
32847 .text(t('background.minimap.description'));
32850 /* imagery offset controls */
32852 var adjustments = content.append('div')
32853 .attr('class', 'adjustments');
32855 adjustments.append('a')
32856 .text(t('background.fix_misalignment'))
32858 .classed('hide-toggle', true)
32859 .classed('expanded', false)
32860 .on('click', function() {
32861 var exp = d3.select(this).classed('expanded');
32862 nudgeContainer.style('display', exp ? 'none' : 'block');
32863 d3.select(this).classed('expanded', !exp);
32864 d3.event.preventDefault();
32867 var nudgeContainer = adjustments.append('div')
32868 .attr('class', 'nudge-container cf')
32869 .style('display', 'none');
32871 nudgeContainer.append('div')
32872 .attr('class', 'nudge-instructions')
32873 .text(t('background.offset'));
32875 var nudgeRect = nudgeContainer.append('div')
32876 .attr('class', 'nudge-outer-rect')
32877 .on('mousedown', dragOffset);
32879 nudgeRect.append('div')
32880 .attr('class', 'nudge-inner-rect')
32882 .on('change', inputOffset)
32883 .on('mousedown', function() {
32884 d3.event.stopPropagation();
32887 nudgeContainer.append('div')
32888 .selectAll('button')
32889 .data(directions).enter()
32891 .attr('class', function(d) { return d[0] + ' nudge'; })
32892 .on('mousedown', function(d) {
32893 buttonOffset(d[1]);
32896 nudgeContainer.append('button')
32897 .attr('title', t('background.reset'))
32898 .attr('class', 'nudge-reset disabled')
32899 .on('click', resetOffset)
32900 .call(iD.svg.Icon('#icon-undo'));
32903 .on('move.background-update', _.debounce(update, 1000));
32905 context.background()
32906 .on('change.background-update', update);
32910 setOpacity(opacityDefault);
32912 var keybinding = d3.keybinding('background')
32917 d3.select(document)
32920 context.surface().on('mousedown.background-outside', hide);
32921 context.container().on('mousedown.background-outside', hide);
32926 // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
32927 // For example, ⌘Z -> Ctrl+Z
32928 iD.ui.cmd = function(code) {
32929 if (iD.detect().os === 'mac') {
32933 if (iD.detect().os === 'win') {
32934 if (code === '⌘⇧Z') return 'Ctrl+Y';
32946 for (var i = 0; i < code.length; i++) {
32947 if (code[i] in replacements) {
32948 result += replacements[code[i]] + '+';
32956 iD.ui.Commit = function(context) {
32957 var dispatch = d3.dispatch('cancel', 'save');
32959 function commit(selection) {
32960 var changes = context.history().changes(),
32961 summary = context.history().difference().summary();
32963 function zoomToEntity(change) {
32964 var entity = change.entity;
32965 if (change.changeType !== 'deleted' &&
32966 context.graph().entity(entity.id).geometry(context.graph()) !== 'vertex') {
32967 context.map().zoomTo(entity);
32968 context.surface().selectAll(
32969 iD.util.entityOrMemberSelector([entity.id], context.graph()))
32970 .classed('hover', true);
32974 var header = selection.append('div')
32975 .attr('class', 'header fillL');
32977 header.append('h3')
32978 .text(t('commit.title'));
32980 var body = selection.append('div')
32981 .attr('class', 'body');
32985 var commentSection = body.append('div')
32986 .attr('class', 'modal-section form-field commit-form');
32988 commentSection.append('label')
32989 .attr('class', 'form-label')
32990 .text(t('commit.message_label'));
32992 var commentField = commentSection.append('textarea')
32993 .attr('placeholder', t('commit.description_placeholder'))
32994 .attr('maxlength', 255)
32995 .property('value', context.storage('comment') || '')
32996 .on('input.save', checkComment)
32997 .on('change.save', checkComment)
32998 .on('blur.save', function() {
32999 context.storage('comment', this.value);
33002 function checkComment() {
33003 d3.selectAll('.save-section .save-button')
33004 .attr('disabled', (this.value.length ? null : true));
33006 var googleWarning = clippyArea
33009 .data(this.value.match(/google/i) ? [true] : []);
33011 googleWarning.exit().remove();
33013 googleWarning.enter()
33015 .attr('target', '_blank')
33016 .attr('tabindex', -1)
33017 .call(iD.svg.Icon('#icon-alert', 'inline'))
33018 .attr('href', t('commit.google_warning_link'))
33020 .text(t('commit.google_warning'));
33023 commentField.node().select();
33025 context.connection().userChangesets(function (err, changesets) {
33030 for (var i = 0; i < changesets.length; i++) {
33031 if (changesets[i].tags.comment) {
33033 title: changesets[i].tags.comment,
33034 value: changesets[i].tags.comment
33039 commentField.call(d3.combobox().caseSensitive(true).data(comments));
33042 var clippyArea = commentSection.append('div')
33043 .attr('class', 'clippy-area');
33046 var changeSetInfo = commentSection.append('div')
33047 .attr('class', 'changeset-info');
33049 changeSetInfo.append('a')
33050 .attr('target', '_blank')
33051 .attr('tabindex', -1)
33052 .call(iD.svg.Icon('#icon-out-link', 'inline'))
33053 .attr('href', t('commit.about_changeset_comments_link'))
33055 .text(t('commit.about_changeset_comments'));
33058 var warnings = body.selectAll('div.warning-section')
33059 .data([context.history().validate(changes)])
33062 .attr('class', 'modal-section warning-section fillL2')
33063 .style('display', function(d) { return _.isEmpty(d) ? 'none' : null; })
33064 .style('background', '#ffb');
33066 warnings.append('h3')
33067 .text(t('commit.warnings'));
33069 var warningLi = warnings.append('ul')
33070 .attr('class', 'changeset-list')
33072 .data(function(d) { return d; })
33076 .on('mouseover', mouseover)
33077 .on('mouseout', mouseout)
33078 .on('click', warningClick);
33081 .call(iD.svg.Icon('#icon-alert', 'pre-text'));
33084 .append('strong').text(function(d) {
33088 warningLi.filter(function(d) { return d.tooltip; })
33089 .call(bootstrap.tooltip()
33090 .title(function(d) { return d.tooltip; })
33095 // Upload Explanation
33096 var saveSection = body.append('div')
33097 .attr('class','modal-section save-section fillL cf');
33099 var prose = saveSection.append('p')
33100 .attr('class', 'commit-info')
33101 .html(t('commit.upload_explanation'));
33103 context.connection().userDetails(function(err, user) {
33106 var userLink = d3.select(document.createElement('div'));
33108 if (user.image_url) {
33109 userLink.append('img')
33110 .attr('src', user.image_url)
33111 .attr('class', 'icon pre-text user-icon');
33114 userLink.append('a')
33115 .attr('class','user-info')
33116 .text(user.display_name)
33117 .attr('href', context.connection().userURL(user.display_name))
33118 .attr('tabindex', -1)
33119 .attr('target', '_blank');
33121 prose.html(t('commit.upload_explanation_with_user', {user: userLink.html()}));
33126 var buttonSection = saveSection.append('div')
33127 .attr('class','buttons fillL cf');
33129 var cancelButton = buttonSection.append('button')
33130 .attr('class', 'secondary-action col5 button cancel-button')
33131 .on('click.cancel', function() { dispatch.cancel(); });
33133 cancelButton.append('span')
33134 .attr('class', 'label')
33135 .text(t('commit.cancel'));
33137 var saveButton = buttonSection.append('button')
33138 .attr('class', 'action col5 button save-button')
33139 .attr('disabled', function() {
33140 var n = d3.select('.commit-form textarea').node();
33141 return (n && n.value.length) ? null : true;
33143 .on('click.save', function() {
33145 comment: commentField.node().value
33149 saveButton.append('span')
33150 .attr('class', 'label')
33151 .text(t('commit.save'));
33155 var changeSection = body.selectAll('div.commit-section')
33159 .attr('class', 'commit-section modal-section fillL2');
33161 changeSection.append('h3')
33162 .text(t('commit.changes', {count: summary.length}));
33164 var li = changeSection.append('ul')
33165 .attr('class', 'changeset-list')
33170 .on('mouseover', mouseover)
33171 .on('mouseout', mouseout)
33172 .on('click', zoomToEntity);
33174 li.each(function(d) {
33176 .call(iD.svg.Icon('#icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
33180 .attr('class', 'change-type')
33181 .text(function(d) {
33182 return t('commit.' + d.changeType) + ' ';
33185 li.append('strong')
33186 .attr('class', 'entity-type')
33187 .text(function(d) {
33188 return context.presets().match(d.entity, d.graph).name();
33192 .attr('class', 'entity-name')
33193 .text(function(d) {
33194 var name = iD.util.displayName(d.entity) || '',
33196 if (name !== '') string += ':';
33197 return string += ' ' + name;
33200 li.style('opacity', 0)
33202 .style('opacity', 1);
33205 function mouseover(d) {
33207 context.surface().selectAll(
33208 iD.util.entityOrMemberSelector([d.entity.id], context.graph())
33209 ).classed('hover', true);
33213 function mouseout() {
33214 context.surface().selectAll('.hover')
33215 .classed('hover', false);
33218 function warningClick(d) {
33220 context.map().zoomTo(d.entity);
33222 iD.modes.Select(context, [d.entity.id])
33223 .suppressMenu(true));
33227 // Call checkComment off the bat, in case a changeset
33228 // comment is recovered from localStorage
33229 commentField.trigger('input');
33232 return d3.rebind(commit, dispatch, 'on');
33234 iD.ui.confirm = function(selection) {
33235 var modal = iD.ui.modal(selection);
33237 modal.select('.modal')
33238 .classed('modal-alert', true);
33240 var section = modal.select('.content');
33242 section.append('div')
33243 .attr('class', 'modal-section header');
33245 section.append('div')
33246 .attr('class', 'modal-section message-text');
33248 var buttons = section.append('div')
33249 .attr('class', 'modal-section buttons cf');
33251 modal.okButton = function() {
33254 .attr('class', 'action col4')
33255 .on('click.confirm', function() {
33258 .text(t('confirm.okay'));
33265 iD.ui.Conflicts = function(context) {
33266 var dispatch = d3.dispatch('download', 'cancel', 'save'),
33269 function conflicts(selection) {
33270 var header = selection
33272 .attr('class', 'header fillL');
33276 .attr('class', 'fr')
33277 .on('click', function() { dispatch.cancel(); })
33278 .call(iD.svg.Icon('#icon-close'));
33282 .text(t('save.conflict.header'));
33284 var body = selection
33286 .attr('class', 'body fillL');
33290 .attr('class', 'conflicts-help')
33291 .text(t('save.conflict.help'))
33293 .attr('class', 'conflicts-download')
33294 .text(t('save.conflict.download_changes'))
33295 .on('click.download', function() { dispatch.download(); });
33299 .attr('class', 'conflict-container fillL3')
33300 .call(showConflict, 0);
33304 .attr('class', 'conflicts-done')
33305 .attr('opacity', 0)
33306 .style('display', 'none')
33307 .text(t('save.conflict.done'));
33311 .attr('class','buttons col12 joined conflicts-buttons');
33315 .attr('disabled', list.length > 1)
33316 .attr('class', 'action conflicts-button col6')
33317 .text(t('save.title'))
33318 .on('click.try_again', function() { dispatch.save(); });
33322 .attr('class', 'secondary-action conflicts-button col6')
33323 .text(t('confirm.cancel'))
33324 .on('click.cancel', function() { dispatch.cancel(); });
33328 function showConflict(selection, index) {
33329 if (index < 0 || index >= list.length) return;
33331 var parent = d3.select(selection.node().parentNode);
33333 // enable save button if this is the last conflict being reviewed..
33334 if (index === list.length - 1) {
33335 window.setTimeout(function() {
33336 parent.select('.conflicts-button')
33337 .attr('disabled', null);
33339 parent.select('.conflicts-done')
33341 .attr('opacity', 1)
33342 .style('display', 'block');
33346 var item = selection
33347 .selectAll('.conflict')
33348 .data([list[index]]);
33350 var enter = item.enter()
33352 .attr('class', 'conflict');
33356 .attr('class', 'conflict-count')
33357 .text(t('save.conflict.count', { num: index + 1, total: list.length }));
33361 .attr('class', 'conflict-description')
33363 .text(function(d) { return d.name; })
33364 .on('click', function(d) {
33365 zoomToEntity(d.id);
33366 d3.event.preventDefault();
33369 var details = enter
33371 .attr('class', 'conflict-detail-container');
33375 .attr('class', 'conflict-detail-list')
33377 .data(function(d) { return d.details || []; })
33380 .attr('class', 'conflict-detail-item')
33381 .html(function(d) { return d; });
33385 .attr('class', 'conflict-choices')
33390 .attr('class', 'conflict-nav-buttons joined cf')
33391 .selectAll('button')
33392 .data(['previous', 'next'])
33395 .text(function(d) { return t('save.conflict.' + d); })
33396 .attr('class', 'conflict-nav-button action col6')
33397 .attr('disabled', function(d, i) {
33398 return (i === 0 && index === 0) ||
33399 (i === 1 && index === list.length - 1) || null;
33401 .on('click', function(d, i) {
33402 var container = parent.select('.conflict-container'),
33403 sign = (i === 0 ? -1 : 1);
33406 .selectAll('.conflict')
33410 .call(showConflict, index + sign);
33412 d3.event.preventDefault();
33420 function addChoices(selection) {
33421 var choices = selection
33423 .attr('class', 'layer-list')
33425 .data(function(d) { return d.choices || []; });
33427 var enter = choices.enter()
33429 .attr('class', 'layer');
33436 .attr('type', 'radio')
33437 .attr('name', function(d) { return d.id; })
33438 .on('change', function(d, i) {
33439 var ul = this.parentNode.parentNode.parentNode;
33440 ul.__data__.chosen = i;
33446 .text(function(d) { return d.text; });
33449 .each(function(d, i) {
33450 var ul = this.parentNode;
33451 if (ul.__data__.chosen === i) choose(ul, d);
33455 function choose(ul, datum) {
33456 if (d3.event) d3.event.preventDefault();
33460 .classed('active', function(d) { return d === datum; })
33461 .selectAll('input')
33462 .property('checked', function(d) { return d === datum; });
33464 var extent = iD.geo.Extent(),
33467 entity = context.graph().hasEntity(datum.id);
33468 if (entity) extent._extend(entity.extent(context.graph()));
33472 entity = context.graph().hasEntity(datum.id);
33473 if (entity) extent._extend(entity.extent(context.graph()));
33475 zoomToEntity(datum.id, extent);
33478 function zoomToEntity(id, extent) {
33479 context.surface().selectAll('.hover')
33480 .classed('hover', false);
33482 var entity = context.graph().hasEntity(id);
33485 context.map().trimmedExtent(extent);
33487 context.map().zoomTo(entity);
33489 context.surface().selectAll(
33490 iD.util.entityOrMemberSelector([entity.id], context.graph()))
33491 .classed('hover', true);
33496 // The conflict list should be an array of objects like:
33499 // name: entityName(local),
33500 // details: merge.conflicts(),
33503 // choice(id, keepMine, forceLocal),
33504 // choice(id, keepTheirs, forceRemote)
33507 conflicts.list = function(_) {
33508 if (!arguments.length) return list;
33513 return d3.rebind(conflicts, dispatch, 'on');
33515 iD.ui.Contributors = function(context) {
33516 var debouncedUpdate = _.debounce(function() { update(); }, 1000),
33519 wrap = d3.select(null);
33521 function update() {
33523 entities = context.intersects(context.map().extent());
33525 entities.forEach(function(entity) {
33526 if (entity && entity.user) users[entity.user] = true;
33529 var u = Object.keys(users),
33530 subset = u.slice(0, u.length > limit ? limit - 1 : limit);
33533 .call(iD.svg.Icon('#icon-nearby', 'pre-text light'));
33535 var userList = d3.select(document.createElement('span'));
33537 userList.selectAll()
33541 .attr('class', 'user-link')
33542 .attr('href', function(d) { return context.connection().userURL(d); })
33543 .attr('target', '_blank')
33544 .attr('tabindex', -1)
33547 if (u.length > limit) {
33548 var count = d3.select(document.createElement('span'));
33551 .attr('target', '_blank')
33552 .attr('tabindex', -1)
33553 .attr('href', function() {
33554 return context.connection().changesetsURL(context.map().center(), context.map().zoom());
33556 .text(u.length - limit + 1);
33558 wrap.append('span')
33559 .html(t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
33562 wrap.append('span')
33563 .html(t('contributors.list', { users: userList.html() }));
33570 .style('opacity', 0);
33572 } else if (hidden) {
33575 .style('opacity', 1);
33579 return function(selection) {
33583 context.connection().on('loaded.contributors', debouncedUpdate);
33584 context.map().on('move.contributors', debouncedUpdate);
33587 iD.ui.Disclosure = function() {
33588 var dispatch = d3.dispatch('toggled'),
33591 content = function () {};
33593 var disclosure = function(selection) {
33594 var $link = selection.selectAll('.hide-toggle')
33597 $link.enter().append('a')
33599 .attr('class', 'hide-toggle');
33602 .on('click', toggle)
33603 .classed('expanded', expanded);
33605 var $body = selection.selectAll('div')
33608 $body.enter().append('div');
33610 $body.classed('hide', !expanded)
33613 function toggle() {
33614 expanded = !expanded;
33615 $link.classed('expanded', expanded);
33616 $body.call(iD.ui.Toggle(expanded));
33617 dispatch.toggled(expanded);
33621 disclosure.title = function(_) {
33622 if (!arguments.length) return title;
33627 disclosure.expanded = function(_) {
33628 if (!arguments.length) return expanded;
33633 disclosure.content = function(_) {
33634 if (!arguments.length) return content;
33639 return d3.rebind(disclosure, dispatch, 'on');
33641 iD.ui.EntityEditor = function(context) {
33642 var dispatch = d3.dispatch('choose'),
33644 coalesceChanges = false,
33651 var presetEditor = iD.ui.preset(context)
33652 .on('change', changeTags);
33653 var rawTagEditor = iD.ui.RawTagEditor(context)
33654 .on('change', changeTags);
33656 function entityEditor(selection) {
33657 var entity = context.entity(id),
33658 tags = _.clone(entity.tags);
33660 var $header = selection.selectAll('.header')
33664 var $enter = $header.enter().append('div')
33665 .attr('class', 'header fillL cf');
33667 $enter.append('button')
33668 .attr('class', 'fl preset-reset preset-choose')
33672 $enter.append('button')
33673 .attr('class', 'fr preset-close')
33674 .call(iD.svg.Icon(modified ? '#icon-apply' : '#icon-close'));
33676 $enter.append('h3');
33679 $header.select('h3')
33680 .text(t('inspector.edit'));
33682 $header.select('.preset-close')
33683 .on('click', function() {
33684 context.enter(iD.modes.Browse(context));
33687 var $body = selection.selectAll('.inspector-body')
33691 $enter = $body.enter().append('div')
33692 .attr('class', 'inspector-body');
33694 $enter.append('div')
33695 .attr('class', 'preset-list-item inspector-inner')
33697 .attr('class', 'preset-list-button-wrap')
33699 .attr('class', 'preset-list-button preset-reset')
33700 .call(bootstrap.tooltip()
33701 .title(t('inspector.back_tooltip'))
33702 .placement('bottom'))
33704 .attr('class', 'label');
33706 $body.select('.preset-list-button-wrap')
33707 .call(reference.button);
33709 $body.select('.preset-list-item')
33710 .call(reference.body);
33712 $enter.append('div')
33713 .attr('class', 'inspector-border inspector-preset');
33715 $enter.append('div')
33716 .attr('class', 'inspector-border raw-tag-editor inspector-inner');
33718 $enter.append('div')
33719 .attr('class', 'inspector-border raw-member-editor inspector-inner');
33721 $enter.append('div')
33722 .attr('class', 'raw-membership-editor inspector-inner');
33724 selection.selectAll('.preset-reset')
33725 .on('click', function() {
33726 dispatch.choose(preset);
33730 $body.select('.preset-list-item button')
33731 .call(iD.ui.PresetIcon()
33732 .geometry(context.geometry(id))
33735 $body.select('.preset-list-item .label')
33736 .text(preset.name());
33738 $body.select('.inspector-preset')
33745 $body.select('.raw-tag-editor')
33752 if (entity.type === 'relation') {
33753 $body.select('.raw-member-editor')
33754 .style('display', 'block')
33755 .call(iD.ui.RawMemberEditor(context)
33758 $body.select('.raw-member-editor')
33759 .style('display', 'none');
33762 $body.select('.raw-membership-editor')
33763 .call(iD.ui.RawMembershipEditor(context)
33766 function historyChanged() {
33767 if (state === 'hide') return;
33769 var entity = context.hasEntity(id),
33770 graph = context.graph();
33771 if (!entity) return;
33773 entityEditor.preset(context.presets().match(entity, graph));
33774 entityEditor.modified(base !== graph);
33775 entityEditor(selection);
33779 .on('change.entity-editor', historyChanged);
33782 function clean(o) {
33784 function cleanVal(k, v) {
33785 function keepSpaces(k) {
33786 var whitelist = ['opening_hours', 'service_times', 'collection_times',
33787 'operating_times', 'smoking_hours', 'happy_hours'];
33788 return _.any(whitelist, function(s) { return k.indexOf(s) !== -1; });
33791 var blacklist = ['description', 'note', 'fixme'];
33792 if (_.any(blacklist, function(s) { return k.indexOf(s) !== -1; })) return v;
33794 var cleaned = v.split(';')
33795 .map(function(s) { return s.trim(); })
33796 .join(keepSpaces(k) ? '; ' : ';');
33798 // The code below is not intended to validate websites and emails.
33799 // It is only intended to prevent obvious copy-paste errors. (#2323)
33801 // clean website- and email-like tags
33802 if (k.indexOf('website') !== -1 ||
33803 k.indexOf('email') !== -1 ||
33804 cleaned.indexOf('http') === 0) {
33806 .replace(/[\u200B-\u200F\uFEFF]/g, ''); // strip LRM and other zero width chars
33813 var out = {}, k, v;
33815 if (k && (v = o[k]) !== undefined) {
33816 out[k] = cleanVal(k, v);
33822 // Tag changes that fire on input can all get coalesced into a single
33823 // history operation when the user leaves the field. #2342
33824 function changeTags(changed, onInput) {
33825 var entity = context.entity(id),
33826 annotation = t('operations.change_tags.annotation'),
33827 tags = _.extend({}, entity.tags, changed);
33830 tags = clean(tags);
33832 if (!_.isEqual(entity.tags, tags)) {
33833 if (coalesceChanges) {
33834 context.overwrite(iD.actions.ChangeTags(id, tags), annotation);
33836 context.perform(iD.actions.ChangeTags(id, tags), annotation);
33837 coalesceChanges = !!onInput;
33842 entityEditor.modified = function(_) {
33843 if (!arguments.length) return modified;
33845 d3.selectAll('button.preset-close use')
33846 .attr('xlink:href', (modified ? '#icon-apply' : '#icon-close'));
33849 entityEditor.state = function(_) {
33850 if (!arguments.length) return state;
33852 return entityEditor;
33855 entityEditor.entityID = function(_) {
33856 if (!arguments.length) return id;
33858 base = context.graph();
33859 entityEditor.preset(context.presets().match(context.entity(id), base));
33860 entityEditor.modified(false);
33861 coalesceChanges = false;
33862 return entityEditor;
33865 entityEditor.preset = function(_) {
33866 if (!arguments.length) return preset;
33867 if (_ !== preset) {
33869 reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context)
33872 return entityEditor;
33875 return d3.rebind(entityEditor, dispatch, 'on');
33877 iD.ui.FeatureInfo = function(context) {
33878 function update(selection) {
33879 var features = context.features(),
33880 stats = features.stats(),
33882 hiddenList = _.compact(_.map(features.hidden(), function(k) {
33885 return String(stats[k]) + ' ' + t('feature.' + k + '.description');
33889 selection.html('');
33891 if (hiddenList.length) {
33892 var tooltip = bootstrap.tooltip()
33895 .title(function() {
33896 return iD.ui.tooltipHtml(hiddenList.join('<br/>'));
33899 var warning = selection.append('a')
33901 .attr('tabindex', -1)
33902 .html(t('feature_info.hidden_warning', { count: count }))
33904 .on('click', function() {
33905 tooltip.hide(warning);
33906 // open map data panel?
33907 d3.event.preventDefault();
33912 .classed('hide', !hiddenList.length);
33915 return function(selection) {
33918 context.features().on('change.feature_info', function() {
33923 iD.ui.FeatureList = function(context) {
33924 var geocodeResults;
33926 function featureList(selection) {
33927 var header = selection.append('div')
33928 .attr('class', 'header fillL cf');
33930 header.append('h3')
33931 .text(t('inspector.feature_list'));
33933 function keypress() {
33934 var q = search.property('value'),
33935 items = list.selectAll('.feature-list-item');
33936 if (d3.event.keyCode === 13 && q.length && items.size()) {
33937 click(items.datum());
33941 function inputevent() {
33942 geocodeResults = undefined;
33946 var searchWrap = selection.append('div')
33947 .attr('class', 'search-header');
33949 var search = searchWrap.append('input')
33950 .attr('placeholder', t('inspector.search'))
33951 .attr('type', 'search')
33952 .on('keypress', keypress)
33953 .on('input', inputevent);
33956 .call(iD.svg.Icon('#icon-search', 'pre-text'));
33958 var listWrap = selection.append('div')
33959 .attr('class', 'inspector-body');
33961 var list = listWrap.append('div')
33962 .attr('class', 'feature-list cf');
33965 .on('exit.feature-list', clearSearch);
33967 .on('drawn.feature-list', mapDrawn);
33969 function clearSearch() {
33970 search.property('value', '');
33974 function mapDrawn(e) {
33980 function features() {
33983 graph = context.graph(),
33984 q = search.property('value').toLowerCase();
33986 if (!q) return result;
33988 var idMatch = q.match(/^([nwr])([0-9]+)$/);
33993 geometry: idMatch[1] === 'n' ? 'point' : idMatch[1] === 'w' ? 'line' : 'relation',
33994 type: idMatch[1] === 'n' ? t('inspector.node') : idMatch[1] === 'w' ? t('inspector.way') : t('inspector.relation'),
33999 var locationMatch = sexagesimal.pair(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
34001 if (locationMatch) {
34002 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
34006 type: t('inspector.location'),
34007 name: loc[0].toFixed(6) + ', ' + loc[1].toFixed(6),
34012 function addEntity(entity) {
34013 if (entity.id in entities || result.length > 200)
34016 entities[entity.id] = true;
34018 var name = iD.util.displayName(entity) || '';
34019 if (name.toLowerCase().indexOf(q) >= 0) {
34023 geometry: context.geometry(entity.id),
34024 type: context.presets().match(entity, graph).name(),
34029 graph.parentRelations(entity).forEach(function(parent) {
34034 var visible = context.surface().selectAll('.point, .line, .area')[0];
34035 for (var i = 0; i < visible.length && result.length <= 200; i++) {
34036 addEntity(visible[i].__data__);
34039 (geocodeResults || []).forEach(function(d) {
34040 // https://github.com/openstreetmap/iD/issues/1890
34041 if (d.osm_type && d.osm_id) {
34043 id: iD.Entity.id.fromOSM(d.osm_type, d.osm_id),
34044 geometry: d.osm_type === 'relation' ? 'relation' : d.osm_type === 'way' ? 'line' : 'point',
34045 type: d.type !== 'yes' ? (d.type.charAt(0).toUpperCase() + d.type.slice(1)).replace('_', ' ')
34046 : (d.class.charAt(0).toUpperCase() + d.class.slice(1)).replace('_', ' '),
34047 name: d.display_name,
34048 extent: new iD.geo.Extent(
34049 [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
34050 [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
34058 function drawList() {
34059 var value = search.property('value'),
34060 results = features();
34062 list.classed('filtered', value.length);
34064 var noResultsWorldwide = geocodeResults && geocodeResults.length === 0;
34066 var resultsIndicator = list.selectAll('.no-results-item')
34068 .enter().append('button')
34069 .property('disabled', true)
34070 .attr('class', 'no-results-item')
34071 .call(iD.svg.Icon('#icon-alert', 'pre-text'));
34073 resultsIndicator.append('span')
34074 .attr('class', 'entity-name');
34076 list.selectAll('.no-results-item .entity-name')
34077 .text(noResultsWorldwide ? t('geocoder.no_results_worldwide') : t('geocoder.no_results_visible'));
34079 list.selectAll('.geocode-item')
34081 .enter().append('button')
34082 .attr('class', 'geocode-item')
34083 .on('click', geocode)
34085 .attr('class', 'label')
34087 .attr('class', 'entity-name')
34088 .text(t('geocoder.search'));
34090 list.selectAll('.no-results-item')
34091 .style('display', (value.length && !results.length) ? 'block' : 'none');
34093 list.selectAll('.geocode-item')
34094 .style('display', (value && geocodeResults === undefined) ? 'block' : 'none');
34096 list.selectAll('.feature-list-item')
34100 var items = list.selectAll('.feature-list-item')
34101 .data(results, function(d) { return d.id; });
34103 var enter = items.enter()
34104 .insert('button', '.geocode-item')
34105 .attr('class', 'feature-list-item')
34106 .on('mouseover', mouseover)
34107 .on('mouseout', mouseout)
34108 .on('click', click);
34112 .attr('class', 'label');
34114 label.each(function(d) {
34116 .call(iD.svg.Icon('#icon-' + d.geometry, 'pre-text'));
34119 label.append('span')
34120 .attr('class', 'entity-type')
34121 .text(function(d) { return d.type; });
34123 label.append('span')
34124 .attr('class', 'entity-name')
34125 .text(function(d) { return d.name; });
34127 enter.style('opacity', 0)
34129 .style('opacity', 1);
34137 function mouseover(d) {
34138 if (d.id === -1) return;
34140 context.surface().selectAll(iD.util.entityOrMemberSelector([d.id], context.graph()))
34141 .classed('hover', true);
34144 function mouseout() {
34145 context.surface().selectAll('.hover')
34146 .classed('hover', false);
34149 function click(d) {
34150 d3.event.preventDefault();
34152 context.map().centerZoom([d.location[1], d.location[0]], 20);
34154 else if (d.entity) {
34155 if (d.entity.type === 'node') {
34156 context.map().center(d.entity.loc);
34157 } else if (d.entity.type === 'way') {
34158 var center = context.projection(context.map().center()),
34159 edge = iD.geo.chooseEdge(context.childNodes(d.entity), center, context.projection);
34160 context.map().center(edge.loc);
34162 context.enter(iD.modes.Select(context, [d.entity.id]).suppressMenu(true));
34164 context.zoomToEntity(d.id);
34168 function geocode() {
34169 var searchVal = encodeURIComponent(search.property('value'));
34170 d3.json('https://nominatim.openstreetmap.org/search/' + searchVal + '?limit=10&format=json', function(err, resp) {
34171 geocodeResults = resp || [];
34177 return featureList;
34179 iD.ui.flash = function(selection) {
34180 var modal = iD.ui.modal(selection);
34182 modal.select('.modal').classed('modal-flash', true);
34184 modal.select('.content')
34185 .classed('modal-section', true)
34187 .attr('class', 'description');
34189 modal.on('click.flash', function() { modal.remove(); });
34191 setTimeout(function() {
34198 iD.ui.FullScreen = function(context) {
34199 var element = context.container().node(),
34200 keybinding = d3.keybinding('full-screen');
34203 function getFullScreenFn() {
34204 if (element.requestFullscreen) {
34205 return element.requestFullscreen;
34206 } else if (element.msRequestFullscreen) {
34207 return element.msRequestFullscreen;
34208 } else if (element.mozRequestFullScreen) {
34209 return element.mozRequestFullScreen;
34210 } else if (element.webkitRequestFullscreen) {
34211 return element.webkitRequestFullscreen;
34215 function getExitFullScreenFn() {
34216 if (document.exitFullscreen) {
34217 return document.exitFullscreen;
34218 } else if (document.msExitFullscreen) {
34219 return document.msExitFullscreen;
34220 } else if (document.mozCancelFullScreen) {
34221 return document.mozCancelFullScreen;
34222 } else if (document.webkitExitFullscreen) {
34223 return document.webkitExitFullscreen;
34227 function isFullScreen() {
34228 return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement ||
34229 document.msFullscreenElement;
34232 function isSupported() {
34233 return !!getFullScreenFn();
34236 function fullScreen() {
34237 d3.event.preventDefault();
34238 if (!isFullScreen()) {
34239 // button.classed('active', true);
34240 getFullScreenFn().apply(element);
34242 // button.classed('active', false);
34243 getExitFullScreenFn().apply(document);
34247 return function() { // selection) {
34248 if (!isSupported())
34251 // var tooltip = bootstrap.tooltip()
34252 // .placement('left');
34254 // button = selection.append('button')
34255 // .attr('title', t('full_screen'))
34256 // .attr('tabindex', -1)
34257 // .on('click', fullScreen)
34260 // button.append('span')
34261 // .attr('class', 'icon full-screen');
34264 .on('f11', fullScreen)
34265 .on(iD.ui.cmd('⌘⇧F'), fullScreen);
34267 d3.select(document)
34271 iD.ui.Geolocate = function(context) {
34272 var geoOptions = { enableHighAccuracy: false, timeout: 6000 /* 6sec */ },
34273 locating = iD.ui.Loading(context).message(t('geolocate.locating')).blocking(true),
34277 context.enter(iD.modes.Browse(context));
34278 context.container().call(locating);
34279 navigator.geolocation.getCurrentPosition(success, error, geoOptions);
34281 // This timeout ensures that we still call finish() even if
34282 // the user declines to share their location in Firefox
34283 timeoutId = setTimeout(finish, 10000 /* 10sec */ );
34286 function success(position) {
34287 var map = context.map(),
34288 extent = iD.geo.Extent([position.coords.longitude, position.coords.latitude])
34289 .padByMeters(position.coords.accuracy);
34291 map.centerZoom(extent.center(), Math.min(20, map.extentZoom(extent)));
34299 function finish() {
34300 locating.close(); // unblock ui
34301 if (timeoutId) { clearTimeout(timeoutId); }
34302 timeoutId = undefined;
34305 return function(selection) {
34306 if (!navigator.geolocation) return;
34308 selection.append('button')
34309 .attr('tabindex', -1)
34310 .attr('title', t('geolocate.title'))
34311 .on('click', click)
34312 .call(iD.svg.Icon('#icon-geolocate', 'light'))
34313 .call(bootstrap.tooltip()
34314 .placement('left'));
34317 iD.ui.Help = function(context) {
34322 'help.editing_saving',
34331 var docs = docKeys.map(function(key) {
34334 title: text.split('\n')[0].replace('#', '').trim(),
34335 html: marked(text.split('\n').slice(1).join('\n'))
34339 function help(selection) {
34345 function toggle() {
34346 if (d3.event) d3.event.preventDefault();
34347 tooltip.hide(button);
34348 setVisible(!button.classed('active'));
34351 function setVisible(show) {
34352 if (show !== shown) {
34353 button.classed('active', show);
34357 selection.on('mousedown.help-inside', function() {
34358 return d3.event.stopPropagation();
34360 pane.style('display', 'block')
34361 .style('right', '-500px')
34364 .style('right', '0px');
34366 pane.style('right', '0px')
34369 .style('right', '-500px')
34370 .each('end', function() {
34371 d3.select(this).style('display', 'none');
34373 selection.on('mousedown.help-inside', null);
34378 function clickHelp(d, i) {
34379 pane.property('scrollTop', 0);
34380 doctitle.html(d.title);
34382 body.selectAll('a')
34383 .attr('target', '_blank');
34384 menuItems.classed('selected', function(m) {
34385 return m.title === d.title;
34391 var prevLink = nav.append('a')
34392 .attr('class', 'previous')
34393 .on('click', function() {
34394 clickHelp(docs[i - 1], i - 1);
34396 prevLink.append('span').html('◄ ' + docs[i - 1].title);
34398 if (i < docs.length - 1) {
34399 var nextLink = nav.append('a')
34400 .attr('class', 'next')
34401 .on('click', function() {
34402 clickHelp(docs[i + 1], i + 1);
34404 nextLink.append('span').html(docs[i + 1].title + ' ►');
34408 function clickWalkthrough() {
34409 d3.select(document.body).call(iD.ui.intro(context));
34414 var pane = selection.append('div')
34415 .attr('class', 'help-wrap map-overlay fillL col5 content hide'),
34416 tooltip = bootstrap.tooltip()
34419 .title(iD.ui.tooltipHtml(t('help.title'), key)),
34420 button = selection.append('button')
34421 .attr('tabindex', -1)
34422 .on('click', toggle)
34423 .call(iD.svg.Icon('#icon-help', 'light'))
34428 var toc = pane.append('ul')
34429 .attr('class', 'toc');
34431 var menuItems = toc.selectAll('li')
34436 .html(function(d) { return d.title; })
34437 .on('click', clickHelp);
34440 .attr('class','walkthrough')
34442 .text(t('splash.walkthrough'))
34443 .on('click', clickWalkthrough);
34445 var content = pane.append('div')
34446 .attr('class', 'left-content');
34448 var doctitle = content.append('h2')
34449 .text(t('help.title'));
34451 var body = content.append('div')
34452 .attr('class', 'body');
34454 var nav = content.append('div')
34455 .attr('class', 'nav');
34457 clickHelp(docs[0], 0);
34459 var keybinding = d3.keybinding('help')
34464 d3.select(document)
34467 context.surface().on('mousedown.help-outside', hide);
34468 context.container().on('mousedown.help-outside', hide);
34473 iD.ui.Info = function(context) {
34474 var key = iD.ui.cmd('⌘I'),
34475 imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
34478 function info(selection) {
34479 function radiansToMeters(r) {
34480 // using WGS84 authalic radius (6371007.1809 m)
34481 return r * 6371007.1809;
34484 function steradiansToSqmeters(r) {
34485 // http://gis.stackexchange.com/a/124857/40446
34486 return r / 12.56637 * 510065621724000;
34489 function toLineString(feature) {
34490 if (feature.type === 'LineString') return feature;
34492 var result = { type: 'LineString', coordinates: [] };
34493 if (feature.type === 'Polygon') {
34494 result.coordinates = feature.coordinates[0];
34495 } else if (feature.type === 'MultiPolygon') {
34496 result.coordinates = feature.coordinates[0][0];
34502 function displayLength(m) {
34503 var d = m * (imperial ? 3.28084 : 1),
34522 // drop unnecessary precision
34523 p = d > 1000 ? 0 : d > 100 ? 1 : 2;
34525 return String(d.toFixed(p)) + ' ' + unit;
34528 function displayArea(m2) {
34529 var d = m2 * (imperial ? 10.7639111056 : 1),
34530 d1, d2, p1, p2, unit1, unit2;
34533 if (d >= 6969600) { // > 0.25mi² show mi²
34541 if (d > 4356 && d < 43560000) { // 0.1 - 1000 acres
34547 if (d >= 250000) { // > 0.25km² show km²
34555 if (d > 1000 && d < 10000000) { // 0.1 - 1000 hectares
34561 // drop unnecessary precision
34562 p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2;
34563 p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2;
34565 return String(d1.toFixed(p1)) + ' ' + unit1 +
34566 (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : '');
34570 function redraw() {
34571 if (hidden) return;
34573 var resolver = context.graph(),
34574 selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }),
34575 singular = selected.length === 1 ? selected[0] : null,
34576 extent = iD.geo.Extent(),
34581 .attr('class', 'infobox-heading fillD')
34582 .text(singular || t('infobox.selected', { n: selected.length }));
34584 if (!selected.length) return;
34587 for (var i = 0; i < selected.length; i++) {
34588 entity = context.entity(selected[i]);
34589 extent._extend(entity.extent(resolver));
34591 center = extent.center();
34594 var list = wrap.append('ul');
34596 // multiple wrap, just display extent center..
34599 .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
34603 // single wrap, display details..
34604 if (!entity) return;
34605 var geometry = entity.geometry(resolver);
34607 if (geometry === 'line' || geometry === 'area') {
34608 var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()),
34609 feature = entity.asGeoJSON(resolver),
34610 length = radiansToMeters(d3.geo.length(toLineString(feature))),
34611 lengthLabel = t('infobox.' + (closed ? 'perimeter' : 'length')),
34612 centroid = d3.geo.centroid(feature);
34615 .text(t('infobox.geometry') + ': ' +
34616 (closed ? t('infobox.closed') + ' ' : '') + t('geometry.' + geometry) );
34619 var area = steradiansToSqmeters(entity.area(resolver));
34621 .text(t('infobox.area') + ': ' + displayArea(area));
34625 .text(lengthLabel + ': ' + displayLength(length));
34628 .text(t('infobox.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5));
34631 var toggle = imperial ? 'imperial' : 'metric';
34633 .text(t('infobox.' + toggle))
34635 .attr('class', 'button')
34636 .on('click', function() {
34637 d3.event.preventDefault();
34638 imperial = !imperial;
34643 var centerLabel = t('infobox.' + (entity.type === 'node' ? 'location' : 'center'));
34646 .text(t('infobox.geometry') + ': ' + t('geometry.' + geometry));
34649 .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
34654 function toggle() {
34655 if (d3.event) d3.event.preventDefault();
34661 .style('display', 'block')
34662 .style('opacity', 1)
34665 .style('opacity', 0)
34666 .each('end', function() {
34667 d3.select(this).style('display', 'none');
34671 .style('display', 'block')
34672 .style('opacity', 0)
34675 .style('opacity', 1);
34682 var wrap = selection.selectAll('.infobox')
34687 .attr('class', 'infobox fillD2')
34688 .style('display', (hidden ? 'none' : 'block'));
34691 .on('drawn.info', redraw);
34695 var keybinding = d3.keybinding('info')
34698 d3.select(document)
34704 iD.ui.Inspector = function(context) {
34705 var presetList = iD.ui.PresetList(context),
34706 entityEditor = iD.ui.EntityEditor(context),
34709 newFeature = false;
34711 function inspector(selection) {
34713 .entityID(entityID)
34714 .autofocus(newFeature)
34715 .on('choose', setPreset);
34719 .entityID(entityID)
34720 .on('choose', showList);
34722 var $wrap = selection.selectAll('.panewrap')
34725 var $enter = $wrap.enter().append('div')
34726 .attr('class', 'panewrap');
34728 $enter.append('div')
34729 .attr('class', 'preset-list-pane pane');
34731 $enter.append('div')
34732 .attr('class', 'entity-editor-pane pane');
34734 var $presetPane = $wrap.select('.preset-list-pane');
34735 var $editorPane = $wrap.select('.entity-editor-pane');
34737 var graph = context.graph(),
34738 entity = context.entity(entityID),
34739 showEditor = state === 'hover' ||
34740 entity.isUsed(graph) ||
34741 entity.isHighwayIntersection(graph);
34744 $wrap.style('right', '0%');
34745 $editorPane.call(entityEditor);
34747 $wrap.style('right', '-100%');
34748 $presetPane.call(presetList);
34751 var $footer = selection.selectAll('.footer')
34754 $footer.enter().append('div')
34755 .attr('class', 'footer');
34757 selection.select('.footer')
34758 .call(iD.ui.ViewOnOSM(context)
34759 .entityID(entityID));
34761 function showList(preset) {
34763 .styleTween('right', function() { return d3.interpolate('0%', '-100%'); });
34765 $presetPane.call(presetList
34770 function setPreset(preset) {
34772 .styleTween('right', function() { return d3.interpolate('-100%', '0%'); });
34774 $editorPane.call(entityEditor
34779 inspector.state = function(_) {
34780 if (!arguments.length) return state;
34782 entityEditor.state(state);
34786 inspector.entityID = function(_) {
34787 if (!arguments.length) return entityID;
34792 inspector.newFeature = function(_) {
34793 if (!arguments.length) return newFeature;
34800 iD.ui.intro = function(context) {
34803 function intro(selection) {
34805 function localizedName(id) {
34807 n2140018997: 'city_hall',
34808 n367813436: 'fire_department',
34809 w203988286: 'memory_isle_park',
34810 w203972937: 'riverwalk_trail',
34811 w203972938: 'riverwalk_trail',
34812 w203972940: 'riverwalk_trail',
34813 w41785752: 'w_michigan_ave',
34814 w134150789: 'w_michigan_ave',
34815 w134150795: 'w_michigan_ave',
34816 w134150800: 'w_michigan_ave',
34817 w134150811: 'w_michigan_ave',
34818 w134150802: 'e_michigan_ave',
34819 w134150836: 'e_michigan_ave',
34820 w41074896: 'e_michigan_ave',
34821 w17965834: 'spring_st',
34822 w203986457: 'scidmore_park',
34823 w203049587: 'petting_zoo',
34824 w17967397: 'n_andrews_st',
34825 w17967315: 's_andrews_st',
34826 w17967326: 'n_constantine_st',
34827 w17966400: 's_constantine_st',
34828 w170848823: 'rocky_river',
34829 w170848824: 'rocky_river',
34830 w170848331: 'rocky_river',
34831 w17967752: 'railroad_dr',
34832 w17965998: 'conrail_rr',
34833 w134150845: 'conrail_rr',
34834 w170989131: 'st_joseph_river',
34835 w143497377: 'n_main_st',
34836 w134150801: 's_main_st',
34837 w134150830: 's_main_st',
34838 w17966462: 's_main_st',
34839 w17967734: 'water_st',
34840 w17964996: 'foster_st',
34841 w170848330: 'portage_river',
34842 w17965351: 'flower_st',
34843 w17965502: 'elm_st',
34844 w17965402: 'walnut_st',
34845 w17964793: 'morris_ave',
34846 w17967444: 'east_st',
34847 w17966984: 'portage_ave'
34849 return features[id] && t('intro.graph.' + features[id]);
34852 context.enter(iD.modes.Browse(context));
34854 // Save current map state
34855 var history = context.history().toJSON(),
34856 hash = window.location.hash,
34857 center = context.map().center(),
34858 zoom = context.map().zoom(),
34859 background = context.background().baseLayerSource(),
34860 opacity = d3.selectAll('#map .layer-background').style('opacity'),
34861 loadedTiles = context.connection().loadedTiles(),
34862 baseEntities = context.history().graph().base().entities,
34866 context.inIntro(true);
34868 // Load semi-real data used in intro
34869 context.connection().toggle(false).flush();
34870 context.history().reset();
34872 introGraph = JSON.parse(iD.introGraph);
34873 for (var key in introGraph) {
34874 introGraph[key] = iD.Entity(introGraph[key]);
34875 name = localizedName(key);
34877 introGraph[key].tags.name = name;
34880 context.history().merge(d3.values(iD.Graph().load(introGraph).entities));
34881 context.background().bing();
34883 d3.selectAll('#map .layer-background').style('opacity', 1);
34885 var curtain = d3.curtain();
34886 selection.call(curtain);
34888 function reveal(box, text, options) {
34889 options = options || {};
34890 if (text) curtain.reveal(box, text, options.tooltipClass, options.duration);
34891 else curtain.reveal(box, '', '', options.duration);
34894 var steps = ['navigation', 'point', 'area', 'line', 'startEditing'].map(function(step, i) {
34895 var s = iD.ui.intro[step](context, reveal)
34896 .on('done', function() {
34897 entered.filter(function(d) {
34898 return d.title === s.title;
34899 }).classed('finished', true);
34900 enter(steps[i + 1]);
34905 steps[steps.length - 1].on('startEditing', function() {
34908 d3.selectAll('#map .layer-background').style('opacity', opacity);
34909 context.connection().toggle(true).flush().loadedTiles(loadedTiles);
34910 context.history().reset().merge(d3.values(baseEntities));
34911 context.background().baseLayerSource(background);
34912 if (history) context.history().fromJSON(history, false);
34913 context.map().centerZoom(center, zoom);
34914 window.location.replace(hash);
34915 context.inIntro(false);
34918 var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
34920 var buttonwrap = navwrap.append('div')
34921 .attr('class', 'joined')
34922 .selectAll('button.step');
34924 var entered = buttonwrap
34928 .attr('class', 'step')
34929 .on('click', enter);
34932 .call(iD.svg.Icon('#icon-apply', 'pre-text'));
34936 .text(function(d) { return t(d.title); });
34940 function enter (newStep) {
34941 if (step) { step.exit(); }
34943 context.enter(iD.modes.Browse(context));
34948 entered.classed('active', function(d) {
34949 return d.title === step.title;
34957 iD.ui.intro.pointBox = function(point, context) {
34958 var rect = context.surfaceRect();
34959 point = context.projection(point);
34961 left: point[0] + rect.left - 30,
34962 top: point[1] + rect.top - 50,
34968 iD.ui.intro.pad = function(box, padding, context) {
34969 if (box instanceof Array) {
34970 var rect = context.surfaceRect();
34971 box = context.projection(box);
34973 left: box[0] + rect.left,
34974 top: box[1] + rect.top
34978 left: box.left - padding,
34979 top: box.top - padding,
34980 width: (box.width || 0) + 2 * padding,
34981 height: (box.width || 0) + 2 * padding
34985 iD.ui.intro.icon = function(name, svgklass) {
34986 return '<svg class="icon ' + (svgklass || '') + '">' +
34987 '<use xlink:href="' + name + '"></use></svg>';
34989 iD.ui.Lasso = function(context) {
34990 var group, polygon;
34992 lasso.coordinates = [];
34994 function lasso(selection) {
34996 context.container().classed('lasso', true);
34998 group = selection.append('g')
34999 .attr('class', 'lasso hide');
35001 polygon = group.append('path')
35002 .attr('class', 'lasso-path');
35004 group.call(iD.ui.Toggle(true));
35010 polygon.data([lasso.coordinates])
35011 .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; });
35015 lasso.extent = function () {
35016 return lasso.coordinates.reduce(function(extent, point) {
35017 return extent.extend(iD.geo.Extent(point));
35018 }, iD.geo.Extent());
35021 lasso.p = function(_) {
35022 if (!arguments.length) return lasso;
35023 lasso.coordinates.push(_);
35028 lasso.close = function() {
35030 group.call(iD.ui.Toggle(false, function() {
35031 d3.select(this).remove();
35034 context.container().classed('lasso', false);
35039 iD.ui.Loading = function(context) {
35044 var loading = function(selection) {
35045 modal = iD.ui.modal(selection, blocking);
35047 var loadertext = modal.select('.content')
35048 .classed('loading-modal', true)
35050 .attr('class', 'modal-section fillL');
35052 loadertext.append('img')
35053 .attr('class', 'loader')
35054 .attr('src', context.imagePath('loader-white.gif'));
35056 loadertext.append('h3')
35059 modal.select('button.close')
35060 .attr('class', 'hide');
35065 loading.message = function(_) {
35066 if (!arguments.length) return message;
35071 loading.blocking = function(_) {
35072 if (!arguments.length) return blocking;
35077 loading.close = function() {
35083 iD.ui.MapData = function(context) {
35085 features = context.features().keys(),
35086 layers = context.layers(),
35087 fills = ['wireframe', 'partial', 'full'],
35088 fillDefault = context.storage('area-fill') || 'partial',
35089 fillSelected = fillDefault;
35092 function map_data(selection) {
35094 function showsFeature(d) {
35095 return context.features().enabled(d);
35098 function autoHiddenFeature(d) {
35099 return context.features().autoHidden(d);
35102 function clickFeature(d) {
35103 context.features().toggle(d);
35107 function showsFill(d) {
35108 return fillSelected === d;
35111 function setFill(d) {
35112 _.each(fills, function(opt) {
35113 context.surface().classed('fill-' + opt, Boolean(opt === d));
35117 if (d !== 'wireframe') {
35119 context.storage('area-fill', d);
35124 function toggleLayer(which) {
35125 var layer = layers.layer(which);
35127 layer.enabled(!layer.enabled());
35132 function clickGpx() {
35133 toggleLayer('gpx');
35136 function clickMapillaryImages() {
35137 toggleLayer('mapillary-images');
35140 function clickMapillarySigns() {
35141 toggleLayer('mapillary-signs');
35145 function drawMapillaryItems(selection) {
35146 var mapillaryImages = layers.layer('mapillary-images'),
35147 mapillarySigns = layers.layer('mapillary-signs'),
35148 supportsMapillaryImages = mapillaryImages && mapillaryImages.supported(),
35149 supportsMapillarySigns = mapillarySigns && mapillarySigns.supported(),
35150 showsMapillaryImages = supportsMapillaryImages && mapillaryImages.enabled(),
35151 showsMapillarySigns = supportsMapillarySigns && mapillarySigns.enabled();
35153 var mapillaryList = selection
35154 .selectAll('.layer-list-mapillary')
35161 .attr('class', 'layer-list layer-list-mapillary');
35163 var mapillaryImageLayerItem = mapillaryList
35164 .selectAll('.list-item-mapillary-images')
35165 .data(supportsMapillaryImages ? [0] : []);
35167 var enterImages = mapillaryImageLayerItem.enter()
35169 .attr('class', 'list-item-mapillary-images');
35171 var labelImages = enterImages.append('label')
35172 .call(bootstrap.tooltip()
35173 .title(t('mapillary_images.tooltip'))
35174 .placement('top'));
35176 labelImages.append('input')
35177 .attr('type', 'checkbox')
35178 .on('change', clickMapillaryImages);
35180 labelImages.append('span')
35181 .text(t('mapillary_images.title'));
35184 var mapillarySignLayerItem = mapillaryList
35185 .selectAll('.list-item-mapillary-signs')
35186 .data(supportsMapillarySigns ? [0] : []);
35188 var enterSigns = mapillarySignLayerItem.enter()
35190 .attr('class', 'list-item-mapillary-signs');
35192 var labelSigns = enterSigns.append('label')
35193 .call(bootstrap.tooltip()
35194 .title(t('mapillary_signs.tooltip'))
35195 .placement('top'));
35197 labelSigns.append('input')
35198 .attr('type', 'checkbox')
35199 .on('change', clickMapillarySigns);
35201 labelSigns.append('span')
35202 .text(t('mapillary_signs.title'));
35205 mapillaryImageLayerItem
35206 .classed('active', showsMapillaryImages)
35207 .selectAll('input')
35208 .property('checked', showsMapillaryImages);
35210 mapillarySignLayerItem
35211 .classed('active', showsMapillarySigns)
35212 .selectAll('input')
35213 .property('checked', showsMapillarySigns);
35216 mapillaryImageLayerItem.exit()
35218 mapillarySignLayerItem.exit()
35223 function drawGpxItem(selection) {
35224 var gpx = layers.layer('gpx'),
35225 hasGpx = gpx && gpx.hasGpx(),
35226 showsGpx = hasGpx && gpx.enabled();
35228 var gpxLayerItem = selection
35229 .selectAll('.layer-list-gpx')
35230 .data(gpx ? [0] : []);
35233 var enter = gpxLayerItem.enter()
35235 .attr('class', 'layer-list layer-list-gpx')
35237 .classed('list-item-gpx', true);
35239 enter.append('button')
35240 .attr('class', 'list-item-gpx-extent')
35241 .call(bootstrap.tooltip()
35242 .title(t('gpx.zoom'))
35243 .placement('left'))
35244 .on('click', function() {
35245 d3.event.preventDefault();
35246 d3.event.stopPropagation();
35249 .call(iD.svg.Icon('#icon-search'));
35251 enter.append('button')
35252 .attr('class', 'list-item-gpx-browse')
35253 .call(bootstrap.tooltip()
35254 .title(t('gpx.browse'))
35255 .placement('left'))
35256 .on('click', function() {
35257 d3.select(document.createElement('input'))
35258 .attr('type', 'file')
35259 .on('change', function() {
35260 gpx.files(d3.event.target.files);
35264 .call(iD.svg.Icon('#icon-geolocate'));
35266 var labelGpx = enter.append('label')
35267 .call(bootstrap.tooltip()
35268 .title(t('gpx.drag_drop'))
35269 .placement('top'));
35271 labelGpx.append('input')
35272 .attr('type', 'checkbox')
35273 .on('change', clickGpx);
35275 labelGpx.append('span')
35276 .text(t('gpx.local_layer'));
35280 .classed('active', showsGpx)
35281 .selectAll('input')
35282 .property('disabled', !hasGpx)
35283 .property('checked', showsGpx);
35286 gpxLayerItem.exit()
35291 function drawList(selection, data, type, name, change, active) {
35292 var items = selection.selectAll('li')
35296 var enter = items.enter()
35298 .attr('class', 'layer')
35299 .call(bootstrap.tooltip()
35301 .title(function(d) {
35302 var tip = t(name + '.' + d + '.tooltip'),
35303 key = (d === 'wireframe' ? 'W' : null);
35305 if (name === 'feature' && autoHiddenFeature(d)) {
35306 tip += '<div>' + t('map_data.autohidden') + '</div>';
35308 return iD.ui.tooltipHtml(tip, key);
35313 var label = enter.append('label');
35315 label.append('input')
35316 .attr('type', type)
35317 .attr('name', name)
35318 .on('change', change);
35320 label.append('span')
35321 .text(function(d) { return t(name + '.' + d + '.description'); });
35325 .classed('active', active)
35326 .selectAll('input')
35327 .property('checked', active)
35328 .property('indeterminate', function(d) {
35329 return (name === 'feature' && autoHiddenFeature(d));
35338 function update() {
35339 dataLayerContainer.call(drawMapillaryItems);
35340 dataLayerContainer.call(drawGpxItem);
35342 fillList.call(drawList, fills, 'radio', 'area_fill', setFill, showsFill);
35344 featureList.call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature);
35347 function hidePanel() {
35351 function togglePanel() {
35352 if (d3.event) d3.event.preventDefault();
35353 tooltip.hide(button);
35354 setVisible(!button.classed('active'));
35357 function toggleWireframe() {
35359 d3.event.preventDefault();
35360 d3.event.stopPropagation();
35362 setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe'));
35363 context.map().pan([0,0]); // trigger a redraw
35366 function setVisible(show) {
35367 if (show !== shown) {
35368 button.classed('active', show);
35373 selection.on('mousedown.map_data-inside', function() {
35374 return d3.event.stopPropagation();
35376 content.style('display', 'block')
35377 .style('right', '-300px')
35380 .style('right', '0px');
35382 content.style('display', 'block')
35383 .style('right', '0px')
35386 .style('right', '-300px')
35387 .each('end', function() {
35388 d3.select(this).style('display', 'none');
35390 selection.on('mousedown.map_data-inside', null);
35396 var content = selection.append('div')
35397 .attr('class', 'fillL map-overlay col3 content hide'),
35398 tooltip = bootstrap.tooltip()
35401 .title(iD.ui.tooltipHtml(t('map_data.description'), key)),
35402 button = selection.append('button')
35403 .attr('tabindex', -1)
35404 .on('click', togglePanel)
35405 .call(iD.svg.Icon('#icon-data', 'light'))
35409 content.append('h4')
35410 .text(t('map_data.title'));
35414 content.append('a')
35415 .text(t('map_data.data_layers'))
35417 .classed('hide-toggle', true)
35418 .classed('expanded', true)
35419 .on('click', function() {
35420 var exp = d3.select(this).classed('expanded');
35421 dataLayerContainer.style('display', exp ? 'none' : 'block');
35422 d3.select(this).classed('expanded', !exp);
35423 d3.event.preventDefault();
35426 var dataLayerContainer = content.append('div')
35427 .attr('class', 'data-data-layers')
35428 .style('display', 'block');
35432 content.append('a')
35433 .text(t('map_data.fill_area'))
35435 .classed('hide-toggle', true)
35436 .classed('expanded', false)
35437 .on('click', function() {
35438 var exp = d3.select(this).classed('expanded');
35439 fillContainer.style('display', exp ? 'none' : 'block');
35440 d3.select(this).classed('expanded', !exp);
35441 d3.event.preventDefault();
35444 var fillContainer = content.append('div')
35445 .attr('class', 'data-area-fills')
35446 .style('display', 'none');
35448 var fillList = fillContainer.append('ul')
35449 .attr('class', 'layer-list layer-fill-list');
35453 content.append('a')
35454 .text(t('map_data.map_features'))
35456 .classed('hide-toggle', true)
35457 .classed('expanded', false)
35458 .on('click', function() {
35459 var exp = d3.select(this).classed('expanded');
35460 featureContainer.style('display', exp ? 'none' : 'block');
35461 d3.select(this).classed('expanded', !exp);
35462 d3.event.preventDefault();
35465 var featureContainer = content.append('div')
35466 .attr('class', 'data-feature-filters')
35467 .style('display', 'none');
35469 var featureList = featureContainer.append('ul')
35470 .attr('class', 'layer-list layer-feature-list');
35474 .on('change.map_data-update', update);
35476 setFill(fillDefault);
35478 var keybinding = d3.keybinding('features')
35479 .on(key, togglePanel)
35480 .on('W', toggleWireframe)
35481 .on('B', hidePanel)
35482 .on('H', hidePanel);
35484 d3.select(document)
35487 context.surface().on('mousedown.map_data-outside', hidePanel);
35488 context.container().on('mousedown.map_data-outside', hidePanel);
35493 iD.ui.MapInMap = function(context) {
35496 function map_in_map(selection) {
35497 var backgroundLayer = iD.TileLayer(),
35498 overlayLayers = {},
35499 projection = iD.geo.RawMercator(),
35500 gpxLayer = iD.svg.Gpx(projection, context).showLabels(false),
35501 zoom = d3.behavior.zoom()
35502 .scaleExtent([ztok(0.5), ztok(24)])
35503 .on('zoom', zoomPan),
35504 transformed = false,
35507 zDiff = 6, // by default, minimap renders at (main zoom - 6)
35508 tStart, tLast, tCurr, kLast, kCurr, tiles, svg, timeoutId;
35510 function ztok(z) { return 256 * Math.pow(2, z); }
35511 function ktoz(k) { return Math.log(k) / Math.LN2 - 8; }
35514 function startMouse() {
35515 context.surface().on('mouseup.map-in-map-outside', endMouse);
35516 context.container().on('mouseup.map-in-map-outside', endMouse);
35518 tStart = tLast = tCurr = projection.translate();
35523 function zoomPan() {
35524 var e = d3.event.sourceEvent,
35525 t = d3.event.translate,
35526 k = d3.event.scale,
35527 zMain = ktoz(context.projection.scale() * 2 * Math.PI),
35530 // restrict minimap zoom to < (main zoom - 3)
35531 if (zMini > zMain - 3) {
35533 zoom.scale(kCurr).translate(tCurr); // restore last good values
35539 zDiff = zMain - zMini;
35541 var scale = kCurr / kLast,
35542 tX = (tCurr[0] / scale - tLast[0]) * scale,
35543 tY = (tCurr[1] / scale - tLast[1]) * scale;
35545 iD.util.setTransform(tiles, tX, tY, scale);
35546 iD.util.setTransform(svg, 0, 0, scale);
35547 transformed = true;
35551 e.preventDefault();
35552 e.stopPropagation();
35556 function endMouse() {
35557 context.surface().on('mouseup.map-in-map-outside', null);
35558 context.container().on('mouseup.map-in-map-outside', null);
35560 updateProjection();
35563 if (tCurr[0] !== tStart[0] && tCurr[1] !== tStart[1]) {
35564 var dMini = wrap.dimensions(),
35565 cMini = [ dMini[0] / 2, dMini[1] / 2 ];
35567 context.map().center(projection.invert(cMini));
35572 function updateProjection() {
35573 var loc = context.map().center(),
35574 dMini = wrap.dimensions(),
35575 cMini = [ dMini[0] / 2, dMini[1] / 2 ],
35576 tMain = context.projection.translate(),
35577 kMain = context.projection.scale(),
35578 zMain = ktoz(kMain * 2 * Math.PI),
35579 zMini = Math.max(zMain - zDiff, 0.5),
35580 kMini = ztok(zMini);
35584 .scale(kMini / (2 * Math.PI));
35586 var s = projection(loc),
35587 mouse = panning ? [ tCurr[0] - tStart[0], tCurr[1] - tStart[1] ] : [0, 0],
35589 cMini[0] - s[0] + tMain[0] + mouse[0],
35590 cMini[1] - s[1] + tMain[1] + mouse[1]
35595 .clipExtent([[0, 0], dMini]);
35602 tLast = tCurr = tMini;
35603 kLast = kCurr = kMini;
35606 iD.util.setTransform(tiles, 0, 0);
35607 iD.util.setTransform(svg, 0, 0);
35608 transformed = false;
35613 function redraw() {
35614 if (hidden) return;
35616 updateProjection();
35618 var dMini = wrap.dimensions(),
35619 zMini = ktoz(projection.scale() * 2 * Math.PI);
35621 // setup tile container
35623 .selectAll('.map-in-map-tiles')
35629 .attr('class', 'map-in-map-tiles');
35631 // redraw background
35633 .source(context.background().baseLayerSource())
35634 .projection(projection)
35635 .dimensions(dMini);
35637 var background = tiles
35638 .selectAll('.map-in-map-background')
35643 .attr('class', 'map-in-map-background');
35646 .call(backgroundLayer);
35650 var overlaySources = context.background().overlayLayerSources();
35651 var activeOverlayLayers = [];
35652 for (var i = 0; i < overlaySources.length; i++) {
35653 if (overlaySources[i].validZoom(zMini)) {
35654 if (!overlayLayers[i]) overlayLayers[i] = iD.TileLayer();
35655 activeOverlayLayers.push(overlayLayers[i]
35656 .source(overlaySources[i])
35657 .projection(projection)
35658 .dimensions(dMini));
35662 var overlay = tiles
35663 .selectAll('.map-in-map-overlay')
35668 .attr('class', 'map-in-map-overlay');
35670 var overlays = overlay
35672 .data(activeOverlayLayers, function(d) { return d.source().name(); });
35674 overlays.enter().append('div');
35675 overlays.each(function(layer) {
35676 d3.select(this).call(layer);
35684 .selectAll('.map-in-map-gpx')
35685 .data(gpxLayer.enabled() ? [0] : []);
35689 .attr('class', 'map-in-map-gpx');
35694 gpx.call(gpxLayer);
35697 // redraw bounding box
35699 var getPath = d3.geo.path().projection(projection),
35700 bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
35702 svg = wrap.selectAll('.map-in-map-svg')
35707 .attr('class', 'map-in-map-svg');
35709 var path = svg.selectAll('.map-in-map-bbox')
35714 .attr('class', 'map-in-map-bbox');
35717 .attr('d', getPath)
35718 .classed('thick', function(d) { return getPath.area(d) < 30; });
35723 function queueRedraw() {
35724 clearTimeout(timeoutId);
35725 timeoutId = setTimeout(function() { redraw(); }, 300);
35729 function toggle() {
35730 if (d3.event) d3.event.preventDefault();
35734 var label = d3.select('.minimap-toggle');
35735 label.classed('active', !hidden)
35736 .select('input').property('checked', !hidden);
35740 .style('display', 'block')
35741 .style('opacity', 1)
35744 .style('opacity', 0)
35745 .each('end', function() {
35746 d3.select(this).style('display', 'none');
35750 .style('display', 'block')
35751 .style('opacity', 0)
35754 .style('opacity', 1);
35760 iD.ui.MapInMap.toggle = toggle;
35762 var wrap = selection.selectAll('.map-in-map')
35767 .attr('class', 'map-in-map')
35768 .style('display', (hidden ? 'none' : 'block'))
35769 .on('mousedown.map-in-map', startMouse)
35770 .on('mouseup.map-in-map', endMouse)
35772 .on('dblclick.zoom', null);
35775 .on('drawn.map-in-map', function(drawn) {
35776 if (drawn.full === true) redraw();
35781 var keybinding = d3.keybinding('map-in-map')
35784 d3.select(document)
35790 iD.ui.modal = function(selection, blocking) {
35791 var keybinding = d3.keybinding('modal');
35792 var previous = selection.select('div.modal');
35793 var animate = previous.empty();
35795 previous.transition()
35797 .style('opacity', 0)
35800 var shaded = selection
35802 .attr('class', 'shaded')
35803 .style('opacity', 0);
35805 shaded.close = function() {
35809 .style('opacity',0)
35814 .style('top','0px');
35820 var modal = shaded.append('div')
35821 .attr('class', 'modal fillL col6');
35824 shaded.on('click.remove-modal', function() {
35825 if (d3.event.target === this) {
35830 modal.append('button')
35831 .attr('class', 'close')
35832 .on('click', shaded.close)
35833 .call(iD.svg.Icon('#icon-close'));
35836 .on('⌫', shaded.close)
35837 .on('⎋', shaded.close);
35839 d3.select(document).call(keybinding);
35842 modal.append('div')
35843 .attr('class', 'content');
35846 shaded.transition().style('opacity', 1);
35848 shaded.style('opacity', 1);
35853 iD.ui.Modes = function(context) {
35855 iD.modes.AddPoint(context),
35856 iD.modes.AddLine(context),
35857 iD.modes.AddArea(context)];
35859 function editable() {
35860 return context.editable() && context.mode().id !== 'save';
35863 return function(selection) {
35864 var buttons = selection.selectAll('button.add-button')
35867 buttons.enter().append('button')
35868 .attr('tabindex', -1)
35869 .attr('class', function(mode) { return mode.id + ' add-button col4'; })
35870 .on('click.mode-buttons', function(mode) {
35871 if (mode.id === context.mode().id) {
35872 context.enter(iD.modes.Browse(context));
35874 context.enter(mode);
35877 .call(bootstrap.tooltip()
35878 .placement('bottom')
35880 .title(function(mode) {
35881 return iD.ui.tooltipHtml(mode.description, mode.key);
35885 .on('move.modes', _.debounce(update, 500));
35888 .on('enter.modes', update);
35890 buttons.each(function(d) {
35892 .call(iD.svg.Icon('#icon-' + d.button, 'pre-text'));
35895 buttons.append('span')
35896 .attr('class', 'label')
35897 .text(function(mode) { return mode.title; });
35899 context.on('enter.editor', function(entered) {
35900 buttons.classed('active', function(mode) { return entered.button === mode.button; });
35901 context.container()
35902 .classed('mode-' + entered.id, true);
35905 context.on('exit.editor', function(exited) {
35906 context.container()
35907 .classed('mode-' + exited.id, false);
35910 var keybinding = d3.keybinding('mode-buttons');
35912 modes.forEach(function(m) {
35913 keybinding.on(m.key, function() { if (editable()) context.enter(m); });
35916 d3.select(document)
35919 function update() {
35920 buttons.property('disabled', !editable());
35924 iD.ui.Notice = function(context) {
35925 return function(selection) {
35926 var div = selection.append('div')
35927 .attr('class', 'notice');
35929 var button = div.append('button')
35930 .attr('class', 'zoom-to notice')
35931 .on('click', function() { context.map().zoom(context.minEditableZoom()); });
35934 .call(iD.svg.Icon('#icon-plus', 'pre-text'))
35936 .attr('class', 'label')
35937 .text(t('zoom_in_edit'));
35939 function disableTooHigh() {
35940 div.style('display', context.editable() ? 'none' : 'block');
35944 .on('move.notice', _.debounce(disableTooHigh, 500));
35949 iD.ui.PresetIcon = function() {
35950 var preset, geometry;
35952 function presetIcon(selection) {
35953 selection.each(render);
35956 function render() {
35957 var selection = d3.select(this),
35958 p = preset.apply(this, arguments),
35959 geom = geometry.apply(this, arguments),
35960 icon = p.icon || (geom === 'line' ? 'other-line' : 'marker-stroked'),
35961 maki = iD.data.featureIcons.hasOwnProperty(icon + '-24');
35963 if (icon === 'dentist') maki = true; // workaround for dentist icon missing in `maki-sprite.json`
35965 function tag_classes(p) {
35967 for (var i in p.tags) {
35969 if (p.tags[i] !== '*') {
35970 s += ' tag-' + i + '-' + p.tags[i];
35976 var $fill = selection.selectAll('.preset-icon-fill')
35979 $fill.enter().append('div');
35981 $fill.attr('class', function() {
35982 return 'preset-icon-fill preset-icon-fill-' + geom + tag_classes(p);
35985 var $frame = selection.selectAll('.preset-icon-frame')
35990 .call(iD.svg.Icon('#preset-icon-frame'));
35992 $frame.attr('class', function() {
35993 return 'preset-icon-frame ' + (geom === 'area' ? '' : 'hide');
35997 var $icon = selection.selectAll('.preset-icon')
36002 .attr('class', 'preset-icon')
36003 .call(iD.svg.Icon(''));
36006 .attr('class', 'preset-icon preset-icon-' + (maki ? '32' : (geom === 'area' ? '44' : '60')));
36008 $icon.selectAll('svg')
36009 .attr('class', function() {
36010 return 'icon ' + icon + tag_classes(p);
36013 $icon.selectAll('use') // workaround: maki parking-24 broken?
36014 .attr('href', '#' + icon + (maki ? ( icon === 'parking' ? '-18' : '-24') : ''));
36017 presetIcon.preset = function(_) {
36018 if (!arguments.length) return preset;
36019 preset = d3.functor(_);
36023 presetIcon.geometry = function(_) {
36024 if (!arguments.length) return geometry;
36025 geometry = d3.functor(_);
36031 iD.ui.preset = function(context) {
36032 var event = d3.dispatch('change'),
36039 function UIField(field, entity, show) {
36040 field = _.clone(field);
36042 field.input = iD.ui.preset[field.type](field, context)
36043 .on('change', event.change);
36045 if (field.input.entity) field.input.entity(entity);
36047 field.keys = field.keys || [field.key];
36051 field.shown = function() {
36052 return field.id === 'name' || field.show || _.any(field.keys, function(key) { return !!tags[key]; });
36055 field.modified = function() {
36056 var original = context.graph().base().entities[entity.id];
36057 return _.any(field.keys, function(key) {
36058 return original ? tags[key] !== original.tags[key] : tags[key];
36062 field.revert = function() {
36063 var original = context.graph().base().entities[entity.id],
36065 field.keys.forEach(function(key) {
36066 t[key] = original ? original.tags[key] : undefined;
36071 field.present = function() {
36072 return _.any(field.keys, function(key) {
36077 field.remove = function() {
36079 field.keys.forEach(function(key) {
36080 t[key] = undefined;
36088 function fieldKey(field) {
36092 function presets(selection) {
36093 selection.call(iD.ui.Disclosure()
36094 .title(t('inspector.all_fields'))
36095 .expanded(context.storage('preset_fields.expanded') !== 'false')
36096 .on('toggled', toggled)
36097 .content(content));
36099 function toggled(expanded) {
36100 context.storage('preset_fields.expanded', expanded);
36104 function content(selection) {
36106 var entity = context.entity(id),
36107 geometry = context.geometry(id);
36109 fields = [UIField(context.presets().field('name'), entity)];
36111 preset.fields.forEach(function(field) {
36112 if (field.matchGeometry(geometry)) {
36113 fields.push(UIField(field, entity, true));
36117 if (entity.isHighwayIntersection(context.graph())) {
36118 fields.push(UIField(context.presets().field('restrictions'), entity, true));
36121 context.presets().universal().forEach(function(field) {
36122 if (preset.fields.indexOf(field) < 0) {
36123 fields.push(UIField(field, entity));
36128 var shown = fields.filter(function(field) { return field.shown(); }),
36129 notShown = fields.filter(function(field) { return !field.shown(); });
36131 var $form = selection.selectAll('.preset-form')
36134 $form.enter().append('div')
36135 .attr('class', 'preset-form inspector-inner fillL3');
36137 var $fields = $form.selectAll('.form-field')
36138 .data(shown, fieldKey);
36142 var $enter = $fields.enter()
36144 .attr('class', function(field) {
36145 return 'form-field form-field-' + field.id;
36148 var $label = $enter.append('label')
36149 .attr('class', 'form-label')
36150 .attr('for', function(field) { return 'preset-input-' + field.id; })
36151 .text(function(field) { return field.label(); });
36153 var wrap = $label.append('div')
36154 .attr('class', 'form-label-button-wrap');
36156 wrap.append('button')
36157 .attr('class', 'remove-icon')
36158 .attr('tabindex', -1)
36159 .call(iD.svg.Icon('#operation-delete'));
36161 wrap.append('button')
36162 .attr('class', 'modified-icon')
36163 .attr('tabindex', -1)
36164 .call(iD.svg.Icon('#icon-undo'));
36168 $fields.select('.form-label-button-wrap .remove-icon')
36169 .on('click', remove);
36171 $fields.select('.modified-icon')
36172 .on('click', revert);
36176 .classed('modified', function(field) {
36177 return field.modified();
36179 .classed('present', function(field) {
36180 return field.present();
36182 .each(function(field) {
36183 var reference = iD.ui.TagReference(field.reference || {key: field.key}, context);
36185 if (state === 'hover') {
36186 reference.showing(false);
36191 .selectAll('input')
36192 .on('keydown', function() {
36193 // if user presses enter, and combobox is not active, accept edits..
36194 if (d3.event.keyCode === 13 && d3.select('.combobox').empty()) {
36195 context.enter(iD.modes.Browse(context));
36198 .call(reference.body)
36199 .select('.form-label-button-wrap')
36200 .call(reference.button);
36202 field.input.tags(tags);
36208 notShown = notShown.map(function(field) {
36210 title: field.label(),
36211 value: field.label(),
36216 var $more = selection.selectAll('.more-fields')
36217 .data((notShown.length > 0) ? [0] : []);
36219 $more.enter().append('div')
36220 .attr('class', 'more-fields')
36222 .text(t('inspector.add_fields'));
36224 var $input = $more.selectAll('.value')
36227 $input.enter().append('input')
36228 .attr('class', 'value')
36229 .attr('type', 'text');
36232 .attr('placeholder', function() {
36233 var placeholder = [];
36234 for (var field in notShown) {
36235 placeholder.push(notShown[field].title);
36237 return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : '');
36239 .call(d3.combobox().data(notShown)
36241 .on('accept', show));
36249 function show(field) {
36250 field = field.field;
36252 content(selection);
36253 field.input.focus();
36256 function revert(field) {
36257 d3.event.stopPropagation();
36258 d3.event.preventDefault();
36259 event.change(field.revert());
36262 function remove(field) {
36263 d3.event.stopPropagation();
36264 d3.event.preventDefault();
36265 event.change(field.remove());
36269 presets.preset = function(_) {
36270 if (!arguments.length) return preset;
36271 if (preset && preset.id === _.id) return presets;
36277 presets.state = function(_) {
36278 if (!arguments.length) return state;
36283 presets.tags = function(_) {
36284 if (!arguments.length) return tags;
36286 // Don't reset fields here.
36290 presets.entityID = function(_) {
36291 if (!arguments.length) return id;
36292 if (id === _) return presets;
36298 return d3.rebind(presets, event, 'on');
36300 iD.ui.PresetList = function(context) {
36301 var event = d3.dispatch('choose'),
36306 function presetList(selection) {
36307 var geometry = context.geometry(id),
36308 presets = context.presets().matchGeometry(geometry);
36310 selection.html('');
36312 var messagewrap = selection.append('div')
36313 .attr('class', 'header fillL cf');
36315 var message = messagewrap.append('h3')
36316 .text(t('inspector.choose'));
36318 if (context.entity(id).isUsed(context.graph())) {
36319 messagewrap.append('button')
36320 .attr('class', 'preset-choose')
36321 .on('click', function() { event.choose(currentPreset); })
36325 messagewrap.append('button')
36326 .attr('class', 'close')
36327 .on('click', function() {
36328 context.enter(iD.modes.Browse(context));
36330 .call(iD.svg.Icon('#icon-close'));
36333 function keydown() {
36334 // hack to let delete shortcut work when search is autofocused
36335 if (search.property('value').length === 0 &&
36336 (d3.event.keyCode === d3.keybinding.keyCodes['⌫'] ||
36337 d3.event.keyCode === d3.keybinding.keyCodes['⌦'])) {
36338 d3.event.preventDefault();
36339 d3.event.stopPropagation();
36340 iD.operations.Delete([id], context)();
36341 } else if (search.property('value').length === 0 &&
36342 (d3.event.ctrlKey || d3.event.metaKey) &&
36343 d3.event.keyCode === d3.keybinding.keyCodes.z) {
36344 d3.event.preventDefault();
36345 d3.event.stopPropagation();
36347 } else if (!d3.event.ctrlKey && !d3.event.metaKey) {
36348 d3.select(this).on('keydown', null);
36352 function keypress() {
36354 var value = search.property('value');
36355 if (d3.event.keyCode === 13 && value.length) {
36356 list.selectAll('.preset-list-item:first-child').datum().choose();
36360 function inputevent() {
36361 var value = search.property('value');
36362 list.classed('filtered', value.length);
36363 if (value.length) {
36364 var results = presets.search(value, geometry);
36365 message.text(t('inspector.results', {
36366 n: results.collection.length,
36369 list.call(drawList, results);
36371 list.call(drawList, context.presets().defaults(geometry, 36));
36372 message.text(t('inspector.choose'));
36376 var searchWrap = selection.append('div')
36377 .attr('class', 'search-header');
36379 var search = searchWrap.append('input')
36380 .attr('class', 'preset-search-input')
36381 .attr('placeholder', t('inspector.search'))
36382 .attr('type', 'search')
36383 .on('keydown', keydown)
36384 .on('keypress', keypress)
36385 .on('input', inputevent);
36388 .call(iD.svg.Icon('#icon-search', 'pre-text'));
36391 search.node().focus();
36394 var listWrap = selection.append('div')
36395 .attr('class', 'inspector-body');
36397 var list = listWrap.append('div')
36398 .attr('class', 'preset-list fillL cf')
36399 .call(drawList, context.presets().defaults(geometry, 36));
36402 function drawList(list, presets) {
36403 var collection = presets.collection.map(function(preset) {
36404 return preset.members ? CategoryItem(preset) : PresetItem(preset);
36407 var items = list.selectAll('.preset-list-item')
36408 .data(collection, function(d) { return d.preset.id; });
36410 items.enter().append('div')
36411 .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
36412 .classed('current', function(item) { return item.preset === currentPreset; })
36413 .each(function(item) {
36414 d3.select(this).call(item);
36416 .style('opacity', 0)
36418 .style('opacity', 1);
36426 function CategoryItem(preset) {
36427 var box, sublist, shown = false;
36429 function item(selection) {
36430 var wrap = selection.append('div')
36431 .attr('class', 'preset-list-button-wrap category col12');
36433 wrap.append('button')
36434 .attr('class', 'preset-list-button')
36435 .classed('expanded', false)
36436 .call(iD.ui.PresetIcon()
36437 .geometry(context.geometry(id))
36439 .on('click', function() {
36440 var isExpanded = d3.select(this).classed('expanded');
36441 var triangle = isExpanded ? '▶ ' : '▼ ';
36442 d3.select(this).classed('expanded', !isExpanded);
36443 d3.select(this).selectAll('.label').text(triangle + preset.name());
36447 .attr('class', 'label')
36449 return '▶ ' + preset.name();
36452 box = selection.append('div')
36453 .attr('class', 'subgrid col12')
36454 .style('max-height', '0px')
36455 .style('opacity', 0);
36458 .attr('class', 'arrow');
36460 sublist = box.append('div')
36461 .attr('class', 'preset-list fillL3 cf fl');
36464 item.choose = function() {
36465 if (!box || !sublist) return;
36471 .style('opacity', '0')
36472 .style('max-height', '0px')
36473 .style('padding-bottom', '0px');
36476 sublist.call(drawList, preset.members);
36479 .style('opacity', '1')
36480 .style('max-height', 200 + preset.members.collection.length * 80 + 'px')
36481 .style('padding-bottom', '20px');
36485 item.preset = preset;
36490 function PresetItem(preset) {
36491 function item(selection) {
36492 var wrap = selection.append('div')
36493 .attr('class', 'preset-list-button-wrap col12');
36495 wrap.append('button')
36496 .attr('class', 'preset-list-button')
36497 .call(iD.ui.PresetIcon()
36498 .geometry(context.geometry(id))
36500 .on('click', item.choose)
36502 .attr('class', 'label')
36503 .text(preset.name());
36505 wrap.call(item.reference.button);
36506 selection.call(item.reference.body);
36509 item.choose = function() {
36510 context.presets().choose(preset);
36513 iD.actions.ChangePreset(id, currentPreset, preset),
36514 t('operations.change_tags.annotation'));
36516 event.choose(preset);
36519 item.help = function() {
36520 d3.event.stopPropagation();
36521 item.reference.toggle();
36524 item.preset = preset;
36525 item.reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context);
36530 presetList.autofocus = function(_) {
36531 if (!arguments.length) return autofocus;
36536 presetList.entityID = function(_) {
36537 if (!arguments.length) return id;
36539 presetList.preset(context.presets().match(context.entity(id), context.graph()));
36543 presetList.preset = function(_) {
36544 if (!arguments.length) return currentPreset;
36549 return d3.rebind(presetList, event, 'on');
36551 iD.ui.RadialMenu = function(context, operations) {
36556 var radialMenu = function(selection) {
36557 if (!operations.length)
36560 selection.node().parentNode.focus();
36562 function click(operation) {
36563 d3.event.stopPropagation();
36564 if (operation.disabled())
36567 radialMenu.close();
36570 menu = selection.append('g')
36571 .attr('class', 'radial-menu')
36572 .attr('transform', 'translate(' + center + ')')
36573 .attr('opacity', 0);
36576 .attr('opacity', 1);
36581 a1 = a0 + (operations.length - 1) * a;
36583 menu.append('path')
36584 .attr('class', 'radial-menu-background')
36585 .attr('d', 'M' + r * Math.sin(a0) + ',' +
36587 ' A' + r + ',' + r + ' 0 ' + (operations.length > 5 ? '1' : '0') + ',0 ' +
36588 (r * Math.sin(a1) + 1e-3) + ',' +
36589 (r * Math.cos(a1) + 1e-3)) // Force positive-length path (#1305)
36590 .attr('stroke-width', 50)
36591 .attr('stroke-linecap', 'round');
36593 var button = menu.selectAll()
36597 .attr('class', function(d) { return 'radial-menu-item radial-menu-item-' + d.id; })
36598 .classed('disabled', function(d) { return d.disabled(); })
36599 .attr('transform', function(d, i) {
36600 return 'translate(' + iD.geo.roundCoords([
36601 r * Math.sin(a0 + i * a),
36602 r * Math.cos(a0 + i * a)]).join(',') + ')';
36605 button.append('circle')
36607 .on('click', click)
36608 .on('mousedown', mousedown)
36609 .on('mouseover', mouseover)
36610 .on('mouseout', mouseout);
36612 button.append('use')
36613 .attr('transform', 'translate(-10,-10)')
36614 .attr('width', '20')
36615 .attr('height', '20')
36616 .attr('xlink:href', function(d) { return '#operation-' + d.id; });
36618 tooltip = d3.select(document.body)
36620 .attr('class', 'tooltip-inner radial-menu-tooltip');
36622 function mousedown() {
36623 d3.event.stopPropagation(); // https://github.com/openstreetmap/iD/issues/1869
36626 function mouseover(d, i) {
36627 var rect = context.surfaceRect(),
36628 angle = a0 + i * a,
36629 top = rect.top + (r + 25) * Math.cos(angle) + center[1] + 'px',
36630 left = rect.left + (r + 25) * Math.sin(angle) + center[0] + 'px',
36631 bottom = rect.height - (r + 25) * Math.cos(angle) - center[1] + 'px',
36632 right = rect.width - (r + 25) * Math.sin(angle) - center[0] + 'px';
36635 .style('top', null)
36636 .style('left', null)
36637 .style('bottom', null)
36638 .style('right', null)
36639 .style('display', 'block')
36640 .html(iD.ui.tooltipHtml(d.tooltip(), d.keys[0]));
36644 .style('right', right)
36645 .style('top', top);
36646 } else if (i >= 4) {
36648 .style('left', left)
36649 .style('bottom', bottom);
36652 .style('left', left)
36653 .style('top', top);
36657 function mouseout() {
36658 tooltip.style('display', 'none');
36662 radialMenu.close = function() {
36665 .style('pointer-events', 'none')
36667 .attr('opacity', 0)
36676 radialMenu.center = function(_) {
36677 if (!arguments.length) return center;
36684 iD.ui.RawMemberEditor = function(context) {
36687 function selectMember(d) {
36688 d3.event.preventDefault();
36689 context.enter(iD.modes.Select(context, [d.id]));
36692 function changeRole(d) {
36693 var role = d3.select(this).property('value');
36694 var member = {id: d.id, type: d.type, role: role};
36696 iD.actions.ChangeMember(d.relation.id, member, d.index),
36697 t('operations.change_role.annotation'));
36700 function deleteMember(d) {
36702 iD.actions.DeleteMember(d.relation.id, d.index),
36703 t('operations.delete_member.annotation'));
36705 if (!context.hasEntity(d.relation.id)) {
36706 context.enter(iD.modes.Browse(context));
36710 function rawMemberEditor(selection) {
36711 var entity = context.entity(id),
36714 entity.members.forEach(function(member, index) {
36721 member: context.hasEntity(member.id)
36725 selection.call(iD.ui.Disclosure()
36726 .title(t('inspector.all_members') + ' (' + memberships.length + ')')
36728 .on('toggled', toggled)
36729 .content(content));
36731 function toggled(expanded) {
36733 selection.node().parentNode.scrollTop += 200;
36737 function content($wrap) {
36738 var $list = $wrap.selectAll('.member-list')
36741 $list.enter().append('ul')
36742 .attr('class', 'member-list');
36744 var $items = $list.selectAll('li')
36745 .data(memberships, function(d) {
36746 return iD.Entity.key(d.relation) + ',' + d.index + ',' +
36747 (d.member ? iD.Entity.key(d.member) : 'incomplete');
36750 var $enter = $items.enter().append('li')
36751 .attr('class', 'member-row form-field')
36752 .classed('member-incomplete', function(d) { return !d.member; });
36754 $enter.each(function(d) {
36756 var $label = d3.select(this).append('label')
36757 .attr('class', 'form-label')
36760 .on('click', selectMember);
36762 $label.append('span')
36763 .attr('class', 'member-entity-type')
36764 .text(function(d) { return context.presets().match(d.member, context.graph()).name(); });
36766 $label.append('span')
36767 .attr('class', 'member-entity-name')
36768 .text(function(d) { return iD.util.displayName(d.member); });
36771 d3.select(this).append('label')
36772 .attr('class', 'form-label')
36773 .text(t('inspector.incomplete'));
36777 $enter.append('input')
36778 .attr('class', 'member-role')
36779 .property('type', 'text')
36780 .attr('maxlength', 255)
36781 .attr('placeholder', t('inspector.role'))
36782 .property('value', function(d) { return d.role; })
36783 .on('change', changeRole);
36785 $enter.append('button')
36786 .attr('tabindex', -1)
36787 .attr('class', 'remove button-input-action member-delete minor')
36788 .on('click', deleteMember)
36789 .call(iD.svg.Icon('#operation-delete'));
36796 rawMemberEditor.entityID = function(_) {
36797 if (!arguments.length) return id;
36799 return rawMemberEditor;
36802 return rawMemberEditor;
36804 iD.ui.RawMembershipEditor = function(context) {
36807 function selectRelation(d) {
36808 d3.event.preventDefault();
36809 context.enter(iD.modes.Select(context, [d.relation.id]));
36812 function changeRole(d) {
36813 var role = d3.select(this).property('value');
36815 iD.actions.ChangeMember(d.relation.id, _.extend({}, d.member, {role: role}), d.index),
36816 t('operations.change_role.annotation'));
36819 function addMembership(d, role) {
36824 iD.actions.AddMember(d.relation.id, {id: id, type: context.entity(id).type, role: role}),
36825 t('operations.add_member.annotation'));
36828 var relation = iD.Relation();
36831 iD.actions.AddEntity(relation),
36832 iD.actions.AddMember(relation.id, {id: id, type: context.entity(id).type, role: role}),
36833 t('operations.add.annotation.relation'));
36835 context.enter(iD.modes.Select(context, [relation.id]));
36839 function deleteMembership(d) {
36841 iD.actions.DeleteMember(d.relation.id, d.index),
36842 t('operations.delete_member.annotation'));
36845 function relations(q) {
36846 var newRelation = {
36848 value: t('inspector.new_relation')
36851 graph = context.graph();
36853 context.intersects(context.extent()).forEach(function(entity) {
36854 if (entity.type !== 'relation' || entity.id === id)
36857 var presetName = context.presets().match(entity, graph).name(),
36858 entityName = iD.util.displayName(entity) || '';
36860 var value = presetName + ' ' + entityName;
36861 if (q && value.toLowerCase().indexOf(q.toLowerCase()) === -1)
36870 result.sort(function(a, b) {
36871 return iD.Relation.creationOrder(a.relation, b.relation);
36874 // Dedupe identical names by appending relation id - see #2891
36875 var dupeGroups = _(result)
36877 .filter(function(v) { return v.length > 1; })
36880 dupeGroups.forEach(function(group) {
36881 group.forEach(function(obj) {
36882 obj.value += ' ' + obj.relation.id;
36886 result.unshift(newRelation);
36891 function rawMembershipEditor(selection) {
36892 var entity = context.entity(id),
36895 context.graph().parentRelations(entity).forEach(function(relation) {
36896 relation.members.forEach(function(member, index) {
36897 if (member.id === entity.id) {
36898 memberships.push({relation: relation, member: member, index: index});
36903 selection.call(iD.ui.Disclosure()
36904 .title(t('inspector.all_relations') + ' (' + memberships.length + ')')
36906 .on('toggled', toggled)
36907 .content(content));
36909 function toggled(expanded) {
36911 selection.node().parentNode.scrollTop += 200;
36915 function content($wrap) {
36916 var $list = $wrap.selectAll('.member-list')
36919 $list.enter().append('ul')
36920 .attr('class', 'member-list');
36922 var $items = $list.selectAll('li.member-row-normal')
36923 .data(memberships, function(d) { return iD.Entity.key(d.relation) + ',' + d.index; });
36925 var $enter = $items.enter().append('li')
36926 .attr('class', 'member-row member-row-normal form-field');
36928 var $label = $enter.append('label')
36929 .attr('class', 'form-label')
36932 .on('click', selectRelation);
36934 $label.append('span')
36935 .attr('class', 'member-entity-type')
36936 .text(function(d) { return context.presets().match(d.relation, context.graph()).name(); });
36938 $label.append('span')
36939 .attr('class', 'member-entity-name')
36940 .text(function(d) { return iD.util.displayName(d.relation); });
36942 $enter.append('input')
36943 .attr('class', 'member-role')
36944 .property('type', 'text')
36945 .attr('maxlength', 255)
36946 .attr('placeholder', t('inspector.role'))
36947 .property('value', function(d) { return d.member.role; })
36948 .on('change', changeRole);
36950 $enter.append('button')
36951 .attr('tabindex', -1)
36952 .attr('class', 'remove button-input-action member-delete minor')
36953 .on('click', deleteMembership)
36954 .call(iD.svg.Icon('#operation-delete'));
36960 var $new = $list.selectAll('.member-row-new')
36963 $enter = $new.enter().append('li')
36964 .attr('class', 'member-row member-row-new form-field');
36966 $enter.append('input')
36967 .attr('type', 'text')
36968 .attr('class', 'member-entity-input')
36969 .call(d3.combobox()
36971 .fetcher(function(value, callback) {
36972 callback(relations(value));
36974 .on('accept', function(d) {
36975 addMembership(d, $new.select('.member-role').property('value'));
36978 $enter.append('input')
36979 .attr('class', 'member-role')
36980 .property('type', 'text')
36981 .attr('maxlength', 255)
36982 .attr('placeholder', t('inspector.role'))
36983 .on('change', changeRole);
36985 $enter.append('button')
36986 .attr('tabindex', -1)
36987 .attr('class', 'remove button-input-action member-delete minor')
36988 .on('click', deleteMembership)
36989 .call(iD.svg.Icon('#operation-delete'));
36992 $list.selectAll('.member-row-new')
36996 var $add = $wrap.selectAll('.add-relation')
37001 .attr('class', 'add-relation')
37002 .call(iD.svg.Icon('#icon-plus', 'light'));
37004 $wrap.selectAll('.add-relation')
37005 .on('click', function() {
37008 $list.selectAll('.member-entity-input').node().focus();
37013 rawMembershipEditor.entityID = function(_) {
37014 if (!arguments.length) return id;
37016 return rawMembershipEditor;
37019 return rawMembershipEditor;
37021 iD.ui.RawTagEditor = function(context) {
37022 var event = d3.dispatch('change'),
37029 function rawTagEditor(selection) {
37030 var count = Object.keys(tags).filter(function(d) { return d; }).length;
37032 selection.call(iD.ui.Disclosure()
37033 .title(t('inspector.all_tags') + ' (' + count + ')')
37034 .expanded(context.storage('raw_tag_editor.expanded') === 'true' || preset.isFallback())
37035 .on('toggled', toggled)
37036 .content(content));
37038 function toggled(expanded) {
37039 context.storage('raw_tag_editor.expanded', expanded);
37041 selection.node().parentNode.scrollTop += 200;
37046 function content($wrap) {
37047 var entries = d3.entries(tags);
37049 if (!entries.length || showBlank) {
37051 entries.push({key: '', value: ''});
37054 var $list = $wrap.selectAll('.tag-list')
37057 $list.enter().append('ul')
37058 .attr('class', 'tag-list');
37060 var $newTag = $wrap.selectAll('.add-tag')
37065 .attr('class', 'add-tag')
37066 .call(iD.svg.Icon('#icon-plus', 'light'));
37068 $newTag.on('click', addTag);
37070 var $items = $list.selectAll('li')
37071 .data(entries, function(d) { return d.key; });
37075 var $enter = $items.enter().append('li')
37076 .attr('class', 'tag-row cf');
37078 $enter.append('div')
37079 .attr('class', 'key-wrap')
37081 .property('type', 'text')
37082 .attr('class', 'key')
37083 .attr('maxlength', 255);
37085 $enter.append('div')
37086 .attr('class', 'input-wrap-position')
37088 .property('type', 'text')
37089 .attr('class', 'value')
37090 .attr('maxlength', 255);
37092 $enter.append('button')
37093 .attr('tabindex', -1)
37094 .attr('class', 'remove minor')
37095 .call(iD.svg.Icon('#operation-delete'));
37097 if (context.taginfo()) {
37098 $enter.each(bindTypeahead);
37105 $items.each(function(tag) {
37106 var isRelation = (context.entity(id).type === 'relation'),
37108 if (isRelation && tag.key === 'type')
37109 reference = iD.ui.TagReference({rtype: tag.value}, context);
37111 reference = iD.ui.TagReference({key: tag.key, value: tag.value}, context);
37113 if (state === 'hover') {
37114 reference.showing(false);
37118 .call(reference.button)
37119 .call(reference.body);
37122 $items.select('input.key')
37123 .attr('title', function(d) { return d.key; })
37124 .value(function(d) { return d.key; })
37125 .on('blur', keyChange)
37126 .on('change', keyChange);
37128 $items.select('input.value')
37129 .attr('title', function(d) { return d.value; })
37130 .value(function(d) { return d.value; })
37131 .on('blur', valueChange)
37132 .on('change', valueChange)
37133 .on('keydown.push-more', pushMore);
37135 $items.select('button.remove')
37136 .on('click', removeTag);
37142 function pushMore() {
37143 if (d3.event.keyCode === 9 && !d3.event.shiftKey &&
37144 $list.selectAll('li:last-child input.value').node() === this) {
37149 function bindTypeahead() {
37150 var row = d3.select(this),
37151 key = row.selectAll('input.key'),
37152 value = row.selectAll('input.value');
37154 function sort(value, data) {
37155 var sameletter = [],
37157 for (var i = 0; i < data.length; i++) {
37158 if (data[i].value.substring(0, value.length) === value) {
37159 sameletter.push(data[i]);
37161 other.push(data[i]);
37164 return sameletter.concat(other);
37167 key.call(d3.combobox()
37168 .fetcher(function(value, callback) {
37169 context.taginfo().keys({
37171 geometry: context.geometry(id),
37173 }, function(err, data) {
37174 if (!err) callback(sort(value, data));
37178 value.call(d3.combobox()
37179 .fetcher(function(value, callback) {
37180 context.taginfo().values({
37183 geometry: context.geometry(id),
37185 }, function(err, data) {
37186 if (!err) callback(sort(value, data));
37191 function unbind() {
37192 var row = d3.select(this);
37194 row.selectAll('input.key')
37195 .call(d3.combobox.off);
37197 row.selectAll('input.value')
37198 .call(d3.combobox.off);
37201 function keyChange(d) {
37203 kNew = this.value.trim(),
37206 if (kNew && kNew !== kOld) {
37207 var match = kNew.match(/^(.*?)(?:_(\d+))?$/),
37209 suffix = +(match[2] || 1);
37210 while (tags[kNew]) { // rename key if already in use
37211 kNew = base + '_' + suffix++;
37214 tag[kOld] = undefined;
37215 tag[kNew] = d.value;
37216 d.key = kNew; // Maintain DOM identity through the subsequent update.
37221 function valueChange(d) {
37223 tag[d.key] = this.value;
37227 function removeTag(d) {
37229 tag[d.key] = undefined;
37231 d3.select(this.parentNode).remove();
37234 function addTag() {
37235 // Wrapped in a setTimeout in case it's being called from a blur
37236 // handler. Without the setTimeout, the call to `content` would
37237 // wipe out the pending value change.
37238 setTimeout(function() {
37241 $list.selectAll('li:last-child input.key').node().focus();
37246 rawTagEditor.state = function(_) {
37247 if (!arguments.length) return state;
37249 return rawTagEditor;
37252 rawTagEditor.preset = function(_) {
37253 if (!arguments.length) return preset;
37255 return rawTagEditor;
37258 rawTagEditor.tags = function(_) {
37259 if (!arguments.length) return tags;
37261 return rawTagEditor;
37264 rawTagEditor.entityID = function(_) {
37265 if (!arguments.length) return id;
37267 return rawTagEditor;
37270 return d3.rebind(rawTagEditor, event, 'on');
37272 iD.ui.Restore = function(context) {
37273 return function(selection) {
37274 if (!context.history().lock() || !context.history().restorableChanges())
37277 var modal = iD.ui.modal(selection, true);
37279 modal.select('.modal')
37280 .attr('class', 'modal fillL col6');
37282 var introModal = modal.select('.content');
37284 introModal.attr('class','cf');
37286 introModal.append('div')
37287 .attr('class', 'modal-section')
37289 .text(t('restore.heading'));
37291 introModal.append('div')
37292 .attr('class','modal-section')
37294 .text(t('restore.description'));
37296 var buttonWrap = introModal.append('div')
37297 .attr('class', 'modal-actions cf');
37299 var restore = buttonWrap.append('button')
37300 .attr('class', 'restore col6')
37301 .text(t('restore.restore'))
37302 .on('click', function() {
37303 context.history().restore();
37307 buttonWrap.append('button')
37308 .attr('class', 'reset col6')
37309 .text(t('restore.reset'))
37310 .on('click', function() {
37311 context.history().clearSaved();
37315 restore.node().focus();
37318 iD.ui.Save = function(context) {
37319 var history = context.history(),
37320 key = iD.ui.cmd('⌘S');
37322 function saving() {
37323 return context.mode().id === 'save';
37327 d3.event.preventDefault();
37328 if (!context.inIntro() && !saving() && history.hasChanges()) {
37329 context.enter(iD.modes.Save(context));
37333 return function(selection) {
37334 var tooltip = bootstrap.tooltip()
37335 .placement('bottom')
37337 .title(iD.ui.tooltipHtml(t('save.no_changes'), key));
37339 var button = selection.append('button')
37340 .attr('class', 'save col12 disabled')
37341 .attr('tabindex', -1)
37345 button.append('span')
37346 .attr('class', 'label')
37347 .text(t('save.title'));
37349 button.append('span')
37350 .attr('class', 'count')
37353 var keybinding = d3.keybinding('undo-redo')
37354 .on(key, save, true);
37356 d3.select(document)
37359 var numChanges = 0;
37361 context.history().on('change.save', function() {
37362 var _ = history.difference().summary().length;
37363 if (_ === numChanges)
37367 tooltip.title(iD.ui.tooltipHtml(t(numChanges > 0 ?
37368 'save.help' : 'save.no_changes'), key));
37371 .classed('disabled', numChanges === 0)
37372 .classed('has-count', numChanges > 0);
37374 button.select('span.count')
37378 context.on('enter.save', function() {
37379 button.property('disabled', saving());
37380 if (saving()) button.call(tooltip.hide);
37384 iD.ui.Scale = function(context) {
37385 var projection = context.projection,
37389 function scaleDefs(loc1, loc2) {
37390 var lat = (loc2[1] + loc1[1]) / 2,
37391 imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
37392 conversion = (imperial ? 3.28084 : 1),
37393 dist = iD.geo.lonToMeters(loc2[0] - loc1[0], lat) * conversion,
37394 scale = { dist: 0, px: 0, text: '' },
37395 buckets, i, val, dLon;
37398 buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
37400 buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
37403 // determine a user-friendly endpoint for the scale
37404 for (i = 0; i < buckets.length; i++) {
37407 scale.dist = Math.floor(dist / val) * val;
37412 dLon = iD.geo.metersToLon(scale.dist / conversion, lat);
37413 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
37416 if (scale.dist >= 5280) {
37417 scale.dist /= 5280;
37418 scale.text = String(scale.dist) + ' mi';
37420 scale.text = String(scale.dist) + ' ft';
37423 if (scale.dist >= 1000) {
37424 scale.dist /= 1000;
37425 scale.text = String(scale.dist) + ' km';
37427 scale.text = String(scale.dist) + ' m';
37434 function update(selection) {
37435 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
37436 var dims = context.map().dimensions(),
37437 loc1 = projection.invert([0, dims[1]]),
37438 loc2 = projection.invert([maxLength, dims[1]]),
37439 scale = scaleDefs(loc1, loc2);
37441 selection.select('#scalepath')
37442 .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
37444 selection.select('#scaletext')
37445 .attr('x', scale.px + 8)
37446 .attr('y', tickHeight)
37450 return function(selection) {
37451 var g = selection.append('svg')
37452 .attr('id', 'scale')
37454 .attr('transform', 'translate(10,11)');
37456 g.append('path').attr('id', 'scalepath');
37457 g.append('text').attr('id', 'scaletext');
37461 context.map().on('move.scale', function() {
37466 iD.ui.SelectionList = function(context, selectedIDs) {
37468 function selectEntity(entity) {
37469 context.enter(iD.modes.Select(context, [entity.id]).suppressMenu(true));
37473 function selectionList(selection) {
37474 selection.classed('selection-list-pane', true);
37476 var header = selection.append('div')
37477 .attr('class', 'header fillL cf');
37479 header.append('h3')
37480 .text(t('inspector.multiselect'));
37482 var listWrap = selection.append('div')
37483 .attr('class', 'inspector-body');
37485 var list = listWrap.append('div')
37486 .attr('class', 'feature-list cf');
37488 context.history().on('change.selection-list', drawList);
37491 function drawList() {
37492 var entities = selectedIDs
37493 .map(function(id) { return context.hasEntity(id); })
37494 .filter(function(entity) { return entity; });
37496 var items = list.selectAll('.feature-list-item')
37497 .data(entities, iD.Entity.key);
37499 var enter = items.enter().append('button')
37500 .attr('class', 'feature-list-item')
37501 .on('click', selectEntity);
37504 var label = enter.append('div')
37505 .attr('class', 'label')
37506 .call(iD.svg.Icon('', 'pre-text'));
37508 label.append('span')
37509 .attr('class', 'entity-type');
37511 label.append('span')
37512 .attr('class', 'entity-name');
37515 items.selectAll('use')
37516 .attr('href', function() {
37517 var entity = this.parentNode.parentNode.__data__;
37518 return '#icon-' + context.geometry(entity.id);
37521 items.selectAll('.entity-type')
37522 .text(function(entity) { return context.presets().match(entity, context.graph()).name(); });
37524 items.selectAll('.entity-name')
37525 .text(function(entity) { return iD.util.displayName(entity); });
37533 return selectionList;
37536 iD.ui.Sidebar = function(context) {
37537 var inspector = iD.ui.Inspector(context),
37540 function sidebar(selection) {
37541 var featureListWrap = selection.append('div')
37542 .attr('class', 'feature-list-pane')
37543 .call(iD.ui.FeatureList(context));
37545 selection.call(iD.ui.Notice(context));
37547 var inspectorWrap = selection.append('div')
37548 .attr('class', 'inspector-hidden inspector-wrap fr');
37550 function hover(id) {
37551 if (!current && context.hasEntity(id)) {
37552 featureListWrap.classed('inspector-hidden', true);
37553 inspectorWrap.classed('inspector-hidden', false)
37554 .classed('inspector-hover', true);
37556 if (inspector.entityID() !== id || inspector.state() !== 'hover') {
37561 inspectorWrap.call(inspector);
37563 } else if (!current) {
37564 featureListWrap.classed('inspector-hidden', false);
37565 inspectorWrap.classed('inspector-hidden', true);
37566 inspector.state('hide');
37570 sidebar.hover = _.throttle(hover, 200);
37572 sidebar.select = function(id, newFeature) {
37573 if (!current && id) {
37574 featureListWrap.classed('inspector-hidden', true);
37575 inspectorWrap.classed('inspector-hidden', false)
37576 .classed('inspector-hover', false);
37578 if (inspector.entityID() !== id || inspector.state() !== 'select') {
37582 .newFeature(newFeature);
37584 inspectorWrap.call(inspector);
37586 } else if (!current) {
37587 featureListWrap.classed('inspector-hidden', false);
37588 inspectorWrap.classed('inspector-hidden', true);
37589 inspector.state('hide');
37593 sidebar.show = function(component) {
37594 featureListWrap.classed('inspector-hidden', true);
37595 inspectorWrap.classed('inspector-hidden', true);
37596 if (current) current.remove();
37597 current = selection.append('div')
37598 .attr('class', 'sidebar-component')
37602 sidebar.hide = function() {
37603 featureListWrap.classed('inspector-hidden', false);
37604 inspectorWrap.classed('inspector-hidden', true);
37605 if (current) current.remove();
37610 sidebar.hover = function() {};
37611 sidebar.hover.cancel = function() {};
37612 sidebar.select = function() {};
37613 sidebar.show = function() {};
37614 sidebar.hide = function() {};
37618 iD.ui.SourceSwitch = function(context) {
37622 d3.event.preventDefault();
37624 if (context.history().hasChanges() &&
37625 !window.confirm(t('source_switch.lose_changes'))) return;
37627 var live = d3.select(this)
37630 context.connection()
37631 .switch(live ? keys[1] : keys[0]);
37633 context.enter(iD.modes.Browse(context));
37637 .text(live ? t('source_switch.dev') : t('source_switch.live'))
37638 .classed('live', !live);
37641 var sourceSwitch = function(selection) {
37642 selection.append('a')
37644 .text(t('source_switch.live'))
37645 .classed('live', true)
37646 .attr('tabindex', -1)
37647 .on('click', click);
37650 sourceSwitch.keys = function(_) {
37651 if (!arguments.length) return keys;
37653 return sourceSwitch;
37656 return sourceSwitch;
37658 iD.ui.Spinner = function(context) {
37659 var connection = context.connection();
37661 return function(selection) {
37662 var img = selection.append('img')
37663 .attr('src', context.imagePath('loader-black.gif'))
37664 .style('opacity', 0);
37666 connection.on('loading.spinner', function() {
37668 .style('opacity', 1);
37671 connection.on('loaded.spinner', function() {
37673 .style('opacity', 0);
37677 iD.ui.Splash = function(context) {
37678 return function(selection) {
37679 if (context.storage('sawSplash'))
37682 context.storage('sawSplash', true);
37684 var modal = iD.ui.modal(selection);
37686 modal.select('.modal')
37687 .attr('class', 'modal-splash modal col6');
37689 var introModal = modal.select('.content')
37691 .attr('class', 'fillL');
37693 introModal.append('div')
37694 .attr('class','modal-section cf')
37695 .append('h3').text(t('splash.welcome'));
37697 introModal.append('div')
37698 .attr('class','modal-section')
37700 .html(t('splash.text', {
37701 version: iD.version,
37702 website: '<a href="http://ideditor.com/">ideditor.com</a>',
37703 github: '<a href="https://github.com/openstreetmap/iD">github.com</a>'
37706 var buttons = introModal.append('div').attr('class', 'modal-actions cf');
37708 buttons.append('button')
37709 .attr('class', 'col6 walkthrough')
37710 .text(t('splash.walkthrough'))
37711 .on('click', function() {
37712 d3.select(document.body).call(iD.ui.intro(context));
37716 buttons.append('button')
37717 .attr('class', 'col6 start')
37718 .text(t('splash.start'))
37719 .on('click', modal.close);
37721 modal.select('button.close').attr('class','hide');
37725 iD.ui.Status = function(context) {
37726 var connection = context.connection(),
37729 return function(selection) {
37731 function update() {
37733 connection.status(function(err, apiStatus) {
37735 selection.html('');
37737 if (err && errCount++ < 2) return;
37740 selection.text(t('status.error'));
37742 } else if (apiStatus === 'readonly') {
37743 selection.text(t('status.readonly'));
37745 } else if (apiStatus === 'offline') {
37746 selection.text(t('status.offline'));
37749 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
37750 if (!err) errCount = 0;
37755 connection.on('auth', function() { update(selection); });
37756 window.setInterval(update, 90000);
37760 iD.ui.Success = function(context) {
37761 var dispatch = d3.dispatch('cancel'),
37764 function success(selection) {
37765 var message = (changeset.comment || t('success.edited_osm')).substring(0, 130) +
37766 ' ' + context.connection().changesetURL(changeset.id);
37768 var header = selection.append('div')
37769 .attr('class', 'header fillL');
37771 header.append('button')
37772 .attr('class', 'fr')
37773 .on('click', function() { dispatch.cancel(); })
37774 .call(iD.svg.Icon('#icon-close'));
37776 header.append('h3')
37777 .text(t('success.just_edited'));
37779 var body = selection.append('div')
37780 .attr('class', 'body save-success fillL');
37783 .html(t('success.help_html'));
37786 .attr('class', 'details')
37787 .attr('target', '_blank')
37788 .attr('tabindex', -1)
37789 .call(iD.svg.Icon('#icon-out-link', 'inline'))
37790 .attr('href', t('success.help_link_url'))
37792 .text(t('success.help_link_text'));
37794 var changesetURL = context.connection().changesetURL(changeset.id);
37797 .attr('class', 'button col12 osm')
37798 .attr('target', '_blank')
37799 .attr('href', changesetURL)
37800 .text(t('success.view_on_osm'));
37803 facebook: 'https://facebook.com/sharer/sharer.php?u=' + encodeURIComponent(changesetURL),
37804 twitter: 'https://twitter.com/intent/tweet?source=webclient&text=' + encodeURIComponent(message),
37805 google: 'https://plus.google.com/share?url=' + encodeURIComponent(changesetURL)
37808 body.selectAll('.button.social')
37809 .data(d3.entries(sharing))
37812 .attr('class', 'button social col4')
37813 .attr('target', '_blank')
37814 .attr('href', function(d) { return d.value; })
37815 .call(bootstrap.tooltip()
37816 .title(function(d) { return t('success.' + d.key); })
37817 .placement('bottom'))
37818 .each(function(d) { d3.select(this).call(iD.svg.Icon('#logo-' + d.key, 'social')); });
37821 success.changeset = function(_) {
37822 if (!arguments.length) return changeset;
37827 return d3.rebind(success, dispatch, 'on');
37829 iD.ui.TagReference = function(tag, context) {
37830 var tagReference = {},
37836 function findLocal(data) {
37837 var locale = iD.detect().locale.toLowerCase(),
37840 localized = _.find(data, function(d) {
37841 return d.lang.toLowerCase() === locale;
37843 if (localized) return localized;
37845 // try the non-regional version of a language, like
37846 // 'en' if the language is 'en-US'
37847 if (locale.indexOf('-') !== -1) {
37848 var first = locale.split('-')[0];
37849 localized = _.find(data, function(d) {
37850 return d.lang.toLowerCase() === first;
37852 if (localized) return localized;
37855 // finally fall back to english
37856 return _.find(data, function(d) {
37857 return d.lang.toLowerCase() === 'en';
37861 function load(param) {
37862 button.classed('tag-reference-loading', true);
37864 context.taginfo().docs(param, function show(err, data) {
37866 if (!err && data) {
37867 docs = findLocal(data);
37872 if (!docs || !docs.description) {
37873 if (param.hasOwnProperty('value')) {
37874 load(_.omit(param, 'value')); // retry with key only
37876 body.append('p').text(t('inspector.no_documentation_key'));
37882 if (docs.image && docs.image.thumb_url_prefix) {
37885 .attr('class', 'wiki-image')
37886 .attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix)
37887 .on('load', function() { done(); })
37888 .on('error', function() { d3.select(this).remove(); done(); });
37895 .text(docs.description);
37899 .attr('target', '_blank')
37900 .attr('tabindex', -1)
37901 .attr('href', 'https://wiki.openstreetmap.org/wiki/' + docs.title)
37902 .call(iD.svg.Icon('#icon-out-link', 'inline'))
37904 .text(t('inspector.reference'));
37911 button.classed('tag-reference-loading', false);
37915 .style('max-height', '200px')
37916 .style('opacity', '1');
37921 function hide(selection) {
37922 selection = selection || body.transition().duration(200);
37925 .style('max-height', '0px')
37926 .style('opacity', '0');
37931 tagReference.button = function(selection) {
37932 button = selection.selectAll('.tag-reference-button')
37937 .attr('class', 'tag-reference-button')
37938 .attr('tabindex', -1)
37939 .call(iD.svg.Icon('#icon-inspect'));
37941 button.on('click', function () {
37942 d3.event.stopPropagation();
37943 d3.event.preventDefault();
37946 } else if (loaded) {
37949 if (context.taginfo()) {
37956 tagReference.body = function(selection) {
37957 body = selection.selectAll('.tag-reference-body')
37960 body.enter().append('div')
37961 .attr('class', 'tag-reference-body cf')
37962 .style('max-height', '0')
37963 .style('opacity', '0');
37965 if (showing === false) {
37970 tagReference.showing = function(_) {
37971 if (!arguments.length) return showing;
37973 return tagReference;
37976 return tagReference;
37978 // toggles the visibility of ui elements, using a combination of the
37979 // hide class, which sets display=none, and a d3 transition for opacity.
37980 // this will cause blinking when called repeatedly, so check that the
37981 // value actually changes between calls.
37982 iD.ui.Toggle = function(show, callback) {
37983 return function(selection) {
37985 .style('opacity', show ? 0 : 1)
37986 .classed('hide', false)
37988 .style('opacity', show ? 1 : 0)
37989 .each('end', function() {
37991 .classed('hide', !show)
37992 .style('opacity', null);
37993 if (callback) callback.apply(this);
37997 iD.ui.UndoRedo = function(context) {
38000 cmd: iD.ui.cmd('⌘Z'),
38001 action: function() { if (!(context.inIntro() || saving())) context.undo(); },
38002 annotation: function() { return context.history().undoAnnotation(); }
38005 cmd: iD.ui.cmd('⌘⇧Z'),
38006 action: function() {if (!(context.inIntro() || saving())) context.redo(); },
38007 annotation: function() { return context.history().redoAnnotation(); }
38010 function saving() {
38011 return context.mode().id === 'save';
38014 return function(selection) {
38015 var tooltip = bootstrap.tooltip()
38016 .placement('bottom')
38018 .title(function (d) {
38019 return iD.ui.tooltipHtml(d.annotation() ?
38020 t(d.id + '.tooltip', {action: d.annotation()}) :
38021 t(d.id + '.nothing'), d.cmd);
38024 var buttons = selection.selectAll('button')
38026 .enter().append('button')
38027 .attr('class', 'col6 disabled')
38028 .on('click', function(d) { return d.action(); })
38031 buttons.each(function(d) {
38033 .call(iD.svg.Icon('#icon-' + d.id));
38036 var keybinding = d3.keybinding('undo')
38037 .on(commands[0].cmd, function() { d3.event.preventDefault(); commands[0].action(); })
38038 .on(commands[1].cmd, function() { d3.event.preventDefault(); commands[1].action(); });
38040 d3.select(document)
38044 .on('change.undo_redo', update);
38047 .on('enter.undo_redo', update);
38049 function update() {
38051 .property('disabled', saving())
38052 .classed('disabled', function(d) { return !d.annotation(); })
38054 var selection = d3.select(this);
38055 if (selection.property('tooltipVisible')) {
38056 selection.call(tooltip.show);
38062 iD.ui.ViewOnOSM = function(context) {
38065 function viewOnOSM(selection) {
38066 var entity = context.entity(id);
38068 selection.style('display', entity.isNew() ? 'none' : null);
38070 var $link = selection.selectAll('.view-on-osm')
38075 .attr('class', 'view-on-osm')
38076 .attr('target', '_blank')
38077 .call(iD.svg.Icon('#icon-out-link', 'inline'))
38079 .text(t('inspector.view_on_osm'));
38082 .attr('href', context.connection().entityURL(entity));
38085 viewOnOSM.entityID = function(_) {
38086 if (!arguments.length) return id;
38093 iD.ui.Zoom = function(context) {
38097 title: t('zoom.in'),
38098 action: context.zoomIn,
38103 title: t('zoom.out'),
38104 action: context.zoomOut,
38108 function zoomIn() {
38109 d3.event.preventDefault();
38110 if (!context.inIntro()) context.zoomIn();
38113 function zoomOut() {
38114 d3.event.preventDefault();
38115 if (!context.inIntro()) context.zoomOut();
38118 function zoomInFurther() {
38119 d3.event.preventDefault();
38120 if (!context.inIntro()) context.zoomInFurther();
38123 function zoomOutFurther() {
38124 d3.event.preventDefault();
38125 if (!context.inIntro()) context.zoomOutFurther();
38129 return function(selection) {
38130 var button = selection.selectAll('button')
38132 .enter().append('button')
38133 .attr('tabindex', -1)
38134 .attr('class', function(d) { return d.id; })
38135 .on('click.editor', function(d) { d.action(); })
38136 .call(bootstrap.tooltip()
38139 .title(function(d) {
38140 return iD.ui.tooltipHtml(d.title, d.key);
38143 button.each(function(d) {
38145 .call(iD.svg.Icon('#icon-' + d.icon, 'light'));
38148 var keybinding = d3.keybinding('zoom');
38150 _.each(['=','ffequals','plus','ffplus'], function(key) {
38151 keybinding.on(key, zoomIn);
38152 keybinding.on('⇧' + key, zoomIn);
38153 keybinding.on(iD.ui.cmd('⌘' + key), zoomInFurther);
38154 keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomInFurther);
38156 _.each(['-','ffminus','_','dash'], function(key) {
38157 keybinding.on(key, zoomOut);
38158 keybinding.on('⇧' + key, zoomOut);
38159 keybinding.on(iD.ui.cmd('⌘' + key), zoomOutFurther);
38160 keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomOutFurther);
38163 d3.select(document)
38167 iD.ui.preset.access = function(field) {
38168 var dispatch = d3.dispatch('change'),
38171 function access(selection) {
38172 var wrap = selection.selectAll('.preset-input-wrap')
38175 wrap.enter().append('div')
38176 .attr('class', 'cf preset-input-wrap')
38179 items = wrap.select('ul').selectAll('li')
38184 var enter = items.enter().append('li')
38185 .attr('class', function(d) { return 'cf preset-access-' + d; });
38187 enter.append('span')
38188 .attr('class', 'col6 label preset-label-access')
38189 .attr('for', function(d) { return 'preset-input-access-' + d; })
38190 .text(function(d) { return field.t('types.' + d); });
38192 enter.append('div')
38193 .attr('class', 'col6 preset-input-access-wrap')
38195 .attr('type', 'text')
38196 .attr('class', 'preset-input-access')
38197 .attr('id', function(d) { return 'preset-input-access-' + d; })
38198 .each(function(d) {
38200 .call(d3.combobox()
38201 .data(access.options(d)));
38206 wrap.selectAll('.preset-input-access')
38207 .on('change', change)
38208 .on('blur', change);
38211 function change(d) {
38213 tag[d] = d3.select(this).value() || undefined;
38214 dispatch.change(tag);
38217 access.options = function(type) {
38218 var options = ['no', 'permissive', 'private', 'destination'];
38220 if (type !== 'access') {
38221 options.unshift('yes');
38222 options.push('designated');
38224 if (type === 'bicycle') {
38225 options.push('dismount');
38229 return options.map(function(option) {
38231 title: field.t('options.' + option + '.description'),
38237 var placeholders = {
38239 foot: 'designated',
38240 motor_vehicle: 'no'
38244 motor_vehicle: 'no',
38250 motor_vehicle: 'no'
38253 motor_vehicle: 'no',
38254 bicycle: 'designated'
38257 motor_vehicle: 'no',
38258 horse: 'designated'
38262 motor_vehicle: 'no',
38268 motor_vehicle: 'yes',
38273 motor_vehicle: 'yes'
38277 motor_vehicle: 'yes',
38283 motor_vehicle: 'yes',
38289 motor_vehicle: 'yes',
38295 motor_vehicle: 'yes',
38301 motor_vehicle: 'yes',
38307 motor_vehicle: 'yes',
38313 motor_vehicle: 'yes',
38318 motor_vehicle: 'yes'
38322 motor_vehicle: 'yes',
38328 motor_vehicle: 'yes',
38334 motor_vehicle: 'yes',
38340 access.tags = function(tags) {
38341 items.selectAll('.preset-input-access')
38342 .value(function(d) { return tags[d] || ''; })
38343 .attr('placeholder', function() {
38344 return tags.access ? tags.access : field.placeholder();
38347 // items.selectAll('#preset-input-access-access')
38348 // .attr('placeholder', 'yes');
38350 _.forEach(placeholders[tags.highway], function(v, k) {
38351 items.selectAll('#preset-input-access-' + k)
38352 .attr('placeholder', function() { return (tags.access || v); });
38356 access.focus = function() {
38357 items.selectAll('.preset-input-access')
38361 return d3.rebind(access, dispatch, 'on');
38363 iD.ui.preset.address = function(field, context) {
38364 var dispatch = d3.dispatch('init', 'change'),
38377 function getStreets() {
38378 var extent = entity.extent(context.graph()),
38379 l = extent.center(),
38380 box = iD.geo.Extent(l).padByMeters(200);
38382 return context.intersects(box)
38383 .filter(isAddressable)
38385 var loc = context.projection([
38386 (extent[0][0] + extent[1][0]) / 2,
38387 (extent[0][1] + extent[1][1]) / 2]),
38388 choice = iD.geo.chooseEdge(context.childNodes(d), loc, context.projection);
38390 title: d.tags.name,
38391 value: d.tags.name,
38392 dist: choice.distance
38394 }).sort(function(a, b) {
38395 return a.dist - b.dist;
38398 function isAddressable(d) {
38399 return d.tags.highway && d.tags.name && d.type === 'way';
38403 function getCities() {
38404 var extent = entity.extent(context.graph()),
38405 l = extent.center(),
38406 box = iD.geo.Extent(l).padByMeters(200);
38408 return context.intersects(box)
38409 .filter(isAddressable)
38412 title: d.tags['addr:city'] || d.tags.name,
38413 value: d.tags['addr:city'] || d.tags.name,
38414 dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
38416 }).sort(function(a, b) {
38417 return a.dist - b.dist;
38420 function isAddressable(d) {
38422 (d.tags.admin_level === '8' || d.tags.border_type === 'city'))
38425 if (d.tags.place && d.tags.name && (
38426 d.tags.place === 'city' ||
38427 d.tags.place === 'town' ||
38428 d.tags.place === 'village'))
38431 if (d.tags['addr:city']) return true;
38437 function getPostCodes() {
38438 var extent = entity.extent(context.graph()),
38439 l = extent.center(),
38440 box = iD.geo.Extent(l).padByMeters(200);
38442 return context.intersects(box)
38443 .filter(isAddressable)
38446 title: d.tags['addr:postcode'],
38447 value: d.tags['addr:postcode'],
38448 dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
38450 }).sort(function(a, b) {
38451 return a.dist - b.dist;
38454 function isAddressable(d) {
38455 return d.tags['addr:postcode'];
38459 function address(selection) {
38460 isInitialized = false;
38462 wrap = selection.selectAll('.preset-input-wrap')
38469 .attr('class', 'preset-input-wrap');
38471 var center = entity.extent(context.graph()).center(),
38474 iD.services.nominatim().countryCode(center, function (err, countryCode) {
38475 addressFormat = _.find(iD.data.addressFormats, function (a) {
38476 return a && a.countryCodes && _.contains(a.countryCodes, countryCode);
38477 }) || _.first(iD.data.addressFormats);
38480 // Normalize widths.
38481 var total = _.reduce(r, function(sum, field) {
38482 return sum + (widths[field] || 0.5);
38485 return r.map(function (field) {
38488 width: (widths[field] || 0.5) / total
38493 wrap.selectAll('div')
38494 .data(addressFormat.format)
38497 .attr('class', 'addr-row')
38498 .selectAll('input')
38502 .property('type', 'text')
38503 .attr('placeholder', function (d) { return field.t('placeholders.' + d.id); })
38504 .attr('class', function (d) { return 'addr-' + d.id; })
38505 .style('width', function (d) { return d.width * 100 + '%'; });
38509 wrap.selectAll('.addr-street')
38510 .call(d3.combobox()
38511 .fetcher(function(value, callback) {
38512 callback(getStreets());
38515 wrap.selectAll('.addr-city')
38516 .call(d3.combobox()
38517 .fetcher(function(value, callback) {
38518 callback(getCities());
38521 wrap.selectAll('.addr-postcode')
38522 .call(d3.combobox()
38523 .fetcher(function(value, callback) {
38524 callback(getPostCodes());
38527 wrap.selectAll('input')
38528 .on('blur', change())
38529 .on('change', change());
38531 wrap.selectAll('input:not(.combobox-input)')
38532 .on('input', change(true));
38535 isInitialized = true;
38539 function change(onInput) {
38540 return function() {
38543 wrap.selectAll('input')
38544 .each(function (field) {
38545 tags['addr:' + field.id] = this.value || undefined;
38548 dispatch.change(tags, onInput);
38552 function updateTags(tags) {
38553 wrap.selectAll('input')
38554 .value(function (field) {
38555 return tags['addr:' + field.id] || '';
38559 address.entity = function(_) {
38560 if (!arguments.length) return entity;
38565 address.tags = function(tags) {
38566 if (isInitialized) {
38569 dispatch.on('init', function () {
38575 address.focus = function() {
38576 var node = wrap.selectAll('input').node();
38577 if (node) node.focus();
38580 return d3.rebind(address, dispatch, 'on');
38582 iD.ui.preset.check =
38583 iD.ui.preset.defaultcheck = function(field) {
38584 var dispatch = d3.dispatch('change'),
38585 options = field.strings && field.strings.options,
38588 entity, value, box, text, label;
38591 for (var k in options) {
38592 values.push(k === 'undefined' ? undefined : k);
38593 texts.push(field.t('options.' + k, { 'default': options[k] }));
38596 values = [undefined, 'yes'];
38597 texts = [t('inspector.unknown'), t('inspector.check.yes')];
38598 if (field.type === 'check') {
38600 texts.push(t('inspector.check.no'));
38604 var check = function(selection) {
38605 // hack: pretend oneway field is a oneway_yes field
38606 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
38607 if (field.id === 'oneway') {
38608 for (var key in entity.tags) {
38609 if (key in iD.oneWayTags && (entity.tags[key] in iD.oneWayTags[key])) {
38610 texts[0] = t('presets.fields.oneway_yes.options.undefined');
38616 selection.classed('checkselect', 'true');
38618 label = selection.selectAll('.preset-input-wrap')
38621 var enter = label.enter().append('label')
38622 .attr('class', 'preset-input-wrap');
38624 enter.append('input')
38625 .property('indeterminate', field.type === 'check')
38626 .attr('type', 'checkbox')
38627 .attr('id', 'preset-input-' + field.id);
38629 enter.append('span')
38631 .attr('class', 'value');
38633 box = label.select('input')
38634 .on('click', function() {
38636 t[field.key] = values[(values.indexOf(value) + 1) % values.length];
38637 dispatch.change(t);
38638 d3.event.stopPropagation();
38641 text = label.select('span.value');
38644 check.entity = function(_) {
38645 if (!arguments.length) return entity;
38650 check.tags = function(tags) {
38651 value = tags[field.key];
38652 box.property('indeterminate', field.type === 'check' && !value);
38653 box.property('checked', value === 'yes');
38654 text.text(texts[values.indexOf(value)]);
38655 label.classed('set', !!value);
38658 check.focus = function() {
38659 box.node().focus();
38662 return d3.rebind(check, dispatch, 'on');
38664 iD.ui.preset.combo =
38665 iD.ui.preset.typeCombo =
38666 iD.ui.preset.multiCombo = function(field, context) {
38667 var dispatch = d3.dispatch('change'),
38668 isMulti = (field.type === 'multiCombo'),
38669 optstrings = field.strings && field.strings.options,
38670 optarray = field.options,
38671 snake_case = (field.snake_case || (field.snake_case === undefined)),
38672 combobox = d3.combobox().minItems(isMulti ? 1 : 2),
38679 // ensure multiCombo field.key ends with a ':'
38680 if (isMulti && field.key.match(/:$/) === null) {
38685 function snake(s) {
38686 return s.replace(/\s+/g, '_');
38689 function unsnake(s) {
38690 return s.replace(/_+/g, ' ');
38693 function clean(s) {
38694 return s.split(';')
38695 .map(function(s) { return s.trim(); })
38700 // returns the tag value for a display value
38701 // (for multiCombo, dval should be the key suffix, not the entire key)
38702 function tagValue(dval) {
38703 dval = clean(dval || '');
38706 var match = _.find(comboData, function(o) { return o.value === dval && o.key; });
38712 if (field.type === 'typeCombo' && !dval) {
38716 return (snake_case ? snake(dval) : dval) || undefined;
38720 // returns the display value for a tag value
38721 // (for multiCombo, tval should be the key suffix, not the entire key)
38722 function displayValue(tval) {
38726 var match = _.find(comboData, function(o) { return o.key === tval && o.value; });
38728 return match.value;
38732 if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
38736 return snake_case ? unsnake(tval) : tval;
38740 function objectDifference(a, b) {
38741 return _.reject(a, function(d1) {
38742 return _.any(b, function(d2) { return d1.value === d2.value; });
38747 function initCombo(selection, attachTo) {
38749 selection.attr('readonly', 'readonly');
38750 selection.call(combobox, attachTo);
38751 setStaticValues(setPlaceholder);
38753 } else if (optarray) {
38754 selection.call(combobox, attachTo);
38755 setStaticValues(setPlaceholder);
38757 } else if (context.taginfo()) {
38758 selection.call(combobox.fetcher(setTaginfoValues), attachTo);
38759 setTaginfoValues('', setPlaceholder);
38764 function setStaticValues(callback) {
38765 if (!(optstrings || optarray)) return;
38768 comboData = Object.keys(optstrings).map(function(k) {
38769 var v = field.t('options.' + k, { 'default': optstrings[k] });
38777 } else if (optarray) {
38778 comboData = optarray.map(function(k) {
38779 var v = snake_case ? unsnake(k) : k;
38788 combobox.data(objectDifference(comboData, multiData));
38789 if (callback) callback(comboData);
38793 function setTaginfoValues(q, callback) {
38794 var fn = isMulti ? 'multikeys' : 'values';
38795 context.taginfo()[fn]({
38798 geometry: context.geometry(entity.id),
38799 query: (isMulti ? field.key : '') + q
38800 }, function(err, data) {
38802 comboData = _.pluck(data, 'value').map(function(k) {
38803 if (isMulti) k = k.replace(field.key, '');
38804 var v = snake_case ? unsnake(k) : k;
38811 comboData = objectDifference(comboData, multiData);
38812 if (callback) callback(comboData);
38817 function setPlaceholder(d) {
38820 ph = field.placeholder() || t('inspector.add');
38822 var vals = _.pluck(d, 'value').filter(function(s) { return s.length < 20; }),
38823 placeholders = vals.length > 1 ? vals : _.pluck(d, 'key');
38824 ph = field.placeholder() || placeholders.slice(0, 3).join(', ');
38827 input.attr('placeholder', ph + '…');
38831 function change() {
38832 var val = tagValue(input.value()),
38837 container.classed('active', false);
38839 field.keys.push(field.key + val);
38840 t[field.key + val] = 'yes';
38841 window.setTimeout(function() { input.node().focus(); }, 10);
38844 t[field.key] = val;
38847 dispatch.change(t);
38851 function removeMultikey(d) {
38852 d3.event.stopPropagation();
38854 t[d.key] = undefined;
38855 dispatch.change(t);
38859 function combo(selection) {
38861 container = selection.selectAll('ul').data([0]);
38865 .attr('class', 'form-field-multicombo')
38866 .on('click', function() {
38867 window.setTimeout(function() { input.node().focus(); }, 10);
38871 container = selection;
38874 input = container.selectAll('input')
38879 .attr('type', 'text')
38880 .attr('id', 'preset-input-' + field.id)
38881 .call(initCombo, selection);
38884 .on('change', change)
38885 .on('blur', change);
38889 .on('accept', function() {
38890 input.node().blur();
38891 input.node().focus();
38895 .on('focus', function() { container.classed('active', true); });
38900 combo.tags = function(tags) {
38904 // Build multiData array containing keys already set..
38905 Object.keys(tags).forEach(function(key) {
38906 if (key.indexOf(field.key) !== 0 || tags[key].toLowerCase() !== 'yes') return;
38908 var suffix = key.substring(field.key.length);
38911 value: displayValue(suffix)
38915 // Set keys for form-field modified (needed for undo and reset buttons)..
38916 field.keys = _.pluck(multiData, 'key');
38918 // Exclude existing multikeys from combo options..
38919 var available = objectDifference(comboData, multiData);
38920 combobox.data(available);
38922 // Hide "Add" button if this field uses fixed set of
38923 // translateable optstrings and they're all currently used..
38924 container.selectAll('.combobox-input, .combobox-caret')
38925 .classed('hide', optstrings && !available.length);
38929 var chips = container.selectAll('.chips').data(multiData);
38931 var enter = chips.enter()
38932 .insert('li', 'input')
38933 .attr('class', 'chips');
38935 enter.append('span');
38938 chips.select('span')
38939 .text(function(d) { return d.value; });
38942 .on('click', removeMultikey)
38943 .attr('class', 'remove')
38950 input.value(displayValue(tags[field.key]));
38955 combo.focus = function() {
38956 input.node().focus();
38960 combo.entity = function(_) {
38961 if (!arguments.length) return entity;
38967 return d3.rebind(combo, dispatch, 'on');
38969 iD.ui.preset.cycleway = function(field) {
38970 var dispatch = d3.dispatch('change'),
38973 function cycleway(selection) {
38974 var wrap = selection.selectAll('.preset-input-wrap')
38977 wrap.enter().append('div')
38978 .attr('class', 'cf preset-input-wrap')
38981 items = wrap.select('ul').selectAll('li')
38986 var enter = items.enter().append('li')
38987 .attr('class', function(d) { return 'cf preset-cycleway-' + d; });
38989 enter.append('span')
38990 .attr('class', 'col6 label preset-label-cycleway')
38991 .attr('for', function(d) { return 'preset-input-cycleway-' + d; })
38992 .text(function(d) { return field.t('types.' + d); });
38994 enter.append('div')
38995 .attr('class', 'col6 preset-input-cycleway-wrap')
38997 .attr('type', 'text')
38998 .attr('class', 'preset-input-cycleway')
38999 .attr('id', function(d) { return 'preset-input-cycleway-' + d; })
39000 .each(function(d) {
39002 .call(d3.combobox()
39003 .data(cycleway.options(d)));
39008 wrap.selectAll('.preset-input-cycleway')
39009 .on('change', change)
39010 .on('blur', change);
39013 function change() {
39014 var inputs = d3.selectAll('.preset-input-cycleway')[0],
39015 left = d3.select(inputs[0]).value(),
39016 right = d3.select(inputs[1]).value(),
39018 if (left === 'none' || left === '') { left = undefined; }
39019 if (right === 'none' || right === '') { right = undefined; }
39021 // Always set both left and right as changing one can affect the other
39023 cycleway: undefined,
39024 'cycleway:left': left,
39025 'cycleway:right': right
39028 // If the left and right tags match, use the cycleway tag to tag both
39029 // sides the same way
39030 if (left === right) {
39033 'cycleway:left': undefined,
39034 'cycleway:right': undefined
39038 dispatch.change(tag);
39041 cycleway.options = function() {
39042 return d3.keys(field.strings.options).map(function(option) {
39044 title: field.t('options.' + option + '.description'),
39050 cycleway.tags = function(tags) {
39051 items.selectAll('.preset-input-cycleway')
39052 .value(function(d) {
39053 // If cycleway is set, always return that
39054 if (tags.cycleway) {
39055 return tags.cycleway;
39057 return tags[d] || '';
39059 .attr('placeholder', field.placeholder());
39062 cycleway.focus = function() {
39063 items.selectAll('.preset-input-cycleway')
39067 return d3.rebind(cycleway, dispatch, 'on');
39069 iD.ui.preset.text =
39070 iD.ui.preset.number =
39072 iD.ui.preset.email =
39073 iD.ui.preset.url = function(field) {
39075 var dispatch = d3.dispatch('change'),
39078 function i(selection) {
39079 input = selection.selectAll('input')
39082 input.enter().append('input')
39083 .attr('type', field.type)
39084 .attr('id', 'preset-input-' + field.id)
39085 .attr('placeholder', field.placeholder() || t('inspector.unknown'));
39088 .on('input', change(true))
39089 .on('blur', change())
39090 .on('change', change());
39092 if (field.type === 'number') {
39093 input.attr('type', 'text');
39095 var spinControl = selection.selectAll('.spin-control')
39098 var enter = spinControl.enter().append('div')
39099 .attr('class', 'spin-control');
39101 enter.append('button')
39103 .attr('class', 'increment')
39104 .attr('tabindex', -1);
39106 enter.append('button')
39108 .attr('class', 'decrement')
39109 .attr('tabindex', -1);
39111 spinControl.selectAll('button')
39112 .on('click', function(d) {
39113 d3.event.preventDefault();
39114 var num = parseInt(input.node().value || 0, 10);
39115 if (!isNaN(num)) input.node().value = num + d;
39121 function change(onInput) {
39122 return function() {
39124 t[field.key] = input.value() || undefined;
39125 dispatch.change(t, onInput);
39129 i.tags = function(tags) {
39130 input.value(tags[field.key] || '');
39133 i.focus = function() {
39134 input.node().focus();
39137 return d3.rebind(i, dispatch, 'on');
39139 iD.ui.preset.localized = function(field, context) {
39140 var dispatch = d3.dispatch('change', 'input'),
39141 wikipedia = iD.services.wikipedia(),
39142 input, localizedInputs, wikiTitles,
39145 function i(selection) {
39146 input = selection.selectAll('.localized-main')
39149 input.enter().append('input')
39150 .attr('type', 'text')
39151 .attr('id', 'preset-input-' + field.id)
39152 .attr('class', 'localized-main')
39153 .attr('placeholder', field.placeholder());
39155 if (field.id === 'name') {
39156 var preset = context.presets().match(entity, context.graph());
39157 input.call(d3.combobox().fetcher(
39158 iD.util.SuggestNames(preset, iD.data.suggestions)
39163 .on('input', change(true))
39164 .on('blur', change())
39165 .on('change', change());
39167 var translateButton = selection.selectAll('.localized-add')
39170 translateButton.enter()
39172 .attr('class', 'button-input-action localized-add minor')
39173 .attr('tabindex', -1)
39174 .call(iD.svg.Icon('#icon-plus'))
39175 .call(bootstrap.tooltip()
39176 .title(t('translate.translate'))
39177 .placement('left'));
39180 .on('click', addNew);
39182 localizedInputs = selection.selectAll('.localized-wrap')
39185 localizedInputs.enter().append('div')
39186 .attr('class', 'localized-wrap');
39189 function addNew() {
39190 d3.event.preventDefault();
39191 var data = localizedInputs.selectAll('div.entry').data();
39192 var defaultLang = iD.detect().locale.toLowerCase().split('-')[0];
39193 var langExists = _.find(data, function(datum) { return datum.lang === defaultLang;});
39194 var isLangEn = defaultLang.indexOf('en') > -1;
39195 if (isLangEn || langExists) {
39198 data.push({ lang: defaultLang, value: '' });
39199 localizedInputs.call(render, data);
39202 function change(onInput) {
39203 return function() {
39205 t[field.key] = d3.select(this).value() || undefined;
39206 dispatch.change(t, onInput);
39210 function key(lang) { return field.key + ':' + lang; }
39212 function changeLang(d) {
39213 var lang = d3.select(this).value(),
39215 language = _.find(iD.data.wikipedia, function(d) {
39216 return d[0].toLowerCase() === lang.toLowerCase() ||
39217 d[1].toLowerCase() === lang.toLowerCase();
39220 if (language) lang = language[2];
39222 if (d.lang && d.lang !== lang) {
39223 t[key(d.lang)] = undefined;
39226 var value = d3.select(this.parentNode)
39227 .selectAll('.localized-value')
39230 if (lang && value) {
39231 t[key(lang)] = value;
39232 } else if (lang && wikiTitles && wikiTitles[d.lang]) {
39233 t[key(lang)] = wikiTitles[d.lang];
39237 dispatch.change(t);
39240 function changeValue(d) {
39241 if (!d.lang) return;
39243 t[key(d.lang)] = d3.select(this).value() || undefined;
39244 dispatch.change(t);
39247 function fetcher(value, cb) {
39248 var v = value.toLowerCase();
39250 cb(iD.data.wikipedia.filter(function(d) {
39251 return d[0].toLowerCase().indexOf(v) >= 0 ||
39252 d[1].toLowerCase().indexOf(v) >= 0 ||
39253 d[2].toLowerCase().indexOf(v) >= 0;
39254 }).map(function(d) {
39255 return { value: d[1] };
39259 function render(selection, data) {
39260 var wraps = selection.selectAll('div.entry').
39261 data(data, function(d) { return d.lang; });
39263 var innerWrap = wraps.enter()
39264 .insert('div', ':first-child');
39266 innerWrap.attr('class', 'entry')
39268 var wrap = d3.select(this);
39269 var langcombo = d3.combobox().fetcher(fetcher).minItems(0);
39271 var label = wrap.append('label')
39272 .attr('class','form-label')
39273 .text(t('translate.localized_translation_label'))
39274 .attr('for','localized-lang');
39276 label.append('button')
39277 .attr('class', 'minor remove')
39278 .on('click', function(d){
39279 d3.event.preventDefault();
39281 t[key(d.lang)] = undefined;
39282 dispatch.change(t);
39283 d3.select(this.parentNode.parentNode)
39285 .style('max-height','240px')
39287 .style('opacity', '0')
39288 .style('max-height','0px')
39291 .call(iD.svg.Icon('#operation-delete'));
39293 wrap.append('input')
39294 .attr('class', 'localized-lang')
39295 .attr('type', 'text')
39296 .attr('placeholder',t('translate.localized_translation_language'))
39297 .on('blur', changeLang)
39298 .on('change', changeLang)
39301 wrap.append('input')
39302 .on('blur', changeValue)
39303 .on('change', changeValue)
39304 .attr('type', 'text')
39305 .attr('placeholder', t('translate.localized_translation_name'))
39306 .attr('class', 'localized-value');
39310 .style('margin-top', '0px')
39311 .style('max-height', '0px')
39312 .style('opacity', '0')
39315 .style('margin-top', '10px')
39316 .style('max-height', '240px')
39317 .style('opacity', '1')
39318 .each('end', function() {
39320 .style('max-height', '')
39321 .style('overflow', 'visible');
39327 .style('max-height','0px')
39328 .style('opacity', '0')
39329 .style('top','-10px')
39332 var entry = selection.selectAll('.entry');
39334 entry.select('.localized-lang')
39335 .value(function(d) {
39336 var lang = _.find(iD.data.wikipedia, function(lang) { return lang[2] === d.lang; });
39337 return lang ? lang[1] : d.lang;
39340 entry.select('.localized-value')
39341 .value(function(d) { return d.value; });
39344 i.tags = function(tags) {
39345 // Fetch translations from wikipedia
39346 if (tags.wikipedia && !wikiTitles) {
39348 var wm = tags.wikipedia.match(/([^:]+):(.+)/);
39349 if (wm && wm[0] && wm[1]) {
39350 wikipedia.translations(wm[1], wm[2], function(d) {
39356 input.value(tags[field.key] || '');
39358 var postfixed = [], k, m;
39360 m = k.match(/^(.*):([a-zA-Z_-]+)$/);
39361 if (m && m[1] === field.key && m[2]) {
39362 postfixed.push({ lang: m[2], value: tags[k] });
39366 localizedInputs.call(render, postfixed.reverse());
39369 i.focus = function() {
39370 input.node().focus();
39373 i.entity = function(_) {
39377 return d3.rebind(i, dispatch, 'on');
39379 iD.ui.preset.maxspeed = function(field, context) {
39380 var dispatch = d3.dispatch('change'),
39387 var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
39388 imperialValues = [20, 25, 30, 35, 40, 45, 50, 55, 65, 70];
39390 function maxspeed(selection) {
39391 combobox = d3.combobox();
39392 var unitCombobox = d3.combobox().data(['km/h', 'mph'].map(comboValues));
39394 input = selection.selectAll('#preset-input-' + field.id)
39397 input.enter().append('input')
39398 .attr('type', 'text')
39399 .attr('id', 'preset-input-' + field.id)
39400 .attr('placeholder', field.placeholder());
39404 .on('change', change)
39405 .on('blur', change);
39407 var childNodes = context.graph().childNodes(context.entity(entity.id)),
39408 loc = childNodes[~~(childNodes.length/2)].loc;
39410 imperial = _.any(iD.data.imperial.features, function(f) {
39411 return _.any(f.geometry.coordinates, function(d) {
39412 return iD.geo.pointInPolygon(loc, d[0]);
39416 unitInput = selection.selectAll('input.maxspeed-unit')
39419 unitInput.enter().append('input')
39420 .attr('type', 'text')
39421 .attr('class', 'maxspeed-unit');
39424 .on('blur', changeUnits)
39425 .on('change', changeUnits)
39426 .call(unitCombobox);
39428 function changeUnits() {
39429 imperial = unitInput.value() === 'mph';
39430 unitInput.value(imperial ? 'mph' : 'km/h');
39437 function setSuggestions() {
39438 combobox.data((imperial ? imperialValues : metricValues).map(comboValues));
39439 unitInput.value(imperial ? 'mph' : 'km/h');
39442 function comboValues(d) {
39444 value: d.toString(),
39445 title: d.toString()
39449 function change() {
39451 value = input.value();
39454 tag[field.key] = undefined;
39455 } else if (isNaN(value) || !imperial) {
39456 tag[field.key] = value;
39458 tag[field.key] = value + ' mph';
39461 dispatch.change(tag);
39464 maxspeed.tags = function(tags) {
39465 var value = tags[field.key];
39467 if (value && value.indexOf('mph') >= 0) {
39468 value = parseInt(value, 10);
39470 } else if (value) {
39476 input.value(value || '');
39479 maxspeed.focus = function() {
39480 input.node().focus();
39483 maxspeed.entity = function(_) {
39487 return d3.rebind(maxspeed, dispatch, 'on');
39489 iD.ui.preset.radio = function(field) {
39490 var dispatch = d3.dispatch('change'),
39491 labels, radios, placeholder;
39493 function radio(selection) {
39494 selection.classed('preset-radio', true);
39496 var wrap = selection.selectAll('.preset-input-wrap')
39499 var buttonWrap = wrap.enter().append('div')
39500 .attr('class', 'preset-input-wrap toggle-list');
39502 buttonWrap.append('span')
39503 .attr('class', 'placeholder');
39505 placeholder = selection.selectAll('.placeholder');
39507 labels = wrap.selectAll('label')
39508 .data(field.options || field.keys);
39510 var enter = labels.enter().append('label');
39512 enter.append('input')
39513 .attr('type', 'radio')
39514 .attr('name', field.id)
39515 .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
39516 .attr('checked', false);
39518 enter.append('span')
39519 .text(function(d) { return field.t('options.' + d, { 'default': d }); });
39521 radios = labels.selectAll('input')
39522 .on('change', change);
39525 function change() {
39527 if (field.key) t[field.key] = undefined;
39528 radios.each(function(d) {
39529 var active = d3.select(this).property('checked');
39531 if (active) t[field.key] = d;
39533 t[d] = active ? 'yes' : undefined;
39536 dispatch.change(t);
39539 radio.tags = function(tags) {
39540 function checked(d) {
39542 return tags[field.key] === d;
39544 return !!(tags[d] && tags[d] !== 'no');
39548 labels.classed('active', checked);
39549 radios.property('checked', checked);
39550 var selection = radios.filter(function() { return this.checked; });
39551 if (selection.empty()) {
39552 placeholder.text(t('inspector.none'));
39554 placeholder.text(selection.attr('value'));
39558 radio.focus = function() {
39559 radios.node().focus();
39562 return d3.rebind(radio, dispatch, 'on');
39564 iD.ui.preset.restrictions = function(field, context) {
39565 var dispatch = d3.dispatch('change'),
39566 hover = iD.behavior.Hover(context),
39571 function restrictions(selection) {
39572 // if form field is hidden or has detached from dom, clean up.
39573 if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
39574 selection.call(restrictions.off);
39578 var wrap = selection.selectAll('.preset-input-wrap')
39581 var enter = wrap.enter()
39583 .attr('class', 'preset-input-wrap');
39587 .attr('class', 'restriction-help');
39590 var intersection = iD.geo.Intersection(context.graph(), vertexID),
39591 graph = intersection.graph,
39592 vertex = graph.entity(vertexID),
39593 filter = d3.functor(true),
39594 extent = iD.geo.Extent(),
39595 projection = iD.geo.RawMercator();
39597 var d = wrap.dimensions(),
39598 c = [d[0] / 2, d[1] / 2],
39602 .scale(256 * Math.pow(2, z) / (2 * Math.PI));
39604 var s = projection(vertex.loc);
39607 .translate([c[0] - s[0], c[1] - s[1]])
39608 .clipExtent([[0, 0], d]);
39610 var drawLayers = iD.svg.Layers(projection, context).only('osm').dimensions(d),
39611 drawVertices = iD.svg.Vertices(projection, context),
39612 drawLines = iD.svg.Lines(projection, context),
39613 drawTurns = iD.svg.Turns(projection, context);
39617 .selectAll('.surface')
39621 var surface = wrap.selectAll('.surface');
39625 .call(drawVertices, graph, [vertex], filter, extent, z)
39626 .call(drawLines, graph, intersection.ways, filter)
39627 .call(drawTurns, graph, intersection.turns(fromNodeID));
39630 .on('click.restrictions', click)
39631 .on('mouseover.restrictions', mouseover)
39632 .on('mouseout.restrictions', mouseout);
39635 .selectAll('.selected')
39636 .classed('selected', false);
39640 .selectAll('.' + intersection.highways[fromNodeID].id)
39641 .classed('selected', true);
39647 .on('change.restrictions', render);
39650 .on('resize.restrictions', function() {
39651 wrap.dimensions(null);
39656 var datum = d3.event.target.__data__;
39657 if (datum instanceof iD.Entity) {
39658 fromNodeID = intersection.adjacentNodeId(datum.id);
39660 } else if (datum instanceof iD.geo.Turn) {
39661 if (datum.restriction) {
39663 iD.actions.UnrestrictTurn(datum, projection),
39664 t('operations.restriction.annotation.delete'));
39667 iD.actions.RestrictTurn(datum, projection),
39668 t('operations.restriction.annotation.create'));
39673 function mouseover() {
39674 var datum = d3.event.target.__data__;
39675 if (datum instanceof iD.geo.Turn) {
39676 var graph = context.graph(),
39677 presets = context.presets(),
39680 if (datum.restriction) {
39681 preset = presets.match(graph.entity(datum.restriction), graph);
39683 preset = presets.item('type/restriction/' +
39684 iD.geo.inferRestriction(
39692 wrap.selectAll('.restriction-help')
39693 .text(t('operations.restriction.help.' +
39694 (datum.restriction ? 'toggle_off' : 'toggle_on'),
39695 {restriction: preset.name()}));
39699 function mouseout() {
39700 wrap.selectAll('.restriction-help')
39701 .text(t('operations.restriction.help.' +
39702 (fromNodeID ? 'toggle' : 'select')));
39705 function render() {
39706 if (context.hasEntity(vertexID)) {
39707 restrictions(selection);
39712 restrictions.entity = function(_) {
39713 if (!vertexID || vertexID !== _.id) {
39719 restrictions.tags = function() {};
39720 restrictions.focus = function() {};
39722 restrictions.off = function(selection) {
39723 selection.selectAll('.surface')
39725 .on('click.restrictions', null)
39726 .on('mouseover.restrictions', null)
39727 .on('mouseout.restrictions', null);
39730 .on('change.restrictions', null);
39733 .on('resize.restrictions', null);
39736 return d3.rebind(restrictions, dispatch, 'on');
39738 iD.ui.preset.textarea = function(field) {
39739 var dispatch = d3.dispatch('change'),
39742 function i(selection) {
39743 input = selection.selectAll('textarea')
39746 input.enter().append('textarea')
39747 .attr('id', 'preset-input-' + field.id)
39748 .attr('placeholder', field.placeholder() || t('inspector.unknown'))
39749 .attr('maxlength', 255);
39752 .on('input', change(true))
39753 .on('blur', change())
39754 .on('change', change());
39757 function change(onInput) {
39758 return function() {
39760 t[field.key] = input.value() || undefined;
39761 dispatch.change(t, onInput);
39765 i.tags = function(tags) {
39766 input.value(tags[field.key] || '');
39769 i.focus = function() {
39770 input.node().focus();
39773 return d3.rebind(i, dispatch, 'on');
39775 iD.ui.preset.wikipedia = function(field, context) {
39776 var dispatch = d3.dispatch('change'),
39777 wikipedia = iD.services.wikipedia(),
39778 link, entity, lang, title;
39780 function i(selection) {
39781 var langcombo = d3.combobox()
39782 .fetcher(function(value, cb) {
39783 var v = value.toLowerCase();
39785 cb(iD.data.wikipedia.filter(function(d) {
39786 return d[0].toLowerCase().indexOf(v) >= 0 ||
39787 d[1].toLowerCase().indexOf(v) >= 0 ||
39788 d[2].toLowerCase().indexOf(v) >= 0;
39789 }).map(function(d) {
39790 return { value: d[1] };
39794 var titlecombo = d3.combobox()
39795 .fetcher(function(value, cb) {
39797 if (!value) value = context.entity(entity.id).tags.name || '';
39798 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
39800 searchfn(language()[2], value, function(query, data) {
39801 cb(data.map(function(d) {
39802 return { value: d };
39807 lang = selection.selectAll('input.wiki-lang')
39810 lang.enter().append('input')
39811 .attr('type', 'text')
39812 .attr('class', 'wiki-lang')
39813 .attr('placeholder', t('translate.localized_translation_language'))
39818 .on('blur', changeLang)
39819 .on('change', changeLang);
39821 title = selection.selectAll('input.wiki-title')
39824 title.enter().append('input')
39825 .attr('type', 'text')
39826 .attr('class', 'wiki-title')
39827 .attr('id', 'preset-input-' + field.id);
39831 .on('blur', change)
39832 .on('change', change);
39834 link = selection.selectAll('a.wiki-link')
39837 link.enter().append('a')
39838 .attr('class', 'wiki-link button-input-action minor')
39839 .attr('tabindex', -1)
39840 .attr('target', '_blank')
39841 .call(iD.svg.Icon('#icon-out-link', 'inline'));
39844 function language() {
39845 var value = lang.value().toLowerCase();
39846 var locale = iD.detect().locale.toLowerCase();
39847 var localeLanguage;
39848 return _.find(iD.data.wikipedia, function(d) {
39849 if (d[2] === locale) localeLanguage = d;
39850 return d[0].toLowerCase() === value ||
39851 d[1].toLowerCase() === value ||
39853 }) || localeLanguage || ['English', 'English', 'en'];
39856 function changeLang() {
39857 lang.value(language()[1]);
39861 function change() {
39862 var value = title.value(),
39863 m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/),
39864 l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
39868 // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
39869 value = decodeURIComponent(m[2]).replace(/_/g, ' ');
39872 // Best-effort `anchordecode:` implementation
39873 anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
39875 anchor = decodeURIComponent(m[3]);
39877 value += '#' + anchor.replace(/_/g, ' ');
39879 value = value.slice(0, 1).toUpperCase() + value.slice(1);
39881 title.value(value);
39885 t[field.key] = value ? language()[2] + ':' + value : undefined;
39886 dispatch.change(t);
39889 i.tags = function(tags) {
39890 var value = tags[field.key] || '',
39891 m = value.match(/([^:]+):([^#]+)(?:#(.+))?/),
39892 l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
39893 anchor = m && m[3];
39895 // value in correct format
39898 title.value(m[2] + (anchor ? ('#' + anchor) : ''));
39901 // Best-effort `anchorencode:` implementation
39902 anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
39904 anchor = anchor.replace(/ /g, '_');
39907 link.attr('href', 'https://' + m[1] + '.wikipedia.org/wiki/' +
39908 m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : ''));
39910 // unrecognized value format
39912 title.value(value);
39913 if (value && value !== '') {
39916 link.attr('href', 'https://en.wikipedia.org/wiki/Special:Search?search=' + value);
39920 i.entity = function(_) {
39924 i.focus = function() {
39925 title.node().focus();
39928 return d3.rebind(i, dispatch, 'on');
39930 iD.ui.intro.area = function(context, reveal) {
39931 var event = d3.dispatch('done'),
39935 title: 'intro.areas.title'
39938 step.enter = function() {
39939 var playground = [-85.63552, 41.94159],
39940 corner = [-85.63565411045074, 41.9417715536927];
39941 context.map().centerZoom(playground, 19);
39942 reveal('button.add-area',
39943 t('intro.areas.add', { button: iD.ui.intro.icon('#icon-area', 'pre-text') }),
39944 { tooltipClass: 'intro-areas-add' });
39946 context.on('enter.intro', addArea);
39948 function addArea(mode) {
39949 if (mode.id !== 'add-area') return;
39950 context.on('enter.intro', drawArea);
39952 var padding = 120 * Math.pow(2, context.map().zoom() - 19);
39953 var pointBox = iD.ui.intro.pad(corner, padding, context);
39954 reveal(pointBox, t('intro.areas.corner'));
39956 context.map().on('move.intro', function() {
39957 padding = 120 * Math.pow(2, context.map().zoom() - 19);
39958 pointBox = iD.ui.intro.pad(corner, padding, context);
39959 reveal(pointBox, t('intro.areas.corner'), {duration: 0});
39963 function drawArea(mode) {
39964 if (mode.id !== 'draw-area') return;
39965 context.on('enter.intro', enterSelect);
39967 var padding = 150 * Math.pow(2, context.map().zoom() - 19);
39968 var pointBox = iD.ui.intro.pad(playground, padding, context);
39969 reveal(pointBox, t('intro.areas.place'));
39971 context.map().on('move.intro', function() {
39972 padding = 150 * Math.pow(2, context.map().zoom() - 19);
39973 pointBox = iD.ui.intro.pad(playground, padding, context);
39974 reveal(pointBox, t('intro.areas.place'), {duration: 0});
39978 function enterSelect(mode) {
39979 if (mode.id !== 'select') return;
39980 context.map().on('move.intro', null);
39981 context.on('enter.intro', null);
39983 timeout = setTimeout(function() {
39984 reveal('.preset-search-input',
39985 t('intro.areas.search',
39986 { name: context.presets().item('leisure/playground').name() }));
39987 d3.select('.preset-search-input').on('keyup.intro', keySearch);
39991 function keySearch() {
39992 var first = d3.select('.preset-list-item:first-child');
39993 if (first.classed('preset-leisure-playground')) {
39994 reveal(first.select('.preset-list-button').node(), t('intro.areas.choose'));
39995 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
39996 d3.select('.preset-search-input').on('keyup.intro', null);
40000 function selectedPreset() {
40002 t('intro.areas.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
40003 context.on('exit.intro', event.done);
40007 step.exit = function() {
40008 window.clearTimeout(timeout);
40009 context.on('enter.intro', null);
40010 context.on('exit.intro', null);
40011 context.history().on('change.intro', null);
40012 context.map().on('move.intro', null);
40013 d3.select('.preset-search-input').on('keyup.intro', null);
40016 return d3.rebind(step, event, 'on');
40018 iD.ui.intro.line = function(context, reveal) {
40019 var event = d3.dispatch('done'),
40023 title: 'intro.lines.title'
40026 function timeout(f, t) {
40027 timeouts.push(window.setTimeout(f, t));
40030 function eventCancel() {
40031 d3.event.stopPropagation();
40032 d3.event.preventDefault();
40035 step.enter = function() {
40036 var centroid = [-85.62830, 41.95699];
40037 var midpoint = [-85.62975395449628, 41.95787501510204];
40038 var start = [-85.6297754121684, 41.95805253325314];
40039 var intersection = [-85.62974496187628, 41.95742515554585];
40041 context.map().centerZoom(start, 18);
40042 reveal('button.add-line',
40043 t('intro.lines.add', { button: iD.ui.intro.icon('#icon-line', 'pre-text') }),
40044 { tooltipClass: 'intro-lines-add' });
40046 context.on('enter.intro', addLine);
40048 function addLine(mode) {
40049 if (mode.id !== 'add-line') return;
40050 context.on('enter.intro', drawLine);
40052 var padding = 150 * Math.pow(2, context.map().zoom() - 18);
40053 var pointBox = iD.ui.intro.pad(start, padding, context);
40054 reveal(pointBox, t('intro.lines.start'));
40056 context.map().on('move.intro', function() {
40057 padding = 150 * Math.pow(2, context.map().zoom() - 18);
40058 pointBox = iD.ui.intro.pad(start, padding, context);
40059 reveal(pointBox, t('intro.lines.start'), {duration: 0});
40063 function drawLine(mode) {
40064 if (mode.id !== 'draw-line') return;
40065 context.history().on('change.intro', addIntersection);
40066 context.on('enter.intro', retry);
40068 var padding = 300 * Math.pow(2, context.map().zoom() - 19);
40069 var pointBox = iD.ui.intro.pad(midpoint, padding, context);
40070 reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}));
40072 context.map().on('move.intro', function() {
40073 padding = 300 * Math.pow(2, context.map().zoom() - 19);
40074 pointBox = iD.ui.intro.pad(midpoint, padding, context);
40075 reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}), {duration: 0});
40079 // ended line before creating intersection
40080 function retry(mode) {
40081 if (mode.id !== 'select') return;
40082 var pointBox = iD.ui.intro.pad(intersection, 30, context),
40083 ids = mode.selectedIDs();
40084 reveal(pointBox, t('intro.lines.restart', {name: t('intro.graph.flower_st')}));
40085 d3.select(window).on('mousedown.intro', eventCancel, true);
40087 timeout(function() {
40088 context.replace(iD.actions.DeleteMultiple(ids));
40094 function addIntersection(changes) {
40095 if ( _.any(changes.created(), function(d) {
40096 return d.type === 'node' && context.graph().parentWays(d).length > 1;
40098 context.history().on('change.intro', null);
40099 context.on('enter.intro', enterSelect);
40101 var padding = 900 * Math.pow(2, context.map().zoom() - 19);
40102 var pointBox = iD.ui.intro.pad(centroid, padding, context);
40103 reveal(pointBox, t('intro.lines.finish'));
40105 context.map().on('move.intro', function() {
40106 padding = 900 * Math.pow(2, context.map().zoom() - 19);
40107 pointBox = iD.ui.intro.pad(centroid, padding, context);
40108 reveal(pointBox, t('intro.lines.finish'), {duration: 0});
40113 function enterSelect(mode) {
40114 if (mode.id !== 'select') return;
40115 context.map().on('move.intro', null);
40116 context.on('enter.intro', null);
40117 d3.select('#curtain').style('pointer-events', 'all');
40122 function presetCategory() {
40123 timeout(function() {
40124 d3.select('#curtain').style('pointer-events', 'none');
40125 var road = d3.select('.preset-category-road .preset-list-button');
40126 reveal(road.node(), t('intro.lines.road'));
40127 road.one('click.intro', roadCategory);
40131 function roadCategory() {
40132 timeout(function() {
40133 var grid = d3.select('.subgrid');
40134 reveal(grid.node(), t('intro.lines.residential'));
40135 grid.selectAll(':not(.preset-highway-residential) .preset-list-button')
40136 .one('click.intro', retryPreset);
40137 grid.selectAll('.preset-highway-residential .preset-list-button')
40138 .one('click.intro', roadDetails);
40142 // selected wrong road type
40143 function retryPreset() {
40144 timeout(function() {
40145 var preset = d3.select('.entity-editor-pane .preset-list-button');
40146 reveal(preset.node(), t('intro.lines.wrong_preset'));
40147 preset.one('click.intro', presetCategory);
40151 function roadDetails() {
40153 t('intro.lines.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
40154 context.on('exit.intro', event.done);
40159 step.exit = function() {
40160 d3.select(window).on('mousedown.intro', null, true);
40161 d3.select('#curtain').style('pointer-events', 'none');
40162 timeouts.forEach(window.clearTimeout);
40163 context.on('enter.intro', null);
40164 context.on('exit.intro', null);
40165 context.map().on('move.intro', null);
40166 context.history().on('change.intro', null);
40169 return d3.rebind(step, event, 'on');
40171 iD.ui.intro.navigation = function(context, reveal) {
40172 var event = d3.dispatch('done'),
40176 title: 'intro.navigation.title'
40179 function set(f, t) {
40180 timeouts.push(window.setTimeout(f, t));
40183 function eventCancel() {
40184 d3.event.stopPropagation();
40185 d3.event.preventDefault();
40188 step.enter = function() {
40189 var rect = context.surfaceRect(),
40191 left: rect.left + 10,
40192 top: rect.top + 70,
40193 width: rect.width - 70,
40194 height: rect.height - 170
40197 context.map().centerZoom([-85.63591, 41.94285], 19);
40199 reveal(map, t('intro.navigation.drag'));
40201 context.map().on('move.intro', _.debounce(function() {
40202 context.map().on('move.intro', null);
40204 context.on('enter.intro', inspectTownHall);
40207 function townhall() {
40208 var hall = [-85.63645945147184, 41.942986488012565];
40210 var point = context.projection(hall);
40211 if (point[0] < 0 || point[0] > rect.width ||
40212 point[1] < 0 || point[1] > rect.height) {
40213 context.map().center(hall);
40216 var box = iD.ui.intro.pointBox(hall, context);
40217 reveal(box, t('intro.navigation.select'));
40219 context.map().on('move.intro', function() {
40220 var box = iD.ui.intro.pointBox(hall, context);
40221 reveal(box, t('intro.navigation.select'), {duration: 0});
40225 function inspectTownHall(mode) {
40226 if (mode.id !== 'select') return;
40227 context.on('enter.intro', null);
40228 context.map().on('move.intro', null);
40230 reveal('.entity-editor-pane',
40231 t('intro.navigation.pane', { button: iD.ui.intro.icon('#icon-close', 'pre-text') }));
40232 context.on('exit.intro', streetSearch);
40236 function streetSearch() {
40237 context.on('exit.intro', null);
40238 reveal('.search-header input',
40239 t('intro.navigation.search', { name: t('intro.graph.spring_st') }));
40240 d3.select('.search-header input').on('keyup.intro', searchResult);
40243 function searchResult() {
40244 var first = d3.select('.feature-list-item:nth-child(0n+2)'), // skip No Results item
40245 firstName = first.select('.entity-name'),
40246 name = t('intro.graph.spring_st');
40248 if (!firstName.empty() && firstName.text() === name) {
40249 reveal(first.node(), t('intro.navigation.choose', { name: name }));
40250 context.on('exit.intro', selectedStreet);
40251 d3.select('.search-header input')
40252 .on('keydown.intro', eventCancel, true)
40253 .on('keyup.intro', null);
40257 function selectedStreet() {
40258 var springSt = [-85.63585099140167, 41.942506848938926];
40259 context.map().center(springSt);
40260 context.on('exit.intro', event.done);
40262 reveal('.entity-editor-pane',
40263 t('intro.navigation.chosen', {
40264 name: t('intro.graph.spring_st'),
40265 button: iD.ui.intro.icon('#icon-close', 'pre-text')
40271 step.exit = function() {
40272 timeouts.forEach(window.clearTimeout);
40273 context.map().on('move.intro', null);
40274 context.on('enter.intro', null);
40275 context.on('exit.intro', null);
40276 d3.select('.search-header input')
40277 .on('keydown.intro', null)
40278 .on('keyup.intro', null);
40281 return d3.rebind(step, event, 'on');
40283 iD.ui.intro.point = function(context, reveal) {
40284 var event = d3.dispatch('done'),
40288 title: 'intro.points.title'
40291 function setTimeout(f, t) {
40292 timeouts.push(window.setTimeout(f, t));
40295 function eventCancel() {
40296 d3.event.stopPropagation();
40297 d3.event.preventDefault();
40300 step.enter = function() {
40301 context.map().centerZoom([-85.63279, 41.94394], 19);
40302 reveal('button.add-point',
40303 t('intro.points.add', { button: iD.ui.intro.icon('#icon-point', 'pre-text') }),
40304 { tooltipClass: 'intro-points-add' });
40306 var corner = [-85.632481,41.944094];
40308 context.on('enter.intro', addPoint);
40310 function addPoint(mode) {
40311 if (mode.id !== 'add-point') return;
40312 context.on('enter.intro', enterSelect);
40314 var pointBox = iD.ui.intro.pad(corner, 150, context);
40315 reveal(pointBox, t('intro.points.place'));
40317 context.map().on('move.intro', function() {
40318 pointBox = iD.ui.intro.pad(corner, 150, context);
40319 reveal(pointBox, t('intro.points.place'), {duration: 0});
40323 function enterSelect(mode) {
40324 if (mode.id !== 'select') return;
40325 context.map().on('move.intro', null);
40326 context.on('enter.intro', null);
40328 setTimeout(function() {
40329 reveal('.preset-search-input',
40330 t('intro.points.search', {name: context.presets().item('amenity/cafe').name()}));
40331 d3.select('.preset-search-input').on('keyup.intro', keySearch);
40335 function keySearch() {
40336 var first = d3.select('.preset-list-item:first-child');
40337 if (first.classed('preset-amenity-cafe')) {
40338 reveal(first.select('.preset-list-button').node(), t('intro.points.choose'));
40339 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
40340 d3.select('.preset-search-input')
40341 .on('keydown.intro', eventCancel, true)
40342 .on('keyup.intro', null);
40346 function selectedPreset() {
40347 setTimeout(function() {
40348 reveal('.entity-editor-pane', t('intro.points.describe'), {tooltipClass: 'intro-points-describe'});
40349 context.history().on('change.intro', closeEditor);
40350 context.on('exit.intro', selectPoint);
40354 function closeEditor() {
40355 d3.select('.preset-search-input').on('keydown.intro', null);
40356 context.history().on('change.intro', null);
40357 reveal('.entity-editor-pane',
40358 t('intro.points.close', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
40361 function selectPoint() {
40362 context.on('exit.intro', null);
40363 context.history().on('change.intro', null);
40364 context.on('enter.intro', enterReselect);
40366 var pointBox = iD.ui.intro.pad(corner, 150, context);
40367 reveal(pointBox, t('intro.points.reselect'));
40369 context.map().on('move.intro', function() {
40370 pointBox = iD.ui.intro.pad(corner, 150, context);
40371 reveal(pointBox, t('intro.points.reselect'), {duration: 0});
40375 function enterReselect(mode) {
40376 if (mode.id !== 'select') return;
40377 context.map().on('move.intro', null);
40378 context.on('enter.intro', null);
40380 setTimeout(function() {
40381 reveal('.entity-editor-pane',
40382 t('intro.points.fixname', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
40383 context.on('exit.intro', deletePoint);
40387 function deletePoint() {
40388 context.on('exit.intro', null);
40389 context.on('enter.intro', enterDelete);
40391 var pointBox = iD.ui.intro.pad(corner, 150, context);
40392 reveal(pointBox, t('intro.points.reselect_delete'));
40394 context.map().on('move.intro', function() {
40395 pointBox = iD.ui.intro.pad(corner, 150, context);
40396 reveal(pointBox, t('intro.points.reselect_delete'), {duration: 0});
40400 function enterDelete(mode) {
40401 if (mode.id !== 'select') return;
40402 context.map().on('move.intro', null);
40403 context.on('enter.intro', null);
40404 context.on('exit.intro', deletePoint);
40405 context.map().on('move.intro', deletePoint);
40406 context.history().on('change.intro', deleted);
40408 setTimeout(function() {
40409 var node = d3.select('.radial-menu-item-delete').node();
40410 var pointBox = iD.ui.intro.pad(node.getBoundingClientRect(), 50, context);
40412 t('intro.points.delete', { button: iD.ui.intro.icon('#operation-delete', 'pre-text') }));
40416 function deleted(changed) {
40417 if (changed.deleted().length) event.done();
40422 step.exit = function() {
40423 timeouts.forEach(window.clearTimeout);
40424 context.on('exit.intro', null);
40425 context.on('enter.intro', null);
40426 context.map().on('move.intro', null);
40427 context.history().on('change.intro', null);
40428 d3.select('.preset-search-input')
40429 .on('keyup.intro', null)
40430 .on('keydown.intro', null);
40433 return d3.rebind(step, event, 'on');
40435 iD.ui.intro.startEditing = function(context, reveal) {
40436 var event = d3.dispatch('done', 'startEditing'),
40441 title: 'intro.startediting.title'
40444 function timeout(f, t) {
40445 timeouts.push(window.setTimeout(f, t));
40448 step.enter = function() {
40449 reveal('.map-control.help-control',
40450 t('intro.startediting.help', { button: iD.ui.intro.icon('#icon-help', 'pre-text') }));
40452 timeout(function() {
40453 reveal('#bar button.save', t('intro.startediting.save'));
40456 timeout(function() {
40457 reveal('#surface');
40460 timeout(function() {
40461 modal = iD.ui.modal(context.container());
40463 modal.select('.modal')
40464 .attr('class', 'modal-splash modal col6');
40466 modal.selectAll('.close').remove();
40468 var startbutton = modal.select('.content')
40469 .attr('class', 'fillL')
40471 .attr('class', 'modal-section huge-modal-button')
40472 .on('click', function() {
40476 startbutton.append('div')
40477 .attr('class','illustration');
40478 startbutton.append('h2')
40479 .text(t('intro.startediting.start'));
40481 event.startEditing();
40485 step.exit = function() {
40486 if (modal) modal.remove();
40487 timeouts.forEach(window.clearTimeout);
40490 return d3.rebind(step, event, 'on');
40492 iD.presets = function() {
40494 // an iD.presets.Collection with methods for
40495 // loading new data and returning defaults
40497 var all = iD.presets.Collection([]),
40498 defaults = { area: all, line: all, point: all, vertex: all, relation: all },
40501 recent = iD.presets.Collection([]);
40503 // Index of presets by (geometry, tag key).
40512 all.match = function(entity, resolver) {
40513 var geometry = entity.geometry(resolver),
40514 geometryMatches = index[geometry],
40518 for (var k in entity.tags) {
40519 var keyMatches = geometryMatches[k];
40520 if (!keyMatches) continue;
40522 for (var i = 0; i < keyMatches.length; i++) {
40523 var score = keyMatches[i].matchScore(entity);
40524 if (score > best) {
40526 match = keyMatches[i];
40531 return match || all.item(geometry);
40534 // Because of the open nature of tagging, iD will never have a complete
40535 // list of tags used in OSM, so we want it to have logic like "assume
40536 // that a closed way with an amenity tag is an area, unless the amenity
40537 // is one of these specific types". This function computes a structure
40538 // that allows testing of such conditions, based on the presets designated
40539 // as as supporting (or not supporting) the area geometry.
40541 // The returned object L is a whitelist/blacklist of tags. A closed way
40542 // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
40543 // (see `iD.Way#isArea()`). In other words, the keys of L form the whitelist,
40544 // and the subkeys form the blacklist.
40545 all.areaKeys = function() {
40547 ignore = ['barrier', 'highway', 'footway', 'railway', 'type'],
40548 presets = _.reject(all.collection, 'suggestion');
40551 presets.forEach(function(d) {
40552 for (var key in d.tags) break;
40554 if (ignore.indexOf(key) !== -1) return;
40556 if (d.geometry.indexOf('area') !== -1) {
40557 areaKeys[key] = areaKeys[key] || {};
40562 presets.forEach(function(d) {
40563 for (var key in d.tags) break;
40565 if (ignore.indexOf(key) !== -1) return;
40567 var value = d.tags[key];
40568 if (d.geometry.indexOf('area') === -1 &&
40569 d.geometry.indexOf('line') !== -1 &&
40570 key in areaKeys && value !== '*') {
40571 areaKeys[key][value] = true;
40578 all.load = function(d) {
40581 _.forEach(d.fields, function(d, id) {
40582 fields[id] = iD.presets.Field(id, d);
40583 if (d.universal) universal.push(fields[id]);
40588 _.forEach(d.presets, function(d, id) {
40589 all.collection.push(iD.presets.Preset(id, d, fields));
40593 if (d.categories) {
40594 _.forEach(d.categories, function(d, id) {
40595 all.collection.push(iD.presets.Category(id, d, all));
40600 var getItem = _.bind(all.item, all);
40602 area: iD.presets.Collection(d.defaults.area.map(getItem)),
40603 line: iD.presets.Collection(d.defaults.line.map(getItem)),
40604 point: iD.presets.Collection(d.defaults.point.map(getItem)),
40605 vertex: iD.presets.Collection(d.defaults.vertex.map(getItem)),
40606 relation: iD.presets.Collection(d.defaults.relation.map(getItem))
40610 for (var i = 0; i < all.collection.length; i++) {
40611 var preset = all.collection[i],
40612 geometry = preset.geometry;
40614 for (var j = 0; j < geometry.length; j++) {
40615 var g = index[geometry[j]];
40616 for (var k in preset.tags) {
40617 (g[k] = g[k] || []).push(preset);
40625 all.field = function(id) {
40629 all.universal = function() {
40633 all.defaults = function(geometry, n) {
40634 var rec = recent.matchGeometry(geometry).collection.slice(0, 4),
40635 def = _.uniq(rec.concat(defaults[geometry].collection)).slice(0, n - 1);
40636 return iD.presets.Collection(_.unique(rec.concat(def).concat(all.item(geometry))));
40639 all.choose = function(preset) {
40640 if (!preset.isFallback()) {
40641 recent = iD.presets.Collection(_.unique([preset].concat(recent.collection)));
40648 iD.presets.Category = function(id, category, all) {
40649 category = _.clone(category);
40653 category.members = iD.presets.Collection(category.members.map(function(id) {
40654 return all.item(id);
40657 category.matchGeometry = function(geometry) {
40658 return category.geometry.indexOf(geometry) >= 0;
40661 category.matchScore = function() { return -1; };
40663 category.name = function() {
40664 return t('presets.categories.' + id + '.name', {'default': id});
40667 category.terms = function() {
40673 iD.presets.Collection = function(collection) {
40675 var maxSearchResults = 50,
40676 maxSuggestionResults = 10;
40680 collection: collection,
40682 item: function(id) {
40683 return _.find(collection, function(d) {
40684 return d.id === id;
40688 matchGeometry: function(geometry) {
40689 return iD.presets.Collection(collection.filter(function(d) {
40690 return d.matchGeometry(geometry);
40694 search: function(value, geometry) {
40695 if (!value) return this;
40697 value = value.toLowerCase();
40699 var searchable = _.filter(collection, function(a) {
40700 return a.searchable !== false && a.suggestion !== true;
40702 suggestions = _.filter(collection, function(a) {
40703 return a.suggestion === true;
40706 function leading(a) {
40707 var index = a.indexOf(value);
40708 return index === 0 || a[index - 1] === ' ';
40711 // matches value to preset.name
40712 var leading_name = _.filter(searchable, function(a) {
40713 return leading(a.name().toLowerCase());
40714 }).sort(function(a, b) {
40715 var i = a.name().toLowerCase().indexOf(value) - b.name().toLowerCase().indexOf(value);
40716 if (i === 0) return a.name().length - b.name().length;
40720 // matches value to preset.terms values
40721 var leading_terms = _.filter(searchable, function(a) {
40722 return _.any(a.terms() || [], leading);
40725 // matches value to preset.tags values
40726 var leading_tag_values = _.filter(searchable, function(a) {
40727 return _.any(_.without(_.values(a.tags || {}), '*'), leading);
40731 // finds close matches to value in preset.name
40732 var levenstein_name = searchable.map(function(a) {
40735 dist: iD.util.editDistance(value, a.name().toLowerCase())
40737 }).filter(function(a) {
40738 return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
40739 }).sort(function(a, b) {
40740 return a.dist - b.dist;
40741 }).map(function(a) {
40745 // finds close matches to value in preset.terms
40746 var leventstein_terms = _.filter(searchable, function(a) {
40747 return _.any(a.terms() || [], function(b) {
40748 return iD.util.editDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
40752 function suggestionName(name) {
40753 var nameArray = name.split(' - ');
40754 if (nameArray.length > 1) {
40755 name = nameArray.slice(0, nameArray.length-1).join(' - ');
40757 return name.toLowerCase();
40760 var leading_suggestions = _.filter(suggestions, function(a) {
40761 return leading(suggestionName(a.name()));
40762 }).sort(function(a, b) {
40763 a = suggestionName(a.name());
40764 b = suggestionName(b.name());
40765 var i = a.indexOf(value) - b.indexOf(value);
40766 if (i === 0) return a.length - b.length;
40770 var leven_suggestions = suggestions.map(function(a) {
40773 dist: iD.util.editDistance(value, suggestionName(a.name()))
40775 }).filter(function(a) {
40776 return a.dist + Math.min(value.length - suggestionName(a.preset.name()).length, 0) < 1;
40777 }).sort(function(a, b) {
40778 return a.dist - b.dist;
40779 }).map(function(a) {
40783 var other = presets.item(geometry);
40785 var results = leading_name.concat(
40787 leading_tag_values,
40788 leading_suggestions.slice(0, maxSuggestionResults+5),
40791 leven_suggestions.slice(0, maxSuggestionResults)
40792 ).slice(0, maxSearchResults-1);
40794 return iD.presets.Collection(_.unique(
40795 results.concat(other)
40802 iD.presets.Field = function(id, field) {
40803 field = _.clone(field);
40807 field.matchGeometry = function(geometry) {
40808 return !field.geometry || field.geometry === geometry;
40811 field.t = function(scope, options) {
40812 return t('presets.fields.' + id + '.' + scope, options);
40815 field.label = function() {
40816 return field.t('label', {'default': id});
40819 var placeholder = field.placeholder;
40820 field.placeholder = function() {
40821 return field.t('placeholder', {'default': placeholder});
40826 iD.presets.Preset = function(id, preset, fields) {
40827 preset = _.clone(preset);
40830 preset.fields = (preset.fields || []).map(getFields);
40831 preset.geometry = (preset.geometry || []);
40833 function getFields(f) {
40837 preset.matchGeometry = function(geometry) {
40838 return preset.geometry.indexOf(geometry) >= 0;
40841 var matchScore = preset.matchScore || 1;
40842 preset.matchScore = function(entity) {
40843 var tags = preset.tags,
40846 for (var t in tags) {
40847 if (entity.tags[t] === tags[t]) {
40848 score += matchScore;
40849 } else if (tags[t] === '*' && t in entity.tags) {
40850 score += matchScore / 2;
40859 preset.t = function(scope, options) {
40860 return t('presets.presets.' + id + '.' + scope, options);
40863 var name = preset.name;
40864 preset.name = function() {
40865 if (preset.suggestion) {
40866 id = id.split('/');
40867 id = id[0] + '/' + id[1];
40868 return name + ' - ' + t('presets.presets.' + id + '.name');
40870 return preset.t('name', {'default': name});
40873 preset.terms = function() {
40874 return preset.t('terms', {'default': ''}).toLowerCase().trim().split(/\s*,+\s*/);
40877 preset.isFallback = function() {
40878 var tagCount = Object.keys(preset.tags).length;
40879 return tagCount === 0 || (tagCount === 1 && preset.tags.hasOwnProperty('area'));
40882 preset.reference = function(geometry) {
40883 var key = Object.keys(preset.tags)[0],
40884 value = preset.tags[key];
40886 if (geometry === 'relation' && key === 'type') {
40887 return { rtype: value };
40888 } else if (value === '*') {
40889 return { key: key };
40891 return { key: key, value: value };
40895 var removeTags = preset.removeTags || preset.tags;
40896 preset.removeTags = function(tags, geometry) {
40897 tags = _.omit(tags, _.keys(removeTags));
40899 for (var f in preset.fields) {
40900 var field = preset.fields[f];
40901 if (field.matchGeometry(geometry) && field.default === tags[field.key]) {
40902 delete tags[field.key];
40910 var applyTags = preset.addTags || preset.tags;
40911 preset.applyTags = function(tags, geometry) {
40914 tags = _.clone(tags);
40916 for (k in applyTags) {
40917 if (applyTags[k] === '*') {
40920 tags[k] = applyTags[k];
40924 // Add area=yes if necessary.
40925 // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
40926 // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
40927 // 2. chosen preset doesn't have a key in areaKeys (`railway=station`)
40928 if (geometry === 'area') {
40929 var needsAreaTag = true;
40930 if (preset.geometry.indexOf('line') === -1) {
40931 for (k in applyTags) {
40932 if (k in iD.areaKeys) {
40933 needsAreaTag = false;
40938 if (needsAreaTag) {
40943 for (var f in preset.fields) {
40944 var field = preset.fields[f];
40945 if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
40946 tags[field.key] = field.default;
40955 iD.validations = {};
40956 iD.validations.DeprecatedTag = function() {
40958 var validation = function(changes) {
40960 for (var i = 0; i < changes.created.length; i++) {
40961 var change = changes.created[i],
40962 deprecatedTags = change.deprecatedTags();
40964 if (!_.isEmpty(deprecatedTags)) {
40965 var tags = iD.util.tagText({ tags: deprecatedTags });
40967 id: 'deprecated_tags',
40968 message: t('validations.deprecated_tags', { tags: tags }),
40978 iD.validations.ManyDeletions = function() {
40979 var threshold = 100;
40981 var validation = function(changes) {
40983 if (changes.deleted.length > threshold) {
40985 id: 'many_deletions',
40986 message: t('validations.many_deletions', { n: changes.deleted.length })
40994 iD.validations.MissingTag = function() {
40996 // Slightly stricter check than Entity#isUsed (#3091)
40997 function hasTags(entity, graph) {
40998 return _.without(Object.keys(entity.tags), 'area', 'name').length > 0 ||
40999 graph.parentRelations(entity).length > 0;
41002 var validation = function(changes, graph) {
41004 for (var i = 0; i < changes.created.length; i++) {
41005 var change = changes.created[i],
41006 geometry = change.geometry(graph);
41008 if ((geometry === 'point' || geometry === 'line' || geometry === 'area') && !hasTags(change, graph)) {
41011 message: t('validations.untagged_' + geometry),
41012 tooltip: t('validations.untagged_' + geometry + '_tooltip'),
41022 iD.validations.TagSuggestsArea = function() {
41024 // https://github.com/openstreetmap/josm/blob/mirror/src/org/
41025 // openstreetmap/josm/data/validation/tests/UnclosedWays.java#L80
41026 function tagSuggestsArea(tags) {
41027 if (_.isEmpty(tags)) return false;
41029 var presence = ['landuse', 'amenities', 'tourism', 'shop'];
41030 for (var i = 0; i < presence.length; i++) {
41031 if (tags[presence[i]] !== undefined) {
41032 return presence[i] + '=' + tags[presence[i]];
41036 if (tags.building && tags.building === 'yes') return 'building=yes';
41039 var validation = function(changes, graph) {
41041 for (var i = 0; i < changes.created.length; i++) {
41042 var change = changes.created[i],
41043 geometry = change.geometry(graph),
41044 suggestion = (geometry === 'line' ? tagSuggestsArea(change.tags) : undefined);
41048 id: 'tag_suggests_area',
41049 message: t('validations.tag_suggests_area', { tag: suggestion }),
41060 window.locale = { _current: 'en' };
41062 locale.current = function(_) {
41063 if (!arguments.length) return locale._current;
41064 if (locale[_] !== undefined) locale._current = _;
41065 else if (locale[_.split('-')[0]]) locale._current = _.split('-')[0];
41069 function t(s, o, loc) {
41070 loc = loc || locale._current;
41072 var path = s.split(".").reverse(),
41075 while (rep !== undefined && path.length) rep = rep[path.pop()];
41077 if (rep !== undefined) {
41078 if (o) for (var k in o) rep = rep.replace('{' + k + '}', o[k]);
41082 if (loc !== 'en') {
41083 return t(s, o, 'en');
41086 if (o && 'default' in o) {
41087 return o['default'];
41090 var missing = 'Missing ' + loc + ' translation: ' + s;
41091 if (typeof console !== "undefined") console.error(missing);
41095 iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]},"n185964961":{"id":"n185964961","loc":[-85.6406588,41.942601]},"n185964962":{"id":"n185964962","loc":[-85.6394548,41.94261]},"n185970607":{"id":"n185970607","loc":[-85.641094,41.94006]},"n185970614":{"id":"n185970614","loc":[-85.641825,41.941316]},"n185970616":{"id":"n185970616","loc":[-85.641838,41.941556]},"n185973650":{"id":"n185973650","loc":[-85.639918,41.940064]},"n185973660":{"id":"n185973660","loc":[-85.640645,41.941339]},"n185973659":{"id":"n185973659","loc":[-85.6406115,41.9400658]},"n185974479":{"id":"n185974479","loc":[-85.639402,41.941344]},"n185974481":{"id":"n185974481","loc":[-85.643071,41.941288]},"n185976259":{"id":"n185976259","loc":[-85.642213,41.940043]},"n185976261":{"id":"n185976261","loc":[-85.643056,41.94001]},"n185964959":{"id":"n185964959","loc":[-85.6431031,41.9425754]},"n185964960":{"id":"n185964960","loc":[-85.6418749,41.9425864]},"n185981481":{"id":"n185981481","loc":[-85.6386827,41.9400828]},"n185981482":{"id":"n185981482","loc":[-85.6393664,41.9400854]},"n2138493844":{"id":"n2138493844","loc":[-85.6427969,41.940522]},"n2138493845":{"id":"n2138493845","loc":[-85.6425891,41.9405228]},"n2138493846":{"id":"n2138493846","loc":[-85.6425868,41.9402875]},"n2138493847":{"id":"n2138493847","loc":[-85.6427969,41.9402858]},"n2138493848":{"id":"n2138493848","loc":[-85.6425708,41.9405234]},"n2138493849":{"id":"n2138493849","loc":[-85.642568,41.9402855]},"n2138493850":{"id":"n2138493850","loc":[-85.6423157,41.9402886]},"n2138493851":{"id":"n2138493851","loc":[-85.6423212,41.9404362]},"n2138493852":{"id":"n2138493852","loc":[-85.6422923,41.9404578]},"n2138493853":{"id":"n2138493853","loc":[-85.6422868,41.9404834]},"n2138493854":{"id":"n2138493854","loc":[-85.6423226,41.9405091]},"n2138493855":{"id":"n2138493855","loc":[-85.6423847,41.9405111]},"n2138493856":{"id":"n2138493856","loc":[-85.6424081,41.9405265]},"n2140155811":{"id":"n2140155811","loc":[-85.6419547,41.9410956]},"n2140155814":{"id":"n2140155814","loc":[-85.6427577,41.9410884]},"n2140155816":{"id":"n2140155816","loc":[-85.6427545,41.9410052]},"n2140155818":{"id":"n2140155818","loc":[-85.6428057,41.9410028]},"n2140155821":{"id":"n2140155821","loc":[-85.6427993,41.9407339]},"n2140155823":{"id":"n2140155823","loc":[-85.6427385,41.9407339]},"n2140155825":{"id":"n2140155825","loc":[-85.6427417,41.9406435]},"n2140155827":{"id":"n2140155827","loc":[-85.6419515,41.9406482]},"n2140155828":{"id":"n2140155828","loc":[-85.6429368,41.9412407]},"n2140155829":{"id":"n2140155829","loc":[-85.6417756,41.9412526]},"n2140155830":{"id":"n2140155830","loc":[-85.641766,41.9405983]},"n2140155831":{"id":"n2140155831","loc":[-85.6419803,41.9405983]},"n2140155832":{"id":"n2140155832","loc":[-85.6419611,41.9401366]},"n2140155833":{"id":"n2140155833","loc":[-85.6429336,41.94012]},"n2140155834":{"id":"n2140155834","loc":[-85.6430697,41.9411732]},"n2140155835":{"id":"n2140155835","loc":[-85.6428411,41.9409974]},"n2140155837":{"id":"n2140155837","loc":[-85.6428388,41.9407211]},"n2140155839":{"id":"n2140155839","loc":[-85.6430624,41.9405521]},"n2140155840":{"id":"n2140155840","loc":[-85.6427323,41.9412396]},"n2140155842":{"id":"n2140155842","loc":[-85.6418147,41.9412457]},"n2140155844":{"id":"n2140155844","loc":[-85.641813,41.9411319]},"n2140155845":{"id":"n2140155845","loc":[-85.6418394,41.9411111]},"n2140155847":{"id":"n2140155847","loc":[-85.6418838,41.9410977]},"n2140155849":{"id":"n2140155849","loc":[-85.6427324,41.9410921]},"n2140155851":{"id":"n2140155851","loc":[-85.6427798,41.9412945]},"n2140155852":{"id":"n2140155852","loc":[-85.6427701,41.9411777]},"n2140155854":{"id":"n2140155854","loc":[-85.6427323,41.9411572]},"n2140155856":{"id":"n2140155856","loc":[-85.6418478,41.9411666]},"n2165942818":{"id":"n2165942818","loc":[-85.6437533,41.9415029]},"n2165942819":{"id":"n2165942819","loc":[-85.6437623,41.9421195]},"n2168510551":{"id":"n2168510551","loc":[-85.6423795,41.9422615]},"n2168510552":{"id":"n2168510552","loc":[-85.6423744,41.9419439]},"n2168510553":{"id":"n2168510553","loc":[-85.642518,41.9419427]},"n2168510554":{"id":"n2168510554","loc":[-85.6425186,41.9419801]},"n2168510555":{"id":"n2168510555","loc":[-85.6428314,41.9419773]},"n2168510556":{"id":"n2168510556","loc":[-85.6428368,41.9423116]},"n2168510557":{"id":"n2168510557","loc":[-85.6424947,41.9423146]},"n2168510558":{"id":"n2168510558","loc":[-85.6424938,41.9422605]},"n2189046007":{"id":"n2189046007","loc":[-85.6410866,41.9424327]},"n2189046009":{"id":"n2189046009","loc":[-85.6410805,41.9420061]},"n2189046011":{"id":"n2189046011","loc":[-85.6412443,41.9420048]},"n2189046012":{"id":"n2189046012","loc":[-85.6412505,41.9424314]},"n2189046014":{"id":"n2189046014","loc":[-85.6413311,41.942968]},"n2189046016":{"id":"n2189046016","loc":[-85.6413281,41.942713]},"n2189046018":{"id":"n2189046018","loc":[-85.641521,41.9427117]},"n2189046021":{"id":"n2189046021","loc":[-85.6415234,41.9429236]},"n2189046022":{"id":"n2189046022","loc":[-85.6415045,41.9429238]},"n2189046025":{"id":"n2189046025","loc":[-85.641505,41.9429668]},"n2189046053":{"id":"n2189046053","loc":[-85.6385988,41.942412]},"n2189046054":{"id":"n2189046054","loc":[-85.6385985,41.9423311]},"n2189046055":{"id":"n2189046055","loc":[-85.6387617,41.9423308]},"n2189046056":{"id":"n2189046056","loc":[-85.6387616,41.9423026]},"n2189046058":{"id":"n2189046058","loc":[-85.6388215,41.9423025]},"n2189046059":{"id":"n2189046059","loc":[-85.6388219,41.9424115]},"n2189046060":{"id":"n2189046060","loc":[-85.6391096,41.9424486]},"n2189046061":{"id":"n2189046061","loc":[-85.6391105,41.9423673]},"n2189046063":{"id":"n2189046063","loc":[-85.6392911,41.9423684]},"n2189046065":{"id":"n2189046065","loc":[-85.6392903,41.9424497]},"n2189046067":{"id":"n2189046067","loc":[-85.6397927,41.9423876]},"n2189046069":{"id":"n2189046069","loc":[-85.6397897,41.9422981]},"n2189046070":{"id":"n2189046070","loc":[-85.6399702,41.9422947]},"n2189046072":{"id":"n2189046072","loc":[-85.6399732,41.9423843]},"n2189046074":{"id":"n2189046074","loc":[-85.6396331,41.9430227]},"n2189046075":{"id":"n2189046075","loc":[-85.6398673,41.9430189]},"n2189046077":{"id":"n2189046077","loc":[-85.6398656,41.9429637]},"n2189046079":{"id":"n2189046079","loc":[-85.6398885,41.9429633]},"n2189046082":{"id":"n2189046082","loc":[-85.6398832,41.942779]},"n2189046083":{"id":"n2189046083","loc":[-85.6398513,41.9427796]},"n2189046085":{"id":"n2189046085","loc":[-85.6398502,41.9427401]},"n2189046087":{"id":"n2189046087","loc":[-85.6397889,41.9427411]},"n2189046089":{"id":"n2189046089","loc":[-85.6397892,41.942753]},"n2189046090":{"id":"n2189046090","loc":[-85.6396983,41.9427544]},"n2189046092":{"id":"n2189046092","loc":[-85.6396993,41.9427882]},"n2189046094":{"id":"n2189046094","loc":[-85.6396746,41.9427886]},"n2189046096":{"id":"n2189046096","loc":[-85.6396758,41.9428296]},"n2189046097":{"id":"n2189046097","loc":[-85.6397007,41.9428292]},"n2189046099":{"id":"n2189046099","loc":[-85.6397018,41.9428686]},"n2189046103":{"id":"n2189046103","loc":[-85.6396289,41.9428697]},"n2189046112":{"id":"n2189046112","loc":[-85.6435683,41.9429457]},"n2189046113":{"id":"n2189046113","loc":[-85.643568,41.9427766]},"n2189046115":{"id":"n2189046115","loc":[-85.6434011,41.9427767]},"n2189046116":{"id":"n2189046116","loc":[-85.6434012,41.9428631]},"n2189046117":{"id":"n2189046117","loc":[-85.643448,41.9428631]},"n2189046118":{"id":"n2189046118","loc":[-85.6434481,41.9429457]},"n2189046119":{"id":"n2189046119","loc":[-85.6428363,41.9429809]},"n2189046120":{"id":"n2189046120","loc":[-85.6429171,41.9429791]},"n2189046121":{"id":"n2189046121","loc":[-85.642914,41.9429041]},"n2189046122":{"id":"n2189046122","loc":[-85.6429385,41.9429035]},"n2189046123":{"id":"n2189046123","loc":[-85.6429348,41.9428126]},"n2189046124":{"id":"n2189046124","loc":[-85.6427746,41.9428163]},"n2189046125":{"id":"n2189046125","loc":[-85.6427783,41.942906]},"n2189046126":{"id":"n2189046126","loc":[-85.6428332,41.9429047]},"n2189046127":{"id":"n2189046127","loc":[-85.6423018,41.9428859]},"n2189046128":{"id":"n2189046128","loc":[-85.6422987,41.9427208]},"n2189046130":{"id":"n2189046130","loc":[-85.6424218,41.9427195]},"n2189046131":{"id":"n2189046131","loc":[-85.6424246,41.9428684]},"n2189046132":{"id":"n2189046132","loc":[-85.6423845,41.9428689]},"n2189046133":{"id":"n2189046133","loc":[-85.6423848,41.942885]},"n2189046134":{"id":"n2189046134","loc":[-85.641533,41.9429392]},"n2189046135":{"id":"n2189046135","loc":[-85.6416096,41.9428768]},"n2189046137":{"id":"n2189046137","loc":[-85.6416763,41.9429221]},"n2189046138":{"id":"n2189046138","loc":[-85.6415997,41.9429845]},"n2189046139":{"id":"n2189046139","loc":[-85.6420598,41.9428016]},"n2189046140":{"id":"n2189046140","loc":[-85.6420593,41.9427415]},"n2189046141":{"id":"n2189046141","loc":[-85.6421957,41.9427409]},"n2189046142":{"id":"n2189046142","loc":[-85.6421963,41.9428182]},"n2189046143":{"id":"n2189046143","loc":[-85.6421281,41.9428185]},"n2189046144":{"id":"n2189046144","loc":[-85.6421279,41.9428013]},"n2189046145":{"id":"n2189046145","loc":[-85.6409429,41.9429345]},"n2189046146":{"id":"n2189046146","loc":[-85.6410354,41.9429334]},"n2189046147":{"id":"n2189046147","loc":[-85.6410325,41.9427972]},"n2189046148":{"id":"n2189046148","loc":[-85.640997,41.9427976]},"n2189046149":{"id":"n2189046149","loc":[-85.6409963,41.9427643]},"n2189046150":{"id":"n2189046150","loc":[-85.6408605,41.9427659]},"n2189046152":{"id":"n2189046152","loc":[-85.6408623,41.9428482]},"n2189046153":{"id":"n2189046153","loc":[-85.640941,41.9428473]},"n2189152992":{"id":"n2189152992","loc":[-85.6437661,41.9422257]},"n2189152993":{"id":"n2189152993","loc":[-85.643768,41.9424067]},"n2189152994":{"id":"n2189152994","loc":[-85.6432176,41.9417705]},"n2189152995":{"id":"n2189152995","loc":[-85.6432097,41.941327]},"n2189152996":{"id":"n2189152996","loc":[-85.6436493,41.9413226]},"n2189152997":{"id":"n2189152997","loc":[-85.6436563,41.9417164]},"n2189152998":{"id":"n2189152998","loc":[-85.6435796,41.9417171]},"n2189152999":{"id":"n2189152999","loc":[-85.6435805,41.9417669]},"n2189153000":{"id":"n2189153000","loc":[-85.6438202,41.9414953]},"n2189153001":{"id":"n2189153001","loc":[-85.6438173,41.9413175]},"n2189153004":{"id":"n2189153004","loc":[-85.6432535,41.9418466]},"n2189153005":{"id":"n2189153005","loc":[-85.6433935,41.9418599]},"n2189153006":{"id":"n2189153006","loc":[-85.6434831,41.9418986]},"n2189153007":{"id":"n2189153007","loc":[-85.6435678,41.9419774]},"n2189153008":{"id":"n2189153008","loc":[-85.6435987,41.9420282]},"n2189153009":{"id":"n2189153009","loc":[-85.643438,41.9419573]},"n2189153010":{"id":"n2189153010","loc":[-85.6435284,41.9424676]},"n2189153011":{"id":"n2189153011","loc":[-85.6436207,41.9423631]},"n2189153012":{"id":"n2189153012","loc":[-85.6434957,41.9422973]},"n2189153013":{"id":"n2189153013","loc":[-85.6434457,41.9422458]},"n2189153014":{"id":"n2189153014","loc":[-85.6433976,41.9421772]},"n2189153015":{"id":"n2189153015","loc":[-85.6433861,41.9420785]},"n2189153016":{"id":"n2189153016","loc":[-85.6433765,41.9420313]},"n2189153017":{"id":"n2189153017","loc":[-85.6432207,41.9420284]},"n2189153018":{"id":"n2189153018","loc":[-85.6432245,41.9422759]},"n2189153019":{"id":"n2189153019","loc":[-85.6432649,41.9423474]},"n2189153020":{"id":"n2189153020","loc":[-85.6433226,41.9424132]},"n2189153021":{"id":"n2189153021","loc":[-85.6434111,41.9424704]},"n2189153022":{"id":"n2189153022","loc":[-85.6434591,41.9424347]},"n2189153025":{"id":"n2189153025","loc":[-85.6437669,41.9423073]},"n2189153026":{"id":"n2189153026","loc":[-85.6436611,41.942293]},"n2189153027":{"id":"n2189153027","loc":[-85.6435784,41.9422473]},"n2189153028":{"id":"n2189153028","loc":[-85.6435245,41.9421443]},"n2189153029":{"id":"n2189153029","loc":[-85.6435149,41.9420613]},"n2189153030":{"id":"n2189153030","loc":[-85.6433528,41.9419269]},"n2189153031":{"id":"n2189153031","loc":[-85.6432535,41.9419191]},"n2189153032":{"id":"n2189153032","loc":[-85.6430868,41.9419198]},"n2189153033":{"id":"n2189153033","loc":[-85.6434894,41.9420033]},"n2189153034":{"id":"n2189153034","loc":[-85.6432974,41.9419225]},"n2189153035":{"id":"n2189153035","loc":[-85.6433055,41.9421632]},"n2189153036":{"id":"n2189153036","loc":[-85.6433538,41.9422849]},"n2189153037":{"id":"n2189153037","loc":[-85.6434718,41.9423887]},"n2189153038":{"id":"n2189153038","loc":[-85.6436134,41.9422667]},"n2189153040":{"id":"n2189153040","loc":[-85.6438759,41.9414017]},"n2189153041":{"id":"n2189153041","loc":[-85.6438181,41.9413687]},"n2189153042":{"id":"n2189153042","loc":[-85.6436821,41.9413044]},"n2189153043":{"id":"n2189153043","loc":[-85.6435899,41.9412862]},"n2189153044":{"id":"n2189153044","loc":[-85.6433169,41.9417268]},"n2189153045":{"id":"n2189153045","loc":[-85.643301,41.9412859]},"n2189153046":{"id":"n2189153046","loc":[-85.6435531,41.9416981]},"n2189153047":{"id":"n2189153047","loc":[-85.6435427,41.9412863]},"n185948706":{"id":"n185948706","loc":[-85.6369439,41.940122]},"n185949348":{"id":"n185949348","loc":[-85.640039,41.931135]},"n185949870":{"id":"n185949870","loc":[-85.643195,41.949261]},"n185954680":{"id":"n185954680","loc":[-85.6337802,41.9401143]},"n185954784":{"id":"n185954784","loc":[-85.6487485,41.942527]},"n185958670":{"id":"n185958670","loc":[-85.637255,41.940104]},"n185958672":{"id":"n185958672","loc":[-85.636996,41.941355]},"n185960207":{"id":"n185960207","loc":[-85.634992,41.940118]},"n185963163":{"id":"n185963163","loc":[-85.638831,41.93398]},"n185963165":{"id":"n185963165","loc":[-85.640073,41.933968]},"n185963167":{"id":"n185963167","loc":[-85.641225,41.933972]},"n185963168":{"id":"n185963168","loc":[-85.642386,41.933952]},"n185964695":{"id":"n185964695","loc":[-85.6443608,41.9425645]},"n185964697":{"id":"n185964697","loc":[-85.644384,41.939941]},"n185964963":{"id":"n185964963","loc":[-85.6382347,41.9426146]},"n185964965":{"id":"n185964965","loc":[-85.637022,41.942622]},"n185964967":{"id":"n185964967","loc":[-85.6363706,41.9426606]},"n185964968":{"id":"n185964968","loc":[-85.6357988,41.9427748]},"n185964969":{"id":"n185964969","loc":[-85.6355409,41.9428465]},"n185964970":{"id":"n185964970","loc":[-85.6348729,41.9430443]},"n185966958":{"id":"n185966958","loc":[-85.641946,41.946413]},"n185966960":{"id":"n185966960","loc":[-85.643148,41.946389]},"n185967774":{"id":"n185967774","loc":[-85.641889,41.943852]},"n185967775":{"id":"n185967775","loc":[-85.641922,41.945121]},"n185967776":{"id":"n185967776","loc":[-85.641927,41.947544]},"n185967777":{"id":"n185967777","loc":[-85.641982,41.947622]},"n185969289":{"id":"n185969289","loc":[-85.63928,41.929221]},"n185969704":{"id":"n185969704","loc":[-85.6388186,41.9350099]},"n185969706":{"id":"n185969706","loc":[-85.6400709,41.9349957]},"n185969708":{"id":"n185969708","loc":[-85.6412214,41.9349827]},"n185969710":{"id":"n185969710","loc":[-85.6423509,41.934974]},"n185970602":{"id":"n185970602","loc":[-85.641293,41.931817]},"n185970604":{"id":"n185970604","loc":[-85.641258,41.932705]},"n185970605":{"id":"n185970605","loc":[-85.641148,41.936984]},"n185970606":{"id":"n185970606","loc":[-85.641112,41.938169]},"n185970906":{"id":"n185970906","loc":[-85.639454,41.943871]},"n185970908":{"id":"n185970908","loc":[-85.6394635,41.9450504]},"n185970909":{"id":"n185970909","loc":[-85.6394914,41.9451911]},"n185971368":{"id":"n185971368","loc":[-85.635769,41.940122]},"n185971978":{"id":"n185971978","loc":[-85.640003,41.936988]},"n185971980":{"id":"n185971980","loc":[-85.642299,41.936988]},"n185973633":{"id":"n185973633","loc":[-85.639023,41.92861]},"n185973635":{"id":"n185973635","loc":[-85.639153,41.928969]},"n185973637":{"id":"n185973637","loc":[-85.639213,41.929088]},"n185973639":{"id":"n185973639","loc":[-85.63935,41.929396]},"n185973641":{"id":"n185973641","loc":[-85.640143,41.931462]},"n185973644":{"id":"n185973644","loc":[-85.64019,41.931788]},"n185973646":{"id":"n185973646","loc":[-85.6401365,41.9327199]},"n185973648":{"id":"n185973648","loc":[-85.639983,41.938174]},"n185974477":{"id":"n185974477","loc":[-85.638206,41.941331]},"n185975928":{"id":"n185975928","loc":[-85.640683,41.94513]},"n185975930":{"id":"n185975930","loc":[-85.643102,41.945103]},"n185976255":{"id":"n185976255","loc":[-85.642424,41.931817]},"n185976257":{"id":"n185976257","loc":[-85.64242,41.932699]},"n185976258":{"id":"n185976258","loc":[-85.6422621,41.9381489]},"n185977452":{"id":"n185977452","loc":[-85.6457497,41.9398834]},"n185978772":{"id":"n185978772","loc":[-85.646656,41.939869]},"n185981472":{"id":"n185981472","loc":[-85.6388962,41.9321266]},"n185981474":{"id":"n185981474","loc":[-85.6388769,41.9327334]},"n185981476":{"id":"n185981476","loc":[-85.638829,41.934116]},"n185981478":{"id":"n185981478","loc":[-85.63876,41.937002]},"n185981480":{"id":"n185981480","loc":[-85.638682,41.93819]},"n185981999":{"id":"n185981999","loc":[-85.638194,41.9400866]},"n185982001":{"id":"n185982001","loc":[-85.646302,41.93988]},"n185982877":{"id":"n185982877","loc":[-85.640676,41.943867]},"n185982879":{"id":"n185982879","loc":[-85.640734,41.945887]},"n185985823":{"id":"n185985823","loc":[-85.643106,41.943841]},"n185985824":{"id":"n185985824","loc":[-85.643145,41.947641]},"n185985825":{"id":"n185985825","loc":[-85.643219,41.950829]},"n1475301385":{"id":"n1475301385","loc":[-85.6360612,41.9427042]},"n1475301397":{"id":"n1475301397","loc":[-85.6366651,41.9426328]},"n2139795811":{"id":"n2139795811","loc":[-85.6469154,41.9425427]},"n2139795830":{"id":"n2139795830","loc":[-85.6443194,41.9399444]},"n2139795834":{"id":"n2139795834","loc":[-85.6453506,41.9399002]},"n2139795837":{"id":"n2139795837","loc":[-85.645806,41.9398831]},"n2139858932":{"id":"n2139858932","loc":[-85.6351721,41.9429557]},"n2140019000":{"id":"n2140019000","loc":[-85.6359935,41.9427224]},"n2165942817":{"id":"n2165942817","loc":[-85.6442017,41.9414993]},"n2165942820":{"id":"n2165942820","loc":[-85.6442107,41.9421159]},"n2189152990":{"id":"n2189152990","loc":[-85.6442328,41.942404]},"n2189152991":{"id":"n2189152991","loc":[-85.6442309,41.9422229]},"n2189153002":{"id":"n2189153002","loc":[-85.6441329,41.9413147]},"n2189153003":{"id":"n2189153003","loc":[-85.6441357,41.9414925]},"n2189153023":{"id":"n2189153023","loc":[-85.6443453,41.9423074]},"n2189153024":{"id":"n2189153024","loc":[-85.6442318,41.9423045]},"n2189153039":{"id":"n2189153039","loc":[-85.6441343,41.9414025]},"w208643102":{"id":"w208643102","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153034","n2189153035","n2189153036","n2189153037","n2189153038"]},"w17966942":{"id":"w17966942","tags":{"highway":"residential","name":"Millard St"},"nodes":["n185954680","n185960207","n185971368","n185948706","n185958670","n185981999","n185981481","n185981482","n185973650","n185973659","n185970607","n185976259","n185976261","n2139795830","n185964697","n2139795834","n185977452","n2139795837","n185982001","n185978772"]},"w208643105":{"id":"w208643105","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153046","n2189153047"]},"w208631637":{"id":"w208631637","tags":{"area":"yes","building":"yes"},"nodes":["n2189046014","n2189046016","n2189046018","n2189046021","n2189046022","n2189046025","n2189046014"]},"w208643096":{"id":"w208643096","tags":{"amenity":"parking","area":"yes","fee":"no"},"nodes":["n2189152990","n2189153024","n2189152991","n2189152992","n2189153025","n2189152993","n2189152990"]},"w208631656":{"id":"w208631656","tags":{"area":"yes","building":"yes"},"nodes":["n2189046134","n2189046135","n2189046137","n2189046138","n2189046134"]},"w204003417":{"id":"w204003417","tags":{"area":"yes","building":"school"},"nodes":["n2140155811","n2140155814","n2140155816","n2140155818","n2140155821","n2140155823","n2140155825","n2140155827","n2140155811"]},"w208631654":{"id":"w208631654","tags":{"area":"yes","building":"yes"},"nodes":["n2189046127","n2189046128","n2189046130","n2189046131","n2189046132","n2189046133","n2189046127"]},"w17966327":{"id":"w17966327","tags":{"highway":"residential","name":"S Douglas Ave"},"nodes":["n185976261","n2140155839","n2140155834","n185974481","n2189153032","n185964959"]},"w41785752":{"id":"w41785752","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60","access":"yes"},"nodes":["n185954784","n2139795811","n185964695","n185964959","n185964960","n185964961","n185964962","n185964963","n185964965","n1475301397","n185964967","n1475301385","n2140019000","n185964968","n185964969","n2139858932","n185964970"]},"w203841842":{"id":"w203841842","tags":{"area":"yes","leisure":"playground"},"nodes":["n2138493848","n2138493849","n2138493850","n2138493851","n2138493852","n2138493853","n2138493854","n2138493855","n2138493856","n2138493848"]},"w208643103":{"id":"w208643103","tags":{"highway":"service"},"nodes":["n2189153039","n2189153040","n2189153041","n2189153042","n2189153043","n2189153047","n2189153045","n185974481"]},"w208643098":{"id":"w208643098","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153000","n2189153041","n2189153001","n2189153002","n2189153039","n2189153003","n2189153000"]},"w208631646":{"id":"w208631646","tags":{"area":"yes","building":"yes"},"nodes":["n2189046067","n2189046069","n2189046070","n2189046072","n2189046067"]},"w208631653":{"id":"w208631653","tags":{"area":"yes","building":"yes"},"nodes":["n2189046119","n2189046120","n2189046121","n2189046122","n2189046123","n2189046124","n2189046125","n2189046126","n2189046119"]},"w17966041":{"id":"w17966041","tags":{"highway":"residential","name":"S Lincoln Ave"},"nodes":["n185973659","n185973660","n185964961"]},"w208631645":{"id":"w208631645","tags":{"area":"yes","building":"yes"},"nodes":["n2189046060","n2189046061","n2189046063","n2189046065","n2189046060"]},"w206803397":{"id":"w206803397","tags":{"area":"yes","building":"yes"},"nodes":["n2168510551","n2168510552","n2168510553","n2168510554","n2168510555","n2168510556","n2168510557","n2168510558","n2168510551"]},"w17965792":{"id":"w17965792","tags":{"highway":"residential","name":"N Hooker Ave"},"nodes":["n185964962","n185970906","n185970908","n185970909"]},"w208631651":{"id":"w208631651","tags":{"area":"yes","building":"yes"},"nodes":["n2189046112","n2189046113","n2189046115","n2189046116","n2189046117","n2189046118","n2189046112"]},"w208631643":{"id":"w208631643","tags":{"area":"yes","building":"yes"},"nodes":["n2189046053","n2189046054","n2189046055","n2189046056","n2189046058","n2189046059","n2189046053"]},"w17966878":{"id":"w17966878","tags":{"highway":"residential","name":"S Hooker Ave"},"nodes":["n185981472","n185981474","n185963163","n185981476","n185969704","n185981478","n185981480","n185981481"]},"w17966102":{"id":"w17966102","tags":{"highway":"residential","name":"South St"},"nodes":["n185958672","n185974477","n185974479","n185973660","n185970614"]},"w208631660":{"id":"w208631660","tags":{"area":"yes","building":"yes"},"nodes":["n2189046145","n2189046146","n2189046147","n2189046148","n2189046149","n2189046150","n2189046152","n2189046153","n2189046145"]},"w208643101":{"id":"w208643101","tags":{"highway":"service"},"nodes":["n2189153023","n2189153024","n2189153025","n2189153026","n2189153038","n2189153027","n2189153028","n2189153029","n2189153033","n2189153009","n2189153030","n2189153034","n2189153031","n2189153032"]},"w204000205":{"id":"w204000205","tags":{"highway":"residential","name":"South St","oneway":"yes"},"nodes":["n185974481","n2140155851","n185970614"]},"w203841841":{"id":"w203841841","tags":{"area":"yes","leisure":"pitch","pitch":"basketball"},"nodes":["n2138493844","n2138493845","n2138493846","n2138493847","n2138493844"]},"w17965444":{"id":"w17965444","tags":{"highway":"residential","name":"N Grant Ave"},"nodes":["n185964960","n185967774","n185967775","n185966958","n185967776","n185967777"]},"w208631648":{"id":"w208631648","tags":{"area":"yes","building":"yes"},"nodes":["n2189046074","n2189046075","n2189046077","n2189046079","n2189046082","n2189046083","n2189046085","n2189046087","n2189046089","n2189046090","n2189046092","n2189046094","n2189046096","n2189046097","n2189046099","n2189046103","n2189046074"]},"w208643100":{"id":"w208643100","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153010","n2189153011","n2189153012","n2189153013","n2189153014","n2189153015","n2189153016","n2189153017","n2189153018","n2189153019","n2189153020","n2189153021","n2189153022","n2189153010"]},"w17965749":{"id":"w17965749","tags":{"highway":"residential","name":"S Grant Ave"},"nodes":["n185970614","n185970616","n185964960"]},"w206574482":{"id":"w206574482","tags":{"amenity":"library","area":"yes","building":"yes","name":"Three Rivers Public Library"},"nodes":["n2165942817","n2165942818","n2165942819","n2165942820","n2165942817"]},"w208643097":{"id":"w208643097","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189152994","n2189152995","n2189152996","n2189152997","n2189152998","n2189152999","n2189152994"]},"w17966879":{"id":"w17966879","tags":{"highway":"residential","name":"S Hooker Ave"},"nodes":["n185981482","n185974479","n185964962"]},"w17966325":{"id":"w17966325","tags":{"highway":"residential","name":"S Douglas Ave"},"nodes":["n185976255","n185976257","n185963168","n185969710","n185971980","n185976258","n185954700","n185976259"]},"w17967390":{"id":"w17967390","tags":{"highway":"residential","name":"N Douglas Ave"},"nodes":["n185964959","n185985823","n185975930","n185966960","n185985824","n185949870","n185985825"]},"w208631635":{"id":"w208631635","tags":{"area":"yes","building":"yes"},"nodes":["n2189046007","n2189046009","n2189046011","n2189046012","n2189046007"]},"w208643099":{"id":"w208643099","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153031","n2189153004","n2189153005","n2189153006","n2189153007","n2189153008","n2189153029","n2189153033","n2189153009","n2189153030","n2189153031"]},"w208631658":{"id":"w208631658","tags":{"area":"yes","building":"yes"},"nodes":["n2189046139","n2189046140","n2189046141","n2189046142","n2189046143","n2189046144","n2189046139"]},"w208643104":{"id":"w208643104","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153044","n2189153045"]},"w17966039":{"id":"w17966039","tags":{"highway":"residential","name":"S Lincoln Ave"},"nodes":["n185973633","n185973635","n185973637","n185969289","n185973639","n185949348","n185973641","n185973644","n185973646","n185963165","n185969706","n185971978","n185973648","n185973650"]},"w204003420":{"id":"w204003420","tags":{"amenity":"parking","area":"yes"},"nodes":["n2140155840","n2140155842","n2140155844","n2140155845","n2140155847","n2140155849","n2140155854","n2140155840"]},"w204003419":{"id":"w204003419","tags":{"highway":"service"},"nodes":["n2140155834","n2140155835","n2140155837","n2140155839"]},"w204003418":{"id":"w204003418","tags":{"amenity":"school","area":"yes","name":"Andrews Elementary School"},"nodes":["n2140155828","n2140155829","n2140155830","n2140155831","n2140155832","n2140155833","n2140155828"]},"w17965747":{"id":"w17965747","tags":{"highway":"residential","name":"S Grant Ave"},"nodes":["n185970602","n185970604","n185963167","n185969708","n185970605","n185970606","n185970607"]},"w17967073":{"id":"w17967073","tags":{"highway":"residential","name":"N Lincoln Ave"},"nodes":["n185964961","n185982877","n185975928","n185982879"]},"w204003421":{"id":"w204003421","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2140155851","n2140155852","n2140155854","n2140155856"]},"r1943857":{"id":"r1943857","tags":{"modifier":"Business","name":"US 131 Business (Three Rivers, MI)","network":"US:US","ref":"131","route":"road","type":"route"},"members":[{"id":"w17966509","type":"way","role":"forward"},{"id":"w143497377","type":"way","role":""},{"id":"w134150811","type":"way","role":""},{"id":"w134150800","type":"way","role":""},{"id":"w134150789","type":"way","role":""},{"id":"w134150795","type":"way","role":""},{"id":"w41785752","type":"way","role":""},{"id":"w17965146","type":"way","role":"forward"},{"id":"w17964031","type":"way","role":"forward"}]},"r270277":{"id":"r270277","tags":{"network":"US:MI","ref":"60","route":"road","state_id":"MI","type":"route","url":"http://en.wikipedia.org/wiki/M-60_%28Michigan_highway%29"},"members":[{"id":"w17751087","type":"way","role":"east"},{"id":"w117148312","type":"way","role":"east"},{"id":"w40942155","type":"way","role":"west"},{"id":"w17751017","type":"way","role":""},{"id":"w17751083","type":"way","role":""},{"id":"w17747780","type":"way","role":""},{"id":"w41068082","type":"way","role":""},{"id":"w197025212","type":"way","role":""},{"id":"w17743874","type":"way","role":""},{"id":"w17751044","type":"way","role":""},{"id":"w17752167","type":"way","role":""},{"id":"w17751089","type":"way","role":""},{"id":"w17743879","type":"way","role":""},{"id":"w17751064","type":"way","role":""},{"id":"w197057073","type":"way","role":""},{"id":"w167699963","type":"way","role":""},{"id":"w167699972","type":"way","role":""},{"id":"w17967584","type":"way","role":""},{"id":"w167699964","type":"way","role":""},{"id":"w17967582","type":"way","role":"west"},{"id":"w41260270","type":"way","role":"west"},{"id":"w17965146","type":"way","role":"west"},{"id":"w41785752","type":"way","role":""},{"id":"w134150795","type":"way","role":""},{"id":"w134150789","type":"way","role":""},{"id":"w134150800","type":"way","role":""},{"id":"w134150811","type":"way","role":""},{"id":"w134150836","type":"way","role":""},{"id":"w134150802","type":"way","role":""},{"id":"w41074896","type":"way","role":""},{"id":"w17966773","type":"way","role":""},{"id":"w17967415","type":"way","role":""},{"id":"w41074899","type":"way","role":""},{"id":"w17967581","type":"way","role":""},{"id":"w41074902","type":"way","role":""},{"id":"w41074906","type":"way","role":""},{"id":"w209707997","type":"way","role":""},{"id":"w209707998","type":"way","role":""},{"id":"w17964798","type":"way","role":""},{"id":"w17966034","type":"way","role":""},{"id":"w17967593","type":"way","role":""},{"id":"w41074888","type":"way","role":""},{"id":"w17733772","type":"way","role":""},{"id":"w41074813","type":"way","role":""},{"id":"w17742213","type":"way","role":""},{"id":"w17746863","type":"way","role":""},{"id":"w17745772","type":"way","role":""},{"id":"w17742222","type":"way","role":""},{"id":"w17745922","type":"way","role":""},{"id":"w17742198","type":"way","role":""},{"id":"w17747675","type":"way","role":""},{"id":"w17739927","type":"way","role":""},{"id":"w17745708","type":"way","role":""},{"id":"w41006323","type":"way","role":""},{"id":"w17744233","type":"way","role":""},{"id":"w17739436","type":"way","role":""},{"id":"w17742201","type":"way","role":""},{"id":"w151418616","type":"way","role":""},{"id":"w17750062","type":"way","role":""},{"id":"w17742227","type":"way","role":"east"},{"id":"w41006348","type":"way","role":"east"},{"id":"w41260984","type":"way","role":""},{"id":"w17832427","type":"way","role":""},{"id":"w17838408","type":"way","role":""},{"id":"w17835846","type":"way","role":""},{"id":"w17832923","type":"way","role":""},{"id":"w17839388","type":"way","role":""},{"id":"w17838390","type":"way","role":""},{"id":"w17831272","type":"way","role":""},{"id":"w17828581","type":"way","role":""},{"id":"w38240686","type":"way","role":""},{"id":"w17838405","type":"way","role":"east"},{"id":"w123323711","type":"way","role":"east"},{"id":"w17830167","type":"way","role":"east"},{"id":"w99011909","type":"way","role":"east"},{"id":"w41911361","type":"way","role":"east"},{"id":"w41911355","type":"way","role":"east"},{"id":"w41911356","type":"way","role":"east"},{"id":"w117148326","type":"way","role":"west"},{"id":"w41911352","type":"way","role":"west"},{"id":"w41911353","type":"way","role":"west"},{"id":"w41911354","type":"way","role":"west"},{"id":"w41911360","type":"way","role":"west"},{"id":"w38240676","type":"way","role":"west"},{"id":"w123323710","type":"way","role":"west"},{"id":"w41260271","type":"way","role":"east"},{"id":"w41260273","type":"way","role":"east"},{"id":"w17964031","type":"way","role":"east"},{"id":"w41006344","type":"way","role":"west"},{"id":"w41006351","type":"way","role":"west"}]},"n367813436":{"id":"n367813436","loc":[-85.63605205663384,41.94305506683346],"tags":{"amenity":"fire_station","name":"Three Rivers Fire Department"}},"n185948708":{"id":"n185948708","loc":[-85.6369828,41.9408789]},"n185948710":{"id":"n185948710","loc":[-85.6370184,41.9411346]},"n185954691":{"id":"n185954691","loc":[-85.634476,41.941475]},"n185954692":{"id":"n185954692","loc":[-85.635008,41.941846]},"n185954693":{"id":"n185954693","loc":[-85.635362,41.941962]},"n185954695":{"id":"n185954695","loc":[-85.63578,41.941978]},"n185972903":{"id":"n185972903","loc":[-85.63295,41.9430062]},"n185964971":{"id":"n185964971","loc":[-85.6346811,41.9431023]},"n1819805854":{"id":"n1819805854","loc":[-85.6331275,41.9404837]},"n1819805918":{"id":"n1819805918","loc":[-85.6331168,41.942798]},"n1819805762":{"id":"n1819805762","loc":[-85.6333034,41.9424123]},"n1819805907":{"id":"n1819805907","loc":[-85.6334819,41.9419121]},"n1819805915":{"id":"n1819805915","loc":[-85.6334554,41.9413588]},"n1819848888":{"id":"n1819848888","loc":[-85.6331625,41.942679]},"n1819848930":{"id":"n1819848930","loc":[-85.6338684,41.9431252]},"n1819858505":{"id":"n1819858505","loc":[-85.6346782,41.9429092]},"n1819858507":{"id":"n1819858507","loc":[-85.6339003,41.9414534]},"n1819858508":{"id":"n1819858508","loc":[-85.6345709,41.9427742]},"n1819858509":{"id":"n1819858509","loc":[-85.63419,41.9417322]},"n1819858511":{"id":"n1819858511","loc":[-85.6340666,41.9415652]},"n1819858512":{"id":"n1819858512","loc":[-85.6343295,41.9423027]},"n1819858514":{"id":"n1819858514","loc":[-85.6343241,41.942207]},"n1819858521":{"id":"n1819858521","loc":[-85.633391,41.941231]},"n1819858528":{"id":"n1819858528","loc":[-85.6343027,41.9419716]},"n185954683":{"id":"n185954683","loc":[-85.6335412,41.940147]},"n185954685":{"id":"n185954685","loc":[-85.6334296,41.9403023]},"n185954687":{"id":"n185954687","loc":[-85.6333988,41.9404704]},"n185954689":{"id":"n185954689","loc":[-85.6335511,41.9410225]},"n185954690":{"id":"n185954690","loc":[-85.6336721,41.9411669]},"n1820938802":{"id":"n1820938802","loc":[-85.6330671,41.941845]},"n1821006702":{"id":"n1821006702","loc":[-85.6344047,41.9395496]},"n2130304133":{"id":"n2130304133","loc":[-85.6349025,41.9427659]},"n2130304136":{"id":"n2130304136","loc":[-85.6346027,41.9422017]},"n2130304138":{"id":"n2130304138","loc":[-85.6348577,41.9421517]},"n2130304140":{"id":"n2130304140","loc":[-85.6348419,41.9422694]},"n2130304142":{"id":"n2130304142","loc":[-85.6349071,41.9423135]},"n2130304144":{"id":"n2130304144","loc":[-85.6350495,41.9423312]},"n2130304146":{"id":"n2130304146","loc":[-85.6351009,41.9422812]},"n2130304147":{"id":"n2130304147","loc":[-85.6351227,41.9421532]},"n2130304148":{"id":"n2130304148","loc":[-85.635526,41.9421547]},"n2130304149":{"id":"n2130304149","loc":[-85.6355339,41.9425768]},"n2130304150":{"id":"n2130304150","loc":[-85.6351582,41.9426562]},"n2130304151":{"id":"n2130304151","loc":[-85.6351207,41.9427032]},"n2138493807":{"id":"n2138493807","loc":[-85.6350923,41.9415216]},"n2138493808":{"id":"n2138493808","loc":[-85.6353603,41.9411061]},"n2138493809":{"id":"n2138493809","loc":[-85.6354421,41.9410942]},"n2138493810":{"id":"n2138493810","loc":[-85.6355079,41.9411044]},"n2138493811":{"id":"n2138493811","loc":[-85.6355693,41.9411246]},"n2138493812":{"id":"n2138493812","loc":[-85.6355829,41.9411061]},"n2138493813":{"id":"n2138493813","loc":[-85.6355624,41.9409777]},"n2138493814":{"id":"n2138493814","loc":[-85.6355011,41.9409152]},"n2138493815":{"id":"n2138493815","loc":[-85.635383,41.9409219]},"n2138493816":{"id":"n2138493816","loc":[-85.635299,41.9409658]},"n2138493817":{"id":"n2138493817","loc":[-85.6351695,41.941204]},"n2138493818":{"id":"n2138493818","loc":[-85.6348879,41.9415166]},"n2138493819":{"id":"n2138493819","loc":[-85.634897,41.9415757]},"n2138493820":{"id":"n2138493820","loc":[-85.6349606,41.9416399]},"n2138493821":{"id":"n2138493821","loc":[-85.6350219,41.9416669]},"n2138493822":{"id":"n2138493822","loc":[-85.6351241,41.9416314]},"n2138493823":{"id":"n2138493823","loc":[-85.6350855,41.9415622]},"n2138493824":{"id":"n2138493824","loc":[-85.6350401,41.9413603]},"n2138493825":{"id":"n2138493825","loc":[-85.6352206,41.9410765]},"n2138493826":{"id":"n2138493826","loc":[-85.6343865,41.9415594]},"n2138493827":{"id":"n2138493827","loc":[-85.6343506,41.9415873]},"n2138493828":{"id":"n2138493828","loc":[-85.6344158,41.9417557]},"n2138493829":{"id":"n2138493829","loc":[-85.6344614,41.9417968]},"n2138493830":{"id":"n2138493830","loc":[-85.6345005,41.9418186]},"n2138493831":{"id":"n2138493831","loc":[-85.6345965,41.9418162]},"n2138493832":{"id":"n2138493832","loc":[-85.6347317,41.9417242]},"n2138493833":{"id":"n2138493833","loc":[-85.6346722,41.941775]},"n2139858909":{"id":"n2139858909","loc":[-85.633403,41.9391006]},"n2139858910":{"id":"n2139858910","loc":[-85.6332973,41.9393967]},"n2139858911":{"id":"n2139858911","loc":[-85.633205,41.9396742]},"n2139858912":{"id":"n2139858912","loc":[-85.6332203,41.9397772]},"n2139858913":{"id":"n2139858913","loc":[-85.6333453,41.939936]},"n2139858914":{"id":"n2139858914","loc":[-85.6333761,41.9400018]},"n2139858915":{"id":"n2139858915","loc":[-85.63328,41.9402249]},"n2139858916":{"id":"n2139858916","loc":[-85.6332357,41.9403523]},"n2139858917":{"id":"n2139858917","loc":[-85.6332838,41.9405831]},"n2139858918":{"id":"n2139858918","loc":[-85.6333643,41.9408744]},"n2139858919":{"id":"n2139858919","loc":[-85.6334394,41.9410519]},"n2139858920":{"id":"n2139858920","loc":[-85.6335815,41.9411717]},"n2139858921":{"id":"n2139858921","loc":[-85.6337478,41.9412734]},"n2139858922":{"id":"n2139858922","loc":[-85.6343174,41.9415268]},"n2139858923":{"id":"n2139858923","loc":[-85.6343886,41.9417397]},"n2139858924":{"id":"n2139858924","loc":[-85.6344407,41.9418015]},"n2139858925":{"id":"n2139858925","loc":[-85.6345139,41.9418366]},"n2139858926":{"id":"n2139858926","loc":[-85.6344846,41.942005]},"n2139858927":{"id":"n2139858927","loc":[-85.6345775,41.9422218]},"n2139858928":{"id":"n2139858928","loc":[-85.6348771,41.9427814]},"n2139858929":{"id":"n2139858929","loc":[-85.6349487,41.9427995]},"n2139858930":{"id":"n2139858930","loc":[-85.6350415,41.9427874]},"n2139858931":{"id":"n2139858931","loc":[-85.6351246,41.9428589]},"n2139858978":{"id":"n2139858978","loc":[-85.6349658,41.9431481]},"n2139858979":{"id":"n2139858979","loc":[-85.6350081,41.9431287]},"n2139858980":{"id":"n2139858980","loc":[-85.6349967,41.9430997]},"n2139858981":{"id":"n2139858981","loc":[-85.6352158,41.9430352]},"n2139858982":{"id":"n2139858982","loc":[-85.6348174,41.94267]},"n2139858983":{"id":"n2139858983","loc":[-85.6346142,41.9425989]},"n2139858984":{"id":"n2139858984","loc":[-85.6344938,41.9423809]},"n2139858985":{"id":"n2139858985","loc":[-85.6344856,41.9422997]},"n2139870380":{"id":"n2139870380","loc":[-85.6346707,41.9417955]},"n2139870381":{"id":"n2139870381","loc":[-85.6345949,41.9418311]},"n2139870382":{"id":"n2139870382","loc":[-85.6343322,41.9418659]},"n2139870383":{"id":"n2139870383","loc":[-85.6342072,41.941885]},"n2139870384":{"id":"n2139870384","loc":[-85.6341325,41.9418919]},"n2139870385":{"id":"n2139870385","loc":[-85.6341314,41.9422028]},"n2139870386":{"id":"n2139870386","loc":[-85.6340472,41.9423271]},"n2139870387":{"id":"n2139870387","loc":[-85.6342185,41.9427933]},"n2139870388":{"id":"n2139870388","loc":[-85.6340605,41.9423924]},"n2139870389":{"id":"n2139870389","loc":[-85.6339889,41.9424069]},"n2139870390":{"id":"n2139870390","loc":[-85.633971,41.942356]},"n2139870391":{"id":"n2139870391","loc":[-85.63361,41.9424235]},"n2139870392":{"id":"n2139870392","loc":[-85.6337137,41.9426819]},"n2139870393":{"id":"n2139870393","loc":[-85.6336977,41.9428632]},"n2139870394":{"id":"n2139870394","loc":[-85.6338823,41.9428647]},"n2139870395":{"id":"n2139870395","loc":[-85.6339412,41.9430069]},"n2139870396":{"id":"n2139870396","loc":[-85.6338873,41.9430353]},"n2139870397":{"id":"n2139870397","loc":[-85.6337676,41.942815]},"n2139870398":{"id":"n2139870398","loc":[-85.6336822,41.9423505]},"n2139870399":{"id":"n2139870399","loc":[-85.634037,41.9422725]},"n2139870400":{"id":"n2139870400","loc":[-85.6340294,41.9422518]},"n2139870401":{"id":"n2139870401","loc":[-85.6336726,41.9423312]},"n2139870402":{"id":"n2139870402","loc":[-85.6342188,41.9425715]},"n2139870403":{"id":"n2139870403","loc":[-85.6342524,41.942565]},"n2139870404":{"id":"n2139870404","loc":[-85.6341438,41.942299]},"n2139870405":{"id":"n2139870405","loc":[-85.6341149,41.9423061]},"n2139870407":{"id":"n2139870407","loc":[-85.6340846,41.9431458]},"n2139870408":{"id":"n2139870408","loc":[-85.6339436,41.9429032]},"n2139870409":{"id":"n2139870409","loc":[-85.6343143,41.9428207]},"n2139870410":{"id":"n2139870410","loc":[-85.6343507,41.94277]},"n2139870411":{"id":"n2139870411","loc":[-85.6341527,41.942254]},"n2139870412":{"id":"n2139870412","loc":[-85.6340925,41.9422199]},"n2139870413":{"id":"n2139870413","loc":[-85.6335435,41.9423433]},"n2139870414":{"id":"n2139870414","loc":[-85.6335023,41.9423975]},"n2139870415":{"id":"n2139870415","loc":[-85.6335086,41.9424552]},"n2139870416":{"id":"n2139870416","loc":[-85.6336296,41.942665]},"n2139870417":{"id":"n2139870417","loc":[-85.6341396,41.9428596]},"n2139870418":{"id":"n2139870418","loc":[-85.6339701,41.9424487]},"n2139870419":{"id":"n2139870419","loc":[-85.6335514,41.9425294]},"n2139870420":{"id":"n2139870420","loc":[-85.6337406,41.9424929]},"n2139870421":{"id":"n2139870421","loc":[-85.6338939,41.9428687]},"n2139870422":{"id":"n2139870422","loc":[-85.6341323,41.9419538]},"n2139870423":{"id":"n2139870423","loc":[-85.6340321,41.9420376]},"n2139870424":{"id":"n2139870424","loc":[-85.6337648,41.942238]},"n2139870425":{"id":"n2139870425","loc":[-85.6337604,41.9422685]},"n2139870426":{"id":"n2139870426","loc":[-85.6337682,41.9422928]},"n2139870427":{"id":"n2139870427","loc":[-85.6338086,41.9423862]},"n2139870428":{"id":"n2139870428","loc":[-85.6349465,41.9416631]},"n2139870429":{"id":"n2139870429","loc":[-85.6351097,41.9416973]},"n2139870430":{"id":"n2139870430","loc":[-85.6353371,41.9416798]},"n2139870431":{"id":"n2139870431","loc":[-85.6349627,41.9422506]},"n2139870432":{"id":"n2139870432","loc":[-85.634979,41.9421815]},"n2139870433":{"id":"n2139870433","loc":[-85.634885,41.9421679]},"n2139870434":{"id":"n2139870434","loc":[-85.6348689,41.9422377]},"n2139870435":{"id":"n2139870435","loc":[-85.6349779,41.9419486]},"n2139870436":{"id":"n2139870436","loc":[-85.6349505,41.9418933]},"n2139870437":{"id":"n2139870437","loc":[-85.6347327,41.9419505]},"n2139870438":{"id":"n2139870438","loc":[-85.6347614,41.9420087]},"n2139870439":{"id":"n2139870439","loc":[-85.6351889,41.9416912]},"n2139870440":{"id":"n2139870440","loc":[-85.6351092,41.9418426]},"n2139870441":{"id":"n2139870441","loc":[-85.635086,41.9419659]},"n2139870442":{"id":"n2139870442","loc":[-85.6350584,41.9421466]},"n2139870443":{"id":"n2139870443","loc":[-85.6350993,41.9421606]},"n2139870444":{"id":"n2139870444","loc":[-85.6350993,41.9422132]},"n2139870445":{"id":"n2139870445","loc":[-85.6350794,41.9422855]},"n2139870446":{"id":"n2139870446","loc":[-85.6350474,41.9423159]},"n2139870447":{"id":"n2139870447","loc":[-85.6349251,41.9422998]},"n2139870448":{"id":"n2139870448","loc":[-85.634911,41.9422755]},"n2139870449":{"id":"n2139870449","loc":[-85.6349157,41.9422553]},"n2139870450":{"id":"n2139870450","loc":[-85.6347213,41.9419324]},"n2139870451":{"id":"n2139870451","loc":[-85.6349535,41.9418771]},"n2139870452":{"id":"n2139870452","loc":[-85.6350135,41.9419421]},"n2139870453":{"id":"n2139870453","loc":[-85.6348584,41.9418997]},"n2139870454":{"id":"n2139870454","loc":[-85.6348113,41.9418101]},"n2139870455":{"id":"n2139870455","loc":[-85.6347306,41.9417449]},"n2139870456":{"id":"n2139870456","loc":[-85.6349123,41.941776]},"n2139870457":{"id":"n2139870457","loc":[-85.6349423,41.9421448]},"n2139870458":{"id":"n2139870458","loc":[-85.6349436,41.9420652]},"n2139870459":{"id":"n2139870459","loc":[-85.6349136,41.9419963]},"n2139870460":{"id":"n2139870460","loc":[-85.6349814,41.9419789]},"n2139989328":{"id":"n2139989328","loc":[-85.6334188,41.9421725]},"n2139989330":{"id":"n2139989330","loc":[-85.6335087,41.9416308]},"n2139989335":{"id":"n2139989335","loc":[-85.6336856,41.9429371]},"n2139989337":{"id":"n2139989337","loc":[-85.6333713,41.9427217]},"n2139989339":{"id":"n2139989339","loc":[-85.6332912,41.9425383]},"n2139989341":{"id":"n2139989341","loc":[-85.6339369,41.9409198]},"n2139989344":{"id":"n2139989344","loc":[-85.634097,41.9409469]},"n2139989346":{"id":"n2139989346","loc":[-85.634137,41.9412852]},"n2139989348":{"id":"n2139989348","loc":[-85.6344536,41.9414151]},"n2139989350":{"id":"n2139989350","loc":[-85.6350794,41.9412392]},"n2139989351":{"id":"n2139989351","loc":[-85.6352541,41.9409387]},"n2139989353":{"id":"n2139989353","loc":[-85.6357198,41.9408007]},"n2139989355":{"id":"n2139989355","loc":[-85.6357235,41.9427088]},"n2139989357":{"id":"n2139989357","loc":[-85.6337119,41.9421256]},"n2139989359":{"id":"n2139989359","loc":[-85.6336913,41.9420655]},"n2139989360":{"id":"n2139989360","loc":[-85.633582,41.9420867]},"n2139989362":{"id":"n2139989362","loc":[-85.6336058,41.9421491]},"n2139989364":{"id":"n2139989364","loc":[-85.6339685,41.9410995]},"n2139989366":{"id":"n2139989366","loc":[-85.6339067,41.9411383]},"n2139989368":{"id":"n2139989368","loc":[-85.6339685,41.9411972]},"n2139989370":{"id":"n2139989370","loc":[-85.6340398,41.9411619]},"n2139870379":{"id":"n2139870379","loc":[-85.6348391,41.9416651]},"n2140006363":{"id":"n2140006363","loc":[-85.6353144,41.9430345]},"n2140006364":{"id":"n2140006364","loc":[-85.6349191,41.9431422]},"n2140018997":{"id":"n2140018997","loc":[-85.63645945147184,41.942986488012565],"tags":{"amenity":"townhall","name":"Three Rivers City Hall"}},"n2140018998":{"id":"n2140018998","loc":[-85.6370319,41.9427919]},"n2140018999":{"id":"n2140018999","loc":[-85.6360687,41.9427808]},"n2199856288":{"id":"n2199856288","loc":[-85.6344968,41.9407307]},"n2199856289":{"id":"n2199856289","loc":[-85.634492,41.9406036]},"n2199856290":{"id":"n2199856290","loc":[-85.634891,41.9406001]},"n2199856291":{"id":"n2199856291","loc":[-85.6348894,41.9405288]},"n2199856292":{"id":"n2199856292","loc":[-85.6349166,41.94053]},"n2199856293":{"id":"n2199856293","loc":[-85.6349166,41.9404956]},"n2199856294":{"id":"n2199856294","loc":[-85.6350219,41.9404956]},"n2199856295":{"id":"n2199856295","loc":[-85.6350251,41.94053]},"n2199856296":{"id":"n2199856296","loc":[-85.6350538,41.9405288]},"n2199856297":{"id":"n2199856297","loc":[-85.6350602,41.94079]},"n2199856298":{"id":"n2199856298","loc":[-85.6351703,41.9407912]},"n2199856299":{"id":"n2199856299","loc":[-85.6351688,41.9409171]},"n2199856300":{"id":"n2199856300","loc":[-85.6347889,41.9409135]},"n2199856301":{"id":"n2199856301","loc":[-85.6347921,41.94079]},"n2199856302":{"id":"n2199856302","loc":[-85.6348942,41.9407888]},"n2199856303":{"id":"n2199856303","loc":[-85.6348926,41.9407283]},"n185951869":{"id":"n185951869","loc":[-85.6387639,41.957288]},"n185958643":{"id":"n185958643","loc":[-85.636746,41.929221]},"n185958645":{"id":"n185958645","loc":[-85.636791,41.929363]},"n185958647":{"id":"n185958647","loc":[-85.6375975,41.9314987]},"n185958649":{"id":"n185958649","loc":[-85.637669,41.931667]},"n185958651":{"id":"n185958651","loc":[-85.637728,41.931901]},"n185958653":{"id":"n185958653","loc":[-85.637724,41.932187]},"n185958656":{"id":"n185958656","loc":[-85.637732,41.932761]},"n185958658":{"id":"n185958658","loc":[-85.637688,41.93398]},"n185958660":{"id":"n185958660","loc":[-85.637685,41.934223]},"n185958662":{"id":"n185958662","loc":[-85.6376468,41.9350232]},"n185958664":{"id":"n185958664","loc":[-85.637564,41.937028]},"n185958666":{"id":"n185958666","loc":[-85.637458,41.938197]},"n185958668":{"id":"n185958668","loc":[-85.637424,41.938692]},"n185964972":{"id":"n185964972","loc":[-85.6341901,41.9432732]},"n185971361":{"id":"n185971361","loc":[-85.635762,41.938208]},"n185971364":{"id":"n185971364","loc":[-85.635732,41.9384]},"n185971366":{"id":"n185971366","loc":[-85.635736,41.938697]},"n185972775":{"id":"n185972775","loc":[-85.635638,42.070357]},"n185972777":{"id":"n185972777","loc":[-85.635724,42.069929]},"n185972779":{"id":"n185972779","loc":[-85.635804,42.069248]},"n185972781":{"id":"n185972781","loc":[-85.635869,42.068361]},"n185972783":{"id":"n185972783","loc":[-85.635883,42.067582]},"n185972785":{"id":"n185972785","loc":[-85.635875,42.067114]},"n185972787":{"id":"n185972787","loc":[-85.635778,42.065359]},"n185972788":{"id":"n185972788","loc":[-85.635728,42.063416]},"n185972789":{"id":"n185972789","loc":[-85.635665,42.062491]},"n185972790":{"id":"n185972790","loc":[-85.635617,42.061928]},"n185972791":{"id":"n185972791","loc":[-85.635614,42.061898]},"n185972793":{"id":"n185972793","loc":[-85.635379,42.060288]},"n185972795":{"id":"n185972795","loc":[-85.635092,42.05799]},"n185972797":{"id":"n185972797","loc":[-85.634843,42.055781]},"n185972798":{"id":"n185972798","loc":[-85.634817,42.055549]},"n185972800":{"id":"n185972800","loc":[-85.634708,42.053942]},"n185972802":{"id":"n185972802","loc":[-85.634447,42.051809]},"n185972805":{"id":"n185972805","loc":[-85.634241,42.04946]},"n185972807":{"id":"n185972807","loc":[-85.633787,42.045926]},"n185972809":{"id":"n185972809","loc":[-85.633811,42.045645]},"n185972811":{"id":"n185972811","loc":[-85.63373,42.043626]},"n185972813":{"id":"n185972813","loc":[-85.633698,42.042184]},"n185972814":{"id":"n185972814","loc":[-85.63369,42.04181]},"n185972815":{"id":"n185972815","loc":[-85.633681,42.040714]},"n185972816":{"id":"n185972816","loc":[-85.633571,42.036322]},"n185972817":{"id":"n185972817","loc":[-85.633537,42.034044]},"n185972819":{"id":"n185972819","loc":[-85.633481,42.030785]},"n185972821":{"id":"n185972821","loc":[-85.633452,42.027538]},"n185972824":{"id":"n185972824","loc":[-85.633438,42.027427]},"n185972826":{"id":"n185972826","loc":[-85.633342,42.022656]},"n185972830":{"id":"n185972830","loc":[-85.63327,42.020724]},"n185972832":{"id":"n185972832","loc":[-85.633198,42.019106]},"n185972834":{"id":"n185972834","loc":[-85.633249,42.018363]},"n185972835":{"id":"n185972835","loc":[-85.633139,42.012944]},"n185972836":{"id":"n185972836","loc":[-85.63309,42.008284]},"n185972839":{"id":"n185972839","loc":[-85.63298,42.00005]},"n185972845":{"id":"n185972845","loc":[-85.6325369,41.9764959]},"n185972847":{"id":"n185972847","loc":[-85.6327549,41.9750005]},"n185972849":{"id":"n185972849","loc":[-85.6329374,41.9742527]},"n185972851":{"id":"n185972851","loc":[-85.6331387,41.9736039]},"n185972862":{"id":"n185972862","loc":[-85.6383589,41.9585023]},"n185972868":{"id":"n185972868","loc":[-85.6393633,41.9551716]},"n185972878":{"id":"n185972878","loc":[-85.639377,41.95335]},"n185972882":{"id":"n185972882","loc":[-85.6389179,41.9516944]},"n185972885":{"id":"n185972885","loc":[-85.6387444,41.9512105]},"n185972891":{"id":"n185972891","loc":[-85.636421,41.946392]},"n185972895":{"id":"n185972895","loc":[-85.635965,41.945809]},"n185972897":{"id":"n185972897","loc":[-85.635683,41.945449]},"n185972899":{"id":"n185972899","loc":[-85.635281,41.9450252]},"n185972905":{"id":"n185972905","loc":[-85.6324428,41.9425743]},"n185985217":{"id":"n185985217","loc":[-85.638243,41.943674]},"n185985219":{"id":"n185985219","loc":[-85.638228,41.943747]},"n185985221":{"id":"n185985221","loc":[-85.638163,41.943797]},"n185985222":{"id":"n185985222","loc":[-85.638089,41.943832]},"n185985223":{"id":"n185985223","loc":[-85.637969,41.943841]},"n185985225":{"id":"n185985225","loc":[-85.637841,41.943833]},"n185985227":{"id":"n185985227","loc":[-85.637601,41.943789]},"n185985229":{"id":"n185985229","loc":[-85.637449,41.943754]},"n185985231":{"id":"n185985231","loc":[-85.637342,41.943734]},"n185985233":{"id":"n185985233","loc":[-85.637218,41.943703]},"n185985235":{"id":"n185985235","loc":[-85.637151,41.943663]},"n185985238":{"id":"n185985238","loc":[-85.637118,41.943615]},"n185985240":{"id":"n185985240","loc":[-85.637073,41.943494]},"n185990434":{"id":"n185990434","loc":[-85.6329028,41.9984292],"tags":{"railway":"level_crossing"}},"n1475284023":{"id":"n1475284023","loc":[-85.6336163,41.9435806],"tags":{"railway":"level_crossing"}},"n1475293222":{"id":"n1475293222","loc":[-85.6394045,41.953658],"tags":{"railway":"level_crossing"}},"n1475293226":{"id":"n1475293226","loc":[-85.6364975,41.9638663],"tags":{"railway":"level_crossing"}},"n1475293234":{"id":"n1475293234","loc":[-85.6390449,41.9565145]},"n1475293240":{"id":"n1475293240","loc":[-85.636943,41.9473114]},"n1475293252":{"id":"n1475293252","loc":[-85.6392115,41.9559003]},"n1475293254":{"id":"n1475293254","loc":[-85.6348931,41.9685127],"tags":{"railway":"level_crossing"}},"n1475293260":{"id":"n1475293260","loc":[-85.6375999,41.9485401]},"n1475293261":{"id":"n1475293261","loc":[-85.6391256,41.9523817],"tags":{"railway":"level_crossing"}},"n1475293264":{"id":"n1475293264","loc":[-85.6394155,41.9546493],"tags":{"railway":"level_crossing"}},"n1819805614":{"id":"n1819805614","loc":[-85.6345652,41.9363097]},"n1819805618":{"id":"n1819805618","loc":[-85.6295334,41.9426862]},"n1819805622":{"id":"n1819805622","loc":[-85.6308208,41.9430773]},"n1819805626":{"id":"n1819805626","loc":[-85.6274734,41.9406592]},"n1819805629":{"id":"n1819805629","loc":[-85.6296943,41.9430533]},"n1819805632":{"id":"n1819805632","loc":[-85.6340931,41.9354477]},"n1819805636":{"id":"n1819805636","loc":[-85.6304131,41.9436598]},"n1819805639":{"id":"n1819805639","loc":[-85.6304882,41.9426623]},"n1819805641":{"id":"n1819805641","loc":[-85.6336103,41.9367487]},"n1819805643":{"id":"n1819805643","loc":[-85.6300376,41.9418084]},"n1819805645":{"id":"n1819805645","loc":[-85.6365286,41.9336679]},"n1819805647":{"id":"n1819805647","loc":[-85.632016,41.9429221]},"n1819805666":{"id":"n1819805666","loc":[-85.6314753,41.9442663]},"n1819805669":{"id":"n1819805669","loc":[-85.6268619,41.9402203]},"n1819805673":{"id":"n1819805673","loc":[-85.6296728,41.9412099]},"n1819805676":{"id":"n1819805676","loc":[-85.6354557,41.932766]},"n1819805680":{"id":"n1819805680","loc":[-85.632752,41.9431012]},"n1819805683":{"id":"n1819805683","loc":[-85.631147,41.9432014]},"n1819805687":{"id":"n1819805687","loc":[-85.635284,41.9343942]},"n1819805690":{"id":"n1819805690","loc":[-85.6249736,41.9405794]},"n1819805694":{"id":"n1819805694","loc":[-85.6294153,41.9417925]},"n1819805698":{"id":"n1819805698","loc":[-85.6323486,41.9426986]},"n1819805702":{"id":"n1819805702","loc":[-85.6340287,41.9373871]},"n1819805707":{"id":"n1819805707","loc":[-85.6353698,41.9316326]},"n1819805711":{"id":"n1819805711","loc":[-85.6284176,41.940356]},"n1819805715":{"id":"n1819805715","loc":[-85.6291471,41.9412897]},"n1819805718":{"id":"n1819805718","loc":[-85.6311105,41.943979]},"n1819805722":{"id":"n1819805722","loc":[-85.6320868,41.9400128]},"n1819805724":{"id":"n1819805724","loc":[-85.635166,41.9324627]},"n1819805727":{"id":"n1819805727","loc":[-85.6344686,41.9350567]},"n1819805728":{"id":"n1819805728","loc":[-85.6357132,41.9332369]},"n1819805731":{"id":"n1819805731","loc":[-85.629984,41.9434444]},"n1819805760":{"id":"n1819805760","loc":[-85.6330996,41.9378784]},"n1819805766":{"id":"n1819805766","loc":[-85.625274,41.9411141]},"n1819805770":{"id":"n1819805770","loc":[-85.6326321,41.9412173]},"n1819805774":{"id":"n1819805774","loc":[-85.6347047,41.9312096]},"n1819805777":{"id":"n1819805777","loc":[-85.6363569,41.9339552]},"n1819805780":{"id":"n1819805780","loc":[-85.6327392,41.941926]},"n1819805783":{"id":"n1819805783","loc":[-85.6357239,41.9338435]},"n1819805786":{"id":"n1819805786","loc":[-85.6356595,41.9346576]},"n1819805789":{"id":"n1819805789","loc":[-85.6316469,41.9436598]},"n1819805792":{"id":"n1819805792","loc":[-85.6350587,41.9354557]},"n1819805795":{"id":"n1819805795","loc":[-85.6360028,41.9322791]},"n1819805798":{"id":"n1819805798","loc":[-85.63125,41.9443062]},"n1819805802":{"id":"n1819805802","loc":[-85.6263362,41.9408109]},"n1819805805":{"id":"n1819805805","loc":[-85.6315075,41.9438753]},"n1819805808":{"id":"n1819805808","loc":[-85.6340008,41.9316051]},"n1819805810":{"id":"n1819805810","loc":[-85.6345545,41.9320557]},"n1819805812":{"id":"n1819805812","loc":[-85.6250809,41.9408587]},"n1819805814":{"id":"n1819805814","loc":[-85.6257783,41.9400926]},"n1819805834":{"id":"n1819805834","loc":[-85.6326408,41.9424363]},"n1819805838":{"id":"n1819805838","loc":[-85.6365607,41.9334365]},"n1819805842":{"id":"n1819805842","loc":[-85.6288253,41.9410343]},"n1819805846":{"id":"n1819805846","loc":[-85.6279133,41.9402921]},"n1819805849":{"id":"n1819805849","loc":[-85.6289433,41.9405156]},"n1819805852":{"id":"n1819805852","loc":[-85.6313787,41.9439152]},"n1819805858":{"id":"n1819805858","loc":[-85.6300805,41.9420398]},"n1819805861":{"id":"n1819805861","loc":[-85.6321941,41.9396297]},"n1819805864":{"id":"n1819805864","loc":[-85.6329129,41.9393903]},"n1819805868":{"id":"n1819805868","loc":[-85.632001,41.9434922]},"n1819805870":{"id":"n1819805870","loc":[-85.6314903,41.9431535]},"n1819805873":{"id":"n1819805873","loc":[-85.6251667,41.9401166]},"n1819805876":{"id":"n1819805876","loc":[-85.63287,41.939941]},"n1819805878":{"id":"n1819805878","loc":[-85.6307886,41.9437317]},"n1819805880":{"id":"n1819805880","loc":[-85.6321727,41.940348]},"n1819805883":{"id":"n1819805883","loc":[-85.6265872,41.940113]},"n1819805885":{"id":"n1819805885","loc":[-85.6268404,41.9406672]},"n1819805887":{"id":"n1819805887","loc":[-85.6325267,41.9389035]},"n1819805889":{"id":"n1819805889","loc":[-85.6364964,41.933189]},"n1819805911":{"id":"n1819805911","loc":[-85.6248663,41.9401804]},"n1819805922":{"id":"n1819805922","loc":[-85.633267,41.9387199]},"n1819805925":{"id":"n1819805925","loc":[-85.6293402,41.9408428]},"n1819848849":{"id":"n1819848849","loc":[-85.6464957,41.9695178]},"n1819848850":{"id":"n1819848850","loc":[-85.6497642,41.9611355]},"n1819848851":{"id":"n1819848851","loc":[-85.6480943,41.9624818]},"n1819848854":{"id":"n1819848854","loc":[-85.6500362,41.9657367]},"n1819848855":{"id":"n1819848855","loc":[-85.6493673,41.9783496]},"n1819848856":{"id":"n1819848856","loc":[-85.6457409,41.9548007]},"n1819848857":{"id":"n1819848857","loc":[-85.651313,41.9760426]},"n1819848858":{"id":"n1819848858","loc":[-85.6495819,41.9784772]},"n1819848859":{"id":"n1819848859","loc":[-85.6495105,41.9833722]},"n1819848860":{"id":"n1819848860","loc":[-85.6405053,41.9492792]},"n1819848863":{"id":"n1819848863","loc":[-85.6502293,41.9786826]},"n1819848865":{"id":"n1819848865","loc":[-85.6406877,41.9495106]},"n1819848870":{"id":"n1819848870","loc":[-85.6493136,41.9704611]},"n1819848871":{"id":"n1819848871","loc":[-85.6372249,41.9441284]},"n1819848873":{"id":"n1819848873","loc":[-85.6512379,41.9659441]},"n1819848875":{"id":"n1819848875","loc":[-85.6508087,41.9650187]},"n1819848877":{"id":"n1819848877","loc":[-85.6487166,41.9605352]},"n1819848878":{"id":"n1819848878","loc":[-85.6506478,41.9760665]},"n1819848879":{"id":"n1819848879","loc":[-85.651431,41.9758512]},"n1819848886":{"id":"n1819848886","loc":[-85.6477617,41.9563945]},"n1819848889":{"id":"n1819848889","loc":[-85.6497895,41.9832286]},"n1819848892":{"id":"n1819848892","loc":[-85.6504868,41.9791931]},"n1819848893":{"id":"n1819848893","loc":[-85.6498002,41.9615085]},"n1819848894":{"id":"n1819848894","loc":[-85.6404302,41.9502846]},"n1819848901":{"id":"n1819848901","loc":[-85.6354412,41.9439886]},"n1819848903":{"id":"n1819848903","loc":[-85.6472145,41.9698528]},"n1819848904":{"id":"n1819848904","loc":[-85.6401979,41.9486233]},"n1819848905":{"id":"n1819848905","loc":[-85.6475042,41.963503]},"n1819848909":{"id":"n1819848909","loc":[-85.6343405,41.94358]},"n1819848914":{"id":"n1819848914","loc":[-85.6503474,41.9737773]},"n1819848915":{"id":"n1819848915","loc":[-85.6389533,41.9470992]},"n1819848916":{"id":"n1819848916","loc":[-85.6483625,41.9577907]},"n1819848917":{"id":"n1819848917","loc":[-85.6484768,41.9617419]},"n1819848918":{"id":"n1819848918","loc":[-85.644078,41.9545693]},"n1819848919":{"id":"n1819848919","loc":[-85.6437169,41.9543041]},"n1819848920":{"id":"n1819848920","loc":[-85.6478331,41.9627949]},"n1819848922":{"id":"n1819848922","loc":[-85.6499144,41.9785889]},"n1819848924":{"id":"n1819848924","loc":[-85.647633,41.9720066]},"n1819848926":{"id":"n1819848926","loc":[-85.6487987,41.978868]},"n1819848927":{"id":"n1819848927","loc":[-85.6495105,41.9730355]},"n1819848928":{"id":"n1819848928","loc":[-85.648223,41.9829654]},"n1819848929":{"id":"n1819848929","loc":[-85.6514846,41.9659122]},"n1819848931":{"id":"n1819848931","loc":[-85.6498753,41.9731871]},"n1819848932":{"id":"n1819848932","loc":[-85.640906,41.9508575]},"n1819848933":{"id":"n1819848933","loc":[-85.649775,41.9799767]},"n1819848934":{"id":"n1819848934","loc":[-85.6507014,41.9739927]},"n1819848937":{"id":"n1819848937","loc":[-85.6479763,41.9840899]},"n1819848938":{"id":"n1819848938","loc":[-85.6501113,41.9600884]},"n1819848939":{"id":"n1819848939","loc":[-85.6389962,41.9478253]},"n1819848941":{"id":"n1819848941","loc":[-85.637469,41.9445791]},"n1819848942":{"id":"n1819848942","loc":[-85.6494569,41.9601682]},"n1819848943":{"id":"n1819848943","loc":[-85.6368803,41.9439351]},"n1819848945":{"id":"n1819848945","loc":[-85.6474398,41.9724213]},"n1819848946":{"id":"n1819848946","loc":[-85.6382629,41.9463666]},"n1819848948":{"id":"n1819848948","loc":[-85.6489633,41.9830771]},"n1819848952":{"id":"n1819848952","loc":[-85.6488882,41.9600326]},"n1819848953":{"id":"n1819848953","loc":[-85.6488094,41.9774324]},"n1819848954":{"id":"n1819848954","loc":[-85.6491135,41.9600485]},"n1819848955":{"id":"n1819848955","loc":[-85.6501435,41.9734583]},"n1819848956":{"id":"n1819848956","loc":[-85.6495534,41.960958]},"n1819848958":{"id":"n1819848958","loc":[-85.6474683,41.9561491]},"n1819848959":{"id":"n1819848959","loc":[-85.6401083,41.9485451]},"n1819848960":{"id":"n1819848960","loc":[-85.6481764,41.9678686]},"n1819848961":{"id":"n1819848961","loc":[-85.6484017,41.967382]},"n1819848962":{"id":"n1819848962","loc":[-85.6501328,41.959897]},"n1819848964":{"id":"n1819848964","loc":[-85.6403695,41.9504586]},"n1819848966":{"id":"n1819848966","loc":[-85.6398975,41.9491499]},"n1819848967":{"id":"n1819848967","loc":[-85.6412455,41.9510187]},"n1819848968":{"id":"n1819848968","loc":[-85.6482622,41.9619493]},"n1819848969":{"id":"n1819848969","loc":[-85.6405841,41.9501474]},"n1819848970":{"id":"n1819848970","loc":[-85.6478583,41.9703394]},"n1819848971":{"id":"n1819848971","loc":[-85.6493388,41.9832845]},"n1819848972":{"id":"n1819848972","loc":[-85.6485664,41.9829415]},"n1819848974":{"id":"n1819848974","loc":[-85.6491457,41.9779887]},"n1819848975":{"id":"n1819848975","loc":[-85.6468889,41.9697033]},"n1819848976":{"id":"n1819848976","loc":[-85.6452726,41.9546072]},"n1819848977":{"id":"n1819848977","loc":[-85.6448435,41.9546072]},"n1819848979":{"id":"n1819848979","loc":[-85.6485342,41.9763138]},"n1819848980":{"id":"n1819848980","loc":[-85.6495282,41.9664087]},"n1819848986":{"id":"n1819848986","loc":[-85.6486307,41.9603278]},"n1819848987":{"id":"n1819848987","loc":[-85.6492278,41.9791871]},"n1819848990":{"id":"n1819848990","loc":[-85.6501934,41.9800724]},"n1819848992":{"id":"n1819848992","loc":[-85.6482445,41.9819685]},"n1819848993":{"id":"n1819848993","loc":[-85.6481871,41.9704451]},"n1819848994":{"id":"n1819848994","loc":[-85.6371364,41.9457602]},"n1819848996":{"id":"n1819848996","loc":[-85.6500362,41.9801023]},"n1819849000":{"id":"n1819849000","loc":[-85.639007,41.9485914]},"n1819849001":{"id":"n1819849001","loc":[-85.6488882,41.9669253]},"n1819849002":{"id":"n1819849002","loc":[-85.6484698,41.9565062]},"n1819849004":{"id":"n1819849004","loc":[-85.6510769,41.9761064]},"n1819849005":{"id":"n1819849005","loc":[-85.6503581,41.9799029]},"n1819849006":{"id":"n1819849006","loc":[-85.6489381,41.9703893]},"n1819849008":{"id":"n1819849008","loc":[-85.6497457,41.9833588]},"n1819849011":{"id":"n1819849011","loc":[-85.6497358,41.9717593]},"n1819849012":{"id":"n1819849012","loc":[-85.6494676,41.9796796]},"n1819849019":{"id":"n1819849019","loc":[-85.6486093,41.9771034]},"n1819849021":{"id":"n1819849021","loc":[-85.6504546,41.9796556]},"n1819849022":{"id":"n1819849022","loc":[-85.6371294,41.9454154]},"n1819849023":{"id":"n1819849023","loc":[-85.6503436,41.9759249]},"n1819849025":{"id":"n1819849025","loc":[-85.6462382,41.9693822]},"n1819849026":{"id":"n1819849026","loc":[-85.6497573,41.983093]},"n1819849028":{"id":"n1819849028","loc":[-85.6497465,41.9602799]},"n1819849029":{"id":"n1819849029","loc":[-85.6374728,41.9460698]},"n1819849030":{"id":"n1819849030","loc":[-85.6486592,41.9566039]},"n1819849031":{"id":"n1819849031","loc":[-85.6515989,41.9654993]},"n1819849032":{"id":"n1819849032","loc":[-85.6387028,41.9482658]},"n1819849033":{"id":"n1819849033","loc":[-85.6464742,41.9688398]},"n1819849034":{"id":"n1819849034","loc":[-85.6495212,41.9589236]},"n1819849035":{"id":"n1819849035","loc":[-85.6490599,41.9790096]},"n1819849036":{"id":"n1819849036","loc":[-85.6489918,41.9800724]},"n1819849038":{"id":"n1819849038","loc":[-85.6499182,41.9659042]},"n1819849040":{"id":"n1819849040","loc":[-85.639758,41.9490143]},"n1819849041":{"id":"n1819849041","loc":[-85.6514846,41.9755241]},"n1819849042":{"id":"n1819849042","loc":[-85.6436633,41.9540647]},"n1819849045":{"id":"n1819849045","loc":[-85.6475541,41.9726387]},"n1819849046":{"id":"n1819849046","loc":[-85.6488308,41.9718331]},"n1819849047":{"id":"n1819849047","loc":[-85.6377694,41.9460953]},"n1819849048":{"id":"n1819849048","loc":[-85.6490706,41.9804452]},"n1819849049":{"id":"n1819849049","loc":[-85.6485449,41.9766248]},"n1819849051":{"id":"n1819849051","loc":[-85.6483625,41.9790256]},"n1819849052":{"id":"n1819849052","loc":[-85.6490706,41.9585167]},"n1819849053":{"id":"n1819849053","loc":[-85.6425008,41.9522874]},"n1819849054":{"id":"n1819849054","loc":[-85.6475793,41.9632158]},"n1819849055":{"id":"n1819849055","loc":[-85.6408631,41.9499399]},"n1819849056":{"id":"n1819849056","loc":[-85.6483373,41.9814681]},"n1819849057":{"id":"n1819849057","loc":[-85.6313548,41.9442876]},"n1819849058":{"id":"n1819849058","loc":[-85.6432663,41.9529796]},"n1819849059":{"id":"n1819849059","loc":[-85.6487128,41.9582873]},"n1819849060":{"id":"n1819849060","loc":[-85.6482338,41.9817612]},"n1819849062":{"id":"n1819849062","loc":[-85.6485664,41.9788661]},"n1819849063":{"id":"n1819849063","loc":[-85.6373081,41.9448824]},"n1819849064":{"id":"n1819849064","loc":[-85.6472215,41.9557582]},"n1819849065":{"id":"n1819849065","loc":[-85.6348984,41.9440414]},"n1819849066":{"id":"n1819849066","loc":[-85.6501972,41.9647315]},"n1819849067":{"id":"n1819849067","loc":[-85.6489741,41.9808281]},"n1819849068":{"id":"n1819849068","loc":[-85.6420111,41.9515034]},"n1819849069":{"id":"n1819849069","loc":[-85.6397972,41.9488882]},"n1819849070":{"id":"n1819849070","loc":[-85.6499718,41.9593465]},"n1819849071":{"id":"n1819849071","loc":[-85.6486844,41.9811311]},"n1819849072":{"id":"n1819849072","loc":[-85.6390392,41.9474663]},"n1819849074":{"id":"n1819849074","loc":[-85.6495642,41.9616362]},"n1819849075":{"id":"n1819849075","loc":[-85.6483518,41.9791931]},"n1819849076":{"id":"n1819849076","loc":[-85.6478974,41.9833104]},"n1819849077":{"id":"n1819849077","loc":[-85.640155,41.948719]},"n1819849078":{"id":"n1819849078","loc":[-85.6399366,41.9487845]},"n1819849079":{"id":"n1819849079","loc":[-85.6492959,41.9825348]},"n1819849080":{"id":"n1819849080","loc":[-85.6505083,41.9648352]},"n1819849081":{"id":"n1819849081","loc":[-85.6492959,41.9645241]},"n1819849082":{"id":"n1819849082","loc":[-85.6402049,41.9491835]},"n1819849083":{"id":"n1819849083","loc":[-85.6495175,41.9826963]},"n1819849084":{"id":"n1819849084","loc":[-85.6480836,41.9728361]},"n1819849085":{"id":"n1819849085","loc":[-85.6374349,41.9443425]},"n1819849086":{"id":"n1819849086","loc":[-85.6478331,41.9681238]},"n1819849089":{"id":"n1819849089","loc":[-85.639368,41.9486169]},"n1819849092":{"id":"n1819849092","loc":[-85.6503581,41.9788022]},"n1819849093":{"id":"n1819849093","loc":[-85.64862,41.9568014]},"n1819849094":{"id":"n1819849094","loc":[-85.6496999,41.9828877]},"n1819849095":{"id":"n1819849095","loc":[-85.647472,41.972198]},"n1819849096":{"id":"n1819849096","loc":[-85.6485771,41.9644523]},"n1819849097":{"id":"n1819849097","loc":[-85.6388353,41.9480488]},"n1819849099":{"id":"n1819849099","loc":[-85.6472752,41.9683312]},"n1819849104":{"id":"n1819849104","loc":[-85.6479548,41.9836035]},"n1819849105":{"id":"n1819849105","loc":[-85.6462489,41.9691668]},"n1819849107":{"id":"n1819849107","loc":[-85.6511912,41.9746328]},"n1819849108":{"id":"n1819849108","loc":[-85.6498646,41.9714881]},"n1819849111":{"id":"n1819849111","loc":[-85.6488239,41.961684]},"n1819849112":{"id":"n1819849112","loc":[-85.6469356,41.9553812]},"n1819849114":{"id":"n1819849114","loc":[-85.6479548,41.9640853]},"n1819849119":{"id":"n1819849119","loc":[-85.6491565,41.961692]},"n1819849121":{"id":"n1819849121","loc":[-85.651667,41.9656728]},"n1819849124":{"id":"n1819849124","loc":[-85.6388423,41.9484414]},"n1819849126":{"id":"n1819849126","loc":[-85.6371686,41.9450978]},"n1819849127":{"id":"n1819849127","loc":[-85.6502615,41.9656728]},"n1819849129":{"id":"n1819849129","loc":[-85.6498501,41.9613031]},"n1819849131":{"id":"n1819849131","loc":[-85.6513881,41.9653298]},"n1819849133":{"id":"n1819849133","loc":[-85.639883,41.9485291]},"n1819849139":{"id":"n1819849139","loc":[-85.6508693,41.9658264]},"n1819849140":{"id":"n1819849140","loc":[-85.6486806,41.9761642]},"n1819849141":{"id":"n1819849141","loc":[-85.6483159,41.9717613]},"n1819849144":{"id":"n1819849144","loc":[-85.6443714,41.9546232]},"n1819849146":{"id":"n1819849146","loc":[-85.641775,41.9513359]},"n1819849147":{"id":"n1819849147","loc":[-85.6495604,41.9757335]},"n1819849148":{"id":"n1819849148","loc":[-85.6465671,41.9551678]},"n1819849150":{"id":"n1819849150","loc":[-85.6485127,41.9794084]},"n1819849151":{"id":"n1819849151","loc":[-85.6499144,41.9757096]},"n1819849152":{"id":"n1819849152","loc":[-85.6433736,41.9531072]},"n1819849154":{"id":"n1819849154","loc":[-85.6489741,41.9607426]},"n1819849155":{"id":"n1819849155","loc":[-85.640627,41.9507697]},"n1819849156":{"id":"n1819849156","loc":[-85.6509659,41.9743058]},"n1819849157":{"id":"n1819849157","loc":[-85.6486844,41.9704431]},"n1819849158":{"id":"n1819849158","loc":[-85.6498538,41.9711132]},"n1819849159":{"id":"n1819849159","loc":[-85.6358937,41.943719]},"n1819849160":{"id":"n1819849160","loc":[-85.6497358,41.9707702]},"n1819849161":{"id":"n1819849161","loc":[-85.6480476,41.9564842]},"n1819849162":{"id":"n1819849162","loc":[-85.6482982,41.9574556]},"n1819849163":{"id":"n1819849163","loc":[-85.6501757,41.9757794]},"n1819849164":{"id":"n1819849164","loc":[-85.6372973,41.9459916]},"n1819849165":{"id":"n1819849165","loc":[-85.6513773,41.9750775]},"n1819849166":{"id":"n1819849166","loc":[-85.6436418,41.9537455]},"n1819849167":{"id":"n1819849167","loc":[-85.6483625,41.9571524]},"n1819849169":{"id":"n1819849169","loc":[-85.647751,41.9727962]},"n1819849170":{"id":"n1819849170","loc":[-85.6504546,41.9656808]},"n1819849171":{"id":"n1819849171","loc":[-85.6479977,41.971839]},"n1819849172":{"id":"n1819849172","loc":[-85.6482767,41.9642449]},"n1819849174":{"id":"n1819849174","loc":[-85.6414317,41.9512086]},"n1819849176":{"id":"n1819849176","loc":[-85.6469034,41.9685287]},"n1819849179":{"id":"n1819849179","loc":[-85.6408631,41.9497564]},"n1819849182":{"id":"n1819849182","loc":[-85.6476721,41.96384]},"n1819849183":{"id":"n1819849183","loc":[-85.6479725,41.983111]},"n1819849184":{"id":"n1819849184","loc":[-85.640788,41.9500516]},"n1819849185":{"id":"n1819849185","loc":[-85.6427798,41.9528778]},"n1819849186":{"id":"n1819849186","loc":[-85.6435308,41.9534124]},"n1819849187":{"id":"n1819849187","loc":[-85.6483733,41.9821998]},"n1819849189":{"id":"n1819849189","loc":[-85.6351752,41.9440796]},"n1819849191":{"id":"n1819849191","loc":[-85.6487021,41.9601463]},"n1819849192":{"id":"n1819849192","loc":[-85.6363811,41.9437605]},"n1819849193":{"id":"n1819849193","loc":[-85.6490883,41.9759728]},"n1819849194":{"id":"n1819849194","loc":[-85.6423292,41.9520081]},"n1819849195":{"id":"n1819849195","loc":[-85.6500003,41.960242]},"n1819849196":{"id":"n1819849196","loc":[-85.6385778,41.9466443]},"n1819849197":{"id":"n1819849197","loc":[-85.6494032,41.9718789]},"n1819849198":{"id":"n1819849198","loc":[-85.6404339,41.9506501]},"n1819849199":{"id":"n1819849199","loc":[-85.6426226,41.9527083]},"n1819849200":{"id":"n1819849200","loc":[-85.6439101,41.9545035]},"n1819849201":{"id":"n1819849201","loc":[-85.6516563,41.9657845]},"n1819849202":{"id":"n1819849202","loc":[-85.6473395,41.9699585]},"n1819858501":{"id":"n1819858501","loc":[-85.6361263,41.9437126]},"n1819858503":{"id":"n1819858503","loc":[-85.6350068,41.944034]},"n1819858513":{"id":"n1819858513","loc":[-85.6371402,41.9453282]},"n1819858518":{"id":"n1819858518","loc":[-85.6348713,41.9432923]},"n1819858523":{"id":"n1819858523","loc":[-85.6357047,41.943799]},"n1819858526":{"id":"n1819858526","loc":[-85.6349947,41.9435756]},"n1819858531":{"id":"n1819858531","loc":[-85.6350376,41.943827]},"n1820937508":{"id":"n1820937508","loc":[-85.1026013,42.0881722]},"n1820937509":{"id":"n1820937509","loc":[-85.0558088,42.102493]},"n1820937511":{"id":"n1820937511","loc":[-85.3030116,41.9724451]},"n1820937513":{"id":"n1820937513","loc":[-85.0353221,42.1027398]},"n1820937514":{"id":"n1820937514","loc":[-85.0835468,42.1015469]},"n1820937515":{"id":"n1820937515","loc":[-85.2421298,42.0106305]},"n1820937517":{"id":"n1820937517","loc":[-85.0090632,42.0910452]},"n1820937518":{"id":"n1820937518","loc":[-85.086626,42.0948838]},"n1820937520":{"id":"n1820937520","loc":[-85.2552039,42.0015448]},"n1820937521":{"id":"n1820937521","loc":[-85.3739614,41.9969917]},"n1820937522":{"id":"n1820937522","loc":[-85.4831166,41.993898]},"n1820937523":{"id":"n1820937523","loc":[-85.0341084,42.0977657]},"n1820937524":{"id":"n1820937524","loc":[-85.3272802,41.9710333]},"n1820937525":{"id":"n1820937525","loc":[-85.2125568,42.0414521]},"n1820937526":{"id":"n1820937526","loc":[-85.3798022,41.9992458]},"n1820937527":{"id":"n1820937527","loc":[-85.2652021,41.999768]},"n1820937528":{"id":"n1820937528","loc":[-85.3852739,42.0004896]},"n1820937529":{"id":"n1820937529","loc":[-85.3911919,42.0030513]},"n1820937530":{"id":"n1820937530","loc":[-85.5440349,41.9717109]},"n1820937531":{"id":"n1820937531","loc":[-85.2790155,41.9911764]},"n1820937532":{"id":"n1820937532","loc":[-85.4723277,41.9950518]},"n1820937533":{"id":"n1820937533","loc":[-85.5690546,41.9653931]},"n1820937535":{"id":"n1820937535","loc":[-85.5674882,41.9649623]},"n1820937536":{"id":"n1820937536","loc":[-85.6362815,41.9189165]},"n1820937537":{"id":"n1820937537","loc":[-85.5659003,41.963638]},"n1820937539":{"id":"n1820937539","loc":[-85.6391353,41.9122262]},"n1820937540":{"id":"n1820937540","loc":[-85.4834385,41.9894803]},"n1820937541":{"id":"n1820937541","loc":[-85.6399078,41.9160744]},"n1820937542":{"id":"n1820937542","loc":[-85.632874,41.941031]},"n1820937543":{"id":"n1820937543","loc":[-85.1307591,42.0726961]},"n1820937544":{"id":"n1820937544","loc":[-85.6444397,41.9128378]},"n1820937545":{"id":"n1820937545","loc":[-85.6197204,41.9420365]},"n1820937546":{"id":"n1820937546","loc":[-85.1164857,42.0864631]},"n1820937547":{"id":"n1820937547","loc":[-85.6476111,41.9142222]},"n1820937548":{"id":"n1820937548","loc":[-85.2915747,41.9774223]},"n1820937549":{"id":"n1820937549","loc":[-85.6430192,41.9102461]},"n1820937550":{"id":"n1820937550","loc":[-85.1597495,42.0639017]},"n1820937551":{"id":"n1820937551","loc":[-85.5504079,41.9701793]},"n1820937553":{"id":"n1820937553","loc":[-85.2781317,41.9948951]},"n1820937555":{"id":"n1820937555","loc":[-85.3724594,41.997518]},"n1820937556":{"id":"n1820937556","loc":[-85.5629434,41.9665155]},"n1820937557":{"id":"n1820937557","loc":[-85.3791971,41.9990808]},"n1820937558":{"id":"n1820937558","loc":[-85.001891,42.0878843]},"n1820937560":{"id":"n1820937560","loc":[-85.3140838,41.9709056]},"n1820937561":{"id":"n1820937561","loc":[-85.2468032,42.0146987]},"n1820937563":{"id":"n1820937563","loc":[-85.0877378,42.097255]},"n1820937564":{"id":"n1820937564","loc":[-85.2442498,42.0150654]},"n1820937566":{"id":"n1820937566","loc":[-85.3108973,41.9701478]},"n1820937568":{"id":"n1820937568","loc":[-85.0344584,42.1016572]},"n1820937569":{"id":"n1820937569","loc":[-85.2331025,42.0297387]},"n1820937570":{"id":"n1820937570","loc":[-85.5058446,41.9746996]},"n1820937571":{"id":"n1820937571","loc":[-85.5622739,41.9676427]},"n1820937572":{"id":"n1820937572","loc":[-85.2792687,41.9890337]},"n1820937574":{"id":"n1820937574","loc":[-84.9909302,42.08695]},"n1820937575":{"id":"n1820937575","loc":[-85.6218233,41.9418609]},"n1820937576":{"id":"n1820937576","loc":[-85.3577437,41.9931062]},"n1820937577":{"id":"n1820937577","loc":[-85.639028,41.9165853]},"n1820937578":{"id":"n1820937578","loc":[-84.9956576,42.0865348]},"n1820937579":{"id":"n1820937579","loc":[-85.4828376,41.990198]},"n1820937580":{"id":"n1820937580","loc":[-85.3244478,41.9720543]},"n1820937582":{"id":"n1820937582","loc":[-85.0517479,42.1035159]},"n1820937583":{"id":"n1820937583","loc":[-85.225646,42.0338025]},"n1820937584":{"id":"n1820937584","loc":[-84.9941019,42.0862163]},"n1820937586":{"id":"n1820937586","loc":[-85.1051762,42.0879452]},"n1820937587":{"id":"n1820937587","loc":[-85.1245203,42.0753162]},"n1820937588":{"id":"n1820937588","loc":[-85.3250808,41.9719506]},"n1820937589":{"id":"n1820937589","loc":[-85.2720109,41.997933]},"n1820937590":{"id":"n1820937590","loc":[-85.2556653,42.0027248]},"n1820937591":{"id":"n1820937591","loc":[-85.0872483,42.0943544]},"n1820937592":{"id":"n1820937592","loc":[-85.2778353,41.9955023]},"n1820937593":{"id":"n1820937593","loc":[-85.2984733,41.9735538]},"n1820937594":{"id":"n1820937594","loc":[-85.101578,42.0889552]},"n1820937595":{"id":"n1820937595","loc":[-85.3888745,42.0016959]},"n1820937596":{"id":"n1820937596","loc":[-84.9903508,42.0870654]},"n1820937597":{"id":"n1820937597","loc":[-85.6405558,41.9146261]},"n1820937598":{"id":"n1820937598","loc":[-85.6460704,41.9141311]},"n1820937599":{"id":"n1820937599","loc":[-85.0377468,42.1037428]},"n1820937600":{"id":"n1820937600","loc":[-85.2298345,42.0312899]},"n1820937601":{"id":"n1820937601","loc":[-85.1080958,42.0861964]},"n1820937602":{"id":"n1820937602","loc":[-85.6325307,41.9402329]},"n1820937603":{"id":"n1820937603","loc":[-85.1165984,42.0832184]},"n1820937604":{"id":"n1820937604","loc":[-85.6354446,41.9190602]},"n1820937605":{"id":"n1820937605","loc":[-85.1114592,42.0862959]},"n1820937606":{"id":"n1820937606","loc":[-85.0858763,42.1001646]},"n1820937607":{"id":"n1820937607","loc":[-85.0472083,42.1015151]},"n1820937608":{"id":"n1820937608","loc":[-85.0802477,42.1027609]},"n1820937610":{"id":"n1820937610","loc":[-85.0924585,42.0928564]},"n1820937611":{"id":"n1820937611","loc":[-85.0329617,42.09827]},"n1820937612":{"id":"n1820937612","loc":[-85.2814617,41.993465]},"n1820937613":{"id":"n1820937613","loc":[-85.3097708,41.9700282]},"n1820937614":{"id":"n1820937614","loc":[-85.2809427,41.993695]},"n1820937615":{"id":"n1820937615","loc":[-85.0583233,42.1026494]},"n1820937617":{"id":"n1820937617","loc":[-85.2801592,41.9840021]},"n1820937619":{"id":"n1820937619","loc":[-85.1064154,42.0863449]},"n1820937620":{"id":"n1820937620","loc":[-85.0423173,42.1014662]},"n1820937621":{"id":"n1820937621","loc":[-85.2168913,42.0398107]},"n1820937622":{"id":"n1820937622","loc":[-85.2798481,41.9833401]},"n1820937623":{"id":"n1820937623","loc":[-85.0575468,42.1028672]},"n1820937625":{"id":"n1820937625","loc":[-85.0130369,42.0893067]},"n1820937626":{"id":"n1820937626","loc":[-85.0346985,42.1018256]},"n1820937627":{"id":"n1820937627","loc":[-85.2231569,42.0372768]},"n1820937628":{"id":"n1820937628","loc":[-85.2956195,41.9732268]},"n1820937629":{"id":"n1820937629","loc":[-85.1052312,42.086893]},"n1820937630":{"id":"n1820937630","loc":[-85.4813356,41.9958436]},"n1820937631":{"id":"n1820937631","loc":[-85.0961599,42.0914672]},"n1820937632":{"id":"n1820937632","loc":[-85.308419,41.9704749]},"n1820937633":{"id":"n1820937633","loc":[-85.295952,41.9715119]},"n1820937634":{"id":"n1820937634","loc":[-85.3310933,41.9703923]},"n1820937635":{"id":"n1820937635","loc":[-85.2940745,41.9739686]},"n1820937636":{"id":"n1820937636","loc":[-85.3803343,42.000484]},"n1820937637":{"id":"n1820937637","loc":[-85.1174231,42.0845533]},"n1820937638":{"id":"n1820937638","loc":[-85.0095836,42.089839]},"n1820937639":{"id":"n1820937639","loc":[-85.3179354,41.9705866]},"n1820937640":{"id":"n1820937640","loc":[-85.257708,42.0001189]},"n1820937641":{"id":"n1820937641","loc":[-85.2563522,42.0002771]},"n1820937642":{"id":"n1820937642","loc":[-85.3181929,41.970419]},"n1820937643":{"id":"n1820937643","loc":[-85.2911884,41.9757154]},"n1820937644":{"id":"n1820937644","loc":[-85.2714423,41.9975862]},"n1820937645":{"id":"n1820937645","loc":[-85.0193669,42.089888]},"n1820937646":{"id":"n1820937646","loc":[-85.3889818,42.0039921]},"n1820937647":{"id":"n1820937647","loc":[-85.3408093,41.9853965]},"n1820937648":{"id":"n1820937648","loc":[-85.1258091,42.0748332]},"n1820937649":{"id":"n1820937649","loc":[-85.5722561,41.962782]},"n1820937650":{"id":"n1820937650","loc":[-85.3266902,41.9721819]},"n1820937651":{"id":"n1820937651","loc":[-85.1473255,42.065192]},"n1820937652":{"id":"n1820937652","loc":[-85.1462526,42.0655106]},"n1820937653":{"id":"n1820937653","loc":[-85.4641051,42.0013929]},"n1820937654":{"id":"n1820937654","loc":[-85.5620379,41.9700677]},"n1820937655":{"id":"n1820937655","loc":[-85.3226025,41.971121]},"n1820937656":{"id":"n1820937656","loc":[-85.0200965,42.0899516]},"n1820937657":{"id":"n1820937657","loc":[-85.0624714,42.1044711]},"n1820937658":{"id":"n1820937658","loc":[-85.5649562,41.9637178]},"n1820937659":{"id":"n1820937659","loc":[-85.2360315,42.0253315]},"n1820937660":{"id":"n1820937660","loc":[-85.3881449,41.9994475]},"n1820937661":{"id":"n1820937661","loc":[-85.5032911,41.976263]},"n1820937662":{"id":"n1820937662","loc":[-85.481297,41.9871414]},"n1820937663":{"id":"n1820937663","loc":[-85.1167056,42.0841898]},"n1820937664":{"id":"n1820937664","loc":[-85.2891714,41.9787223]},"n1820937665":{"id":"n1820937665","loc":[-85.4393429,42.0058736]},"n1820937666":{"id":"n1820937666","loc":[-85.0077007,42.0895762]},"n1820937667":{"id":"n1820937667","loc":[-85.2736202,41.9979171]},"n1820937668":{"id":"n1820937668","loc":[-84.9935332,42.0859296]},"n1820937669":{"id":"n1820937669","loc":[-85.0622769,42.1046713]},"n1820937670":{"id":"n1820937670","loc":[-85.2309031,42.0311249]},"n1820937671":{"id":"n1820937671","loc":[-85.0343726,42.10069]},"n1820937672":{"id":"n1820937672","loc":[-85.0596551,42.1048612]},"n1820937673":{"id":"n1820937673","loc":[-85.1338597,42.0707449]},"n1820937674":{"id":"n1820937674","loc":[-85.3117663,41.9689194]},"n1820937675":{"id":"n1820937675","loc":[-85.0705649,42.1057499]},"n1820937676":{"id":"n1820937676","loc":[-85.2441425,42.0180944]},"n1820937677":{"id":"n1820937677","loc":[-85.1171174,42.0862692]},"n1820937678":{"id":"n1820937678","loc":[-85.0346824,42.1005519]},"n1820937680":{"id":"n1820937680","loc":[-85.2389927,42.0229245]},"n1820937681":{"id":"n1820937681","loc":[-85.0834892,42.1018642]},"n1820937682":{"id":"n1820937682","loc":[-85.0619443,42.1049459]},"n1820937683":{"id":"n1820937683","loc":[-85.2845366,41.9811868]},"n1820937684":{"id":"n1820937684","loc":[-85.210411,42.0394123]},"n1820937685":{"id":"n1820937685","loc":[-85.4377383,42.0055942]},"n1820937686":{"id":"n1820937686","loc":[-85.2882058,41.9789138]},"n1820937687":{"id":"n1820937687","loc":[-85.2741191,41.9955808]},"n1820937688":{"id":"n1820937688","loc":[-85.3442211,41.9903575]},"n1820937689":{"id":"n1820937689","loc":[-85.2641413,41.9995237]},"n1820937690":{"id":"n1820937690","loc":[-85.2804489,41.9829174]},"n1820937691":{"id":"n1820937691","loc":[-85.5593342,41.9729074]},"n1820937692":{"id":"n1820937692","loc":[-85.3590912,41.9932601]},"n1820937694":{"id":"n1820937694","loc":[-85.4826445,41.9957479]},"n1820937695":{"id":"n1820937695","loc":[-85.4539127,42.0063041]},"n1820937696":{"id":"n1820937696","loc":[-85.2456767,42.0153683]},"n1820937697":{"id":"n1820937697","loc":[-85.5794015,41.9489631]},"n1820937698":{"id":"n1820937698","loc":[-85.4108686,42.0078507]},"n1820937699":{"id":"n1820937699","loc":[-85.0616386,42.1051529]},"n1820937700":{"id":"n1820937700","loc":[-85.4977979,41.978241]},"n1820937701":{"id":"n1820937701","loc":[-85.2488417,42.0086319]},"n1820937702":{"id":"n1820937702","loc":[-85.5588836,41.9728116]},"n1820937703":{"id":"n1820937703","loc":[-85.4557366,42.0051241]},"n1820937705":{"id":"n1820937705","loc":[-85.0723151,42.1056094]},"n1820937706":{"id":"n1820937706","loc":[-85.0057909,42.0887323]},"n1820937707":{"id":"n1820937707","loc":[-85.0756786,42.105677]},"n1820937708":{"id":"n1820937708","loc":[-85.0901504,42.0940001]},"n1820937709":{"id":"n1820937709","loc":[-85.0979999,42.0910213]},"n1820937710":{"id":"n1820937710","loc":[-85.2376301,42.0239686]},"n1820937711":{"id":"n1820937711","loc":[-85.2780671,41.9902299]},"n1820937712":{"id":"n1820937712","loc":[-85.251481,42.0113188]},"n1820937713":{"id":"n1820937713","loc":[-85.3114767,41.9690311]},"n1820937714":{"id":"n1820937714","loc":[-85.2649621,41.9975662]},"n1820937715":{"id":"n1820937715","loc":[-85.283807,41.9813383]},"n1820937716":{"id":"n1820937716","loc":[-85.5515451,41.9703867]},"n1820937717":{"id":"n1820937717","loc":[-85.1176605,42.0850896]},"n1820937718":{"id":"n1820937718","loc":[-85.1069317,42.0862441]},"n1820937719":{"id":"n1820937719","loc":[-85.2739314,41.9976938]},"n1820937720":{"id":"n1820937720","loc":[-85.5550212,41.9702112]},"n1820937721":{"id":"n1820937721","loc":[-85.3076679,41.9719904]},"n1820937722":{"id":"n1820937722","loc":[-85.592319,41.9440316]},"n1820937723":{"id":"n1820937723","loc":[-85.3139979,41.9704031]},"n1820937724":{"id":"n1820937724","loc":[-85.0421134,42.1013149]},"n1820937725":{"id":"n1820937725","loc":[-85.2508373,42.0102741]},"n1820937726":{"id":"n1820937726","loc":[-85.0830922,42.1038821]},"n1820937727":{"id":"n1820937727","loc":[-85.6342473,41.9360031]},"n1820937730":{"id":"n1820937730","loc":[-85.0500192,42.1024942]},"n1820937731":{"id":"n1820937731","loc":[-85.3498644,41.9926221]},"n1820937732":{"id":"n1820937732","loc":[-85.0234117,42.0918903]},"n1820937733":{"id":"n1820937733","loc":[-85.0464425,42.1009408]},"n1820937734":{"id":"n1820937734","loc":[-85.033938,42.099886]},"n1820937736":{"id":"n1820937736","loc":[-85.0152752,42.0886009]},"n1820937737":{"id":"n1820937737","loc":[-85.0441894,42.1012671]},"n1820937738":{"id":"n1820937738","loc":[-85.4668731,41.9979804]},"n1820937739":{"id":"n1820937739","loc":[-85.4407377,42.006033]},"n1820937740":{"id":"n1820937740","loc":[-85.2262253,42.0344878]},"n1820937741":{"id":"n1820937741","loc":[-85.2550001,42.0033706]},"n1820937742":{"id":"n1820937742","loc":[-85.3071422,41.9722617]},"n1820937743":{"id":"n1820937743","loc":[-85.6147852,41.942228]},"n1820937744":{"id":"n1820937744","loc":[-85.0183853,42.0901825]},"n1820937745":{"id":"n1820937745","loc":[-85.6323161,41.9228489]},"n1820937746":{"id":"n1820937746","loc":[-85.0095568,42.0901376]},"n1820937747":{"id":"n1820937747","loc":[-85.2524037,42.0113826]},"n1820937748":{"id":"n1820937748","loc":[-85.3186864,41.9708578]},"n1820937749":{"id":"n1820937749","loc":[-85.2805669,41.9870883]},"n1820937750":{"id":"n1820937750","loc":[-85.0585768,42.1038144]},"n1820937751":{"id":"n1820937751","loc":[-85.2970786,41.9715358]},"n1820937752":{"id":"n1820937752","loc":[-85.1315758,42.0723445]},"n1820937753":{"id":"n1820937753","loc":[-85.2448291,42.0175444]},"n1820937754":{"id":"n1820937754","loc":[-85.2446468,42.0174248]},"n1820937755":{"id":"n1820937755","loc":[-85.229165,42.032129]},"n1820937756":{"id":"n1820937756","loc":[-85.5612654,41.9724926]},"n1820937757":{"id":"n1820937757","loc":[-85.2331776,42.030854]},"n1820937758":{"id":"n1820937758","loc":[-85.2271909,42.0334519]},"n1820937759":{"id":"n1820937759","loc":[-85.1032396,42.0879214]},"n1820937760":{"id":"n1820937760","loc":[-85.0638447,42.1044154]},"n1820937761":{"id":"n1820937761","loc":[-85.1260706,42.0745556]},"n1820937762":{"id":"n1820937762","loc":[-85.3454485,41.99132]},"n1820937763":{"id":"n1820937763","loc":[-85.2639321,41.9980088]},"n1820937764":{"id":"n1820937764","loc":[-85.0837681,42.1013746]},"n1820937765":{"id":"n1820937765","loc":[-85.2808137,41.9869368]},"n1820937766":{"id":"n1820937766","loc":[-85.6338997,41.9309373]},"n1820937767":{"id":"n1820937767","loc":[-85.2267403,42.0332766]},"n1820937768":{"id":"n1820937768","loc":[-85.0605831,42.1052074]},"n1820937769":{"id":"n1820937769","loc":[-85.0259021,42.0930037]},"n1820937770":{"id":"n1820937770","loc":[-85.232963,42.0313162]},"n1820937771":{"id":"n1820937771","loc":[-85.2404947,42.0125381]},"n1820937772":{"id":"n1820937772","loc":[-85.0910892,42.0935742]},"n1820937773":{"id":"n1820937773","loc":[-85.2554829,42.0019435]},"n1820937774":{"id":"n1820937774","loc":[-85.2799339,41.9867773]},"n1820937775":{"id":"n1820937775","loc":[-85.1075432,42.0852767]},"n1820937776":{"id":"n1820937776","loc":[-85.1176927,42.0854001]},"n1820937777":{"id":"n1820937777","loc":[-85.1067064,42.0863357]},"n1820937778":{"id":"n1820937778","loc":[-85.2517492,42.0106333]},"n1820937779":{"id":"n1820937779","loc":[-85.0987174,42.0909031]},"n1820937780":{"id":"n1820937780","loc":[-85.1160083,42.0863994]},"n1820937781":{"id":"n1820937781","loc":[-85.1268645,42.0739703]},"n1820937782":{"id":"n1820937782","loc":[-85.0454702,42.1002852]},"n1820937783":{"id":"n1820937783","loc":[-85.1334145,42.0705418]},"n1820937784":{"id":"n1820937784","loc":[-85.5866542,41.947431]},"n1820937786":{"id":"n1820937786","loc":[-85.2359886,42.0250366]},"n1820937787":{"id":"n1820937787","loc":[-85.3138048,41.9698527]},"n1820937788":{"id":"n1820937788","loc":[-85.1274291,42.0733081]},"n1820937790":{"id":"n1820937790","loc":[-85.6292905,41.9411267]},"n1820937791":{"id":"n1820937791","loc":[-85.5958809,41.9417333]},"n1820937792":{"id":"n1820937792","loc":[-85.1271019,42.0737581]},"n1820937793":{"id":"n1820937793","loc":[-85.2312679,42.0314437]},"n1820937794":{"id":"n1820937794","loc":[-85.1081387,42.0863516]},"n1820937795":{"id":"n1820937795","loc":[-85.2424473,42.0212109]},"n1820937796":{"id":"n1820937796","loc":[-85.2710654,41.9975236]},"n1820937797":{"id":"n1820937797","loc":[-85.4798408,41.9863223]},"n1820937798":{"id":"n1820937798","loc":[-85.035939,42.104296]},"n1820937799":{"id":"n1820937799","loc":[-85.2178139,42.0395398]},"n1820937800":{"id":"n1820937800","loc":[-85.0630709,42.1042614]},"n1820937801":{"id":"n1820937801","loc":[-85.0440124,42.1014861]},"n1820937802":{"id":"n1820937802","loc":[-85.1321874,42.0720458]},"n1820937804":{"id":"n1820937804","loc":[-85.079427,42.1029121]},"n1820937805":{"id":"n1820937805","loc":[-85.2962632,41.9738968]},"n1820937806":{"id":"n1820937806","loc":[-85.6334748,41.9274627]},"n1820937807":{"id":"n1820937807","loc":[-85.1057341,42.0872804]},"n1820937808":{"id":"n1820937808","loc":[-85.4960169,41.9778263]},"n1820937809":{"id":"n1820937809","loc":[-85.2821226,41.9910273]},"n1820937810":{"id":"n1820937810","loc":[-85.0013868,42.0885054]},"n1820937811":{"id":"n1820937811","loc":[-85.2952547,41.9729795]},"n1820937812":{"id":"n1820937812","loc":[-85.1298375,42.0667842]},"n1820937813":{"id":"n1820937813","loc":[-85.1339201,42.0710025]},"n1820937814":{"id":"n1820937814","loc":[-85.0374356,42.103691]},"n1820937815":{"id":"n1820937815","loc":[-85.0061115,42.0880607]},"n1820937817":{"id":"n1820937817","loc":[-85.2398402,42.0226934]},"n1820937818":{"id":"n1820937818","loc":[-85.123501,42.076236]},"n1820937819":{"id":"n1820937819","loc":[-85.1209489,42.0791294]},"n1820937820":{"id":"n1820937820","loc":[-85.0818624,42.1025778]},"n1820937821":{"id":"n1820937821","loc":[-85.4428835,42.0054749]},"n1820937822":{"id":"n1820937822","loc":[-85.4710359,41.9961147]},"n1820937823":{"id":"n1820937823","loc":[-85.4253354,42.006198]},"n1820937824":{"id":"n1820937824","loc":[-85.5486483,41.9709451]},"n1820937825":{"id":"n1820937825","loc":[-85.2303238,42.0310452]},"n1820937826":{"id":"n1820937826","loc":[-85.6450405,41.9136361]},"n1820937828":{"id":"n1820937828","loc":[-85.2606853,41.9964073]},"n1820937830":{"id":"n1820937830","loc":[-85.097383,42.0911447]},"n1820937831":{"id":"n1820937831","loc":[-85.0498207,42.102136]},"n1820937832":{"id":"n1820937832","loc":[-85.1232435,42.0763793]},"n1820937833":{"id":"n1820937833","loc":[-85.394093,42.0055921]},"n1820937834":{"id":"n1820937834","loc":[-85.3566665,41.9928295]},"n1820937835":{"id":"n1820937835","loc":[-85.3543276,41.9920002]},"n1820937837":{"id":"n1820937837","loc":[-85.084668,42.1034932]},"n1820937838":{"id":"n1820937838","loc":[-85.4400296,42.0060649]},"n1820937839":{"id":"n1820937839","loc":[-85.2362246,42.025714]},"n1820937840":{"id":"n1820937840","loc":[-85.0409225,42.1012791]},"n1820937841":{"id":"n1820937841","loc":[-85.2442283,42.019832]},"n1820937842":{"id":"n1820937842","loc":[-85.1123001,42.084824]},"n1820937843":{"id":"n1820937843","loc":[-85.1603074,42.0638061]},"n1820937844":{"id":"n1820937844","loc":[-85.1359744,42.0650646]},"n1820937845":{"id":"n1820937845","loc":[-85.1757569,42.053849]},"n1820937846":{"id":"n1820937846","loc":[-85.5200925,41.9716686]},"n1820937848":{"id":"n1820937848","loc":[-85.5525322,41.9701315]},"n1820937849":{"id":"n1820937849","loc":[-85.0406489,42.10149]},"n1820937850":{"id":"n1820937850","loc":[-85.0142547,42.088825]},"n1820937851":{"id":"n1820937851","loc":[-85.343749,41.9881884]},"n1820937852":{"id":"n1820937852","loc":[-85.074996,42.1060205]},"n1820937853":{"id":"n1820937853","loc":[-85.2436275,42.0136864]},"n1820937854":{"id":"n1820937854","loc":[-85.2641453,41.9980897]},"n1820937856":{"id":"n1820937856","loc":[-85.2802343,41.9870086]},"n1820937857":{"id":"n1820937857","loc":[-85.0099256,42.0909946]},"n1820937858":{"id":"n1820937858","loc":[-85.493957,41.9786079]},"n1820937859":{"id":"n1820937859","loc":[-85.0739405,42.1059795]},"n1820937860":{"id":"n1820937860","loc":[-85.2331605,42.0301423]},"n1820937862":{"id":"n1820937862","loc":[-85.2035231,42.0438425]},"n1820937863":{"id":"n1820937863","loc":[-85.0884928,42.0986971]},"n1820937864":{"id":"n1820937864","loc":[-85.131597,42.0690142]},"n1820937865":{"id":"n1820937865","loc":[-85.3937454,42.0052677]},"n1820937866":{"id":"n1820937866","loc":[-85.2212729,42.0378561]},"n1820937867":{"id":"n1820937867","loc":[-85.0886068,42.0982421]},"n1820937868":{"id":"n1820937868","loc":[-85.0875004,42.0968064]},"n1820937869":{"id":"n1820937869","loc":[-85.0771323,42.1042642]},"n1820937870":{"id":"n1820937870","loc":[-85.0164554,42.0894887]},"n1820937871":{"id":"n1820937871","loc":[-85.6069102,41.9415577]},"n1820937872":{"id":"n1820937872","loc":[-85.3273875,41.9704908]},"n1820937873":{"id":"n1820937873","loc":[-85.3890891,41.9997983]},"n1820937875":{"id":"n1820937875","loc":[-85.5091276,41.9723705]},"n1820937876":{"id":"n1820937876","loc":[-85.0770626,42.1047696]},"n1820937877":{"id":"n1820937877","loc":[-85.612575,41.9419567]},"n1820937878":{"id":"n1820937878","loc":[-85.3868146,42.0036094]},"n1820937879":{"id":"n1820937879","loc":[-85.2722738,41.9981204]},"n1820937880":{"id":"n1820937880","loc":[-85.3064878,41.9723733]},"n1820937882":{"id":"n1820937882","loc":[-85.1270845,42.0727678]},"n1820937884":{"id":"n1820937884","loc":[-85.3316512,41.97923]},"n1820937885":{"id":"n1820937885","loc":[-85.3932519,42.0042472]},"n1820937886":{"id":"n1820937886","loc":[-85.2457411,42.0175444]},"n1820937887":{"id":"n1820937887","loc":[-85.1397509,42.0648415]},"n1820937891":{"id":"n1820937891","loc":[-85.3196735,41.9719665]},"n1820937892":{"id":"n1820937892","loc":[-85.3372473,41.9845033]},"n1820937894":{"id":"n1820937894","loc":[-85.3254778,41.9719745]},"n1820937897":{"id":"n1820937897","loc":[-85.3185148,41.9691268]},"n1820937899":{"id":"n1820937899","loc":[-85.5419106,41.9714556]},"n1820937901":{"id":"n1820937901","loc":[-85.3293509,41.9748368]},"n1820937903":{"id":"n1820937903","loc":[-85.0798078,42.1028365]},"n1820937905":{"id":"n1820937905","loc":[-85.3954191,42.0056025]},"n1820937909":{"id":"n1820937909","loc":[-85.3417534,41.9857155]},"n1820937913":{"id":"n1820937913","loc":[-84.9927822,42.0857107]},"n1820937915":{"id":"n1820937915","loc":[-85.5444212,41.9712801]},"n1820937917":{"id":"n1820937917","loc":[-85.259088,41.9981682]},"n1820937921":{"id":"n1820937921","loc":[-85.2784576,41.9876358]},"n1820937922":{"id":"n1820937922","loc":[-84.9971918,42.087753]},"n1820937924":{"id":"n1820937924","loc":[-85.5310688,41.966899]},"n1820937928":{"id":"n1820937928","loc":[-85.3766436,41.9979326]},"n1820937930":{"id":"n1820937930","loc":[-85.5494852,41.9704346]},"n1820937933":{"id":"n1820937933","loc":[-85.5548281,41.9695412]},"n1820937935":{"id":"n1820937935","loc":[-85.0768588,42.105088]},"n1820937937":{"id":"n1820937937","loc":[-85.2646885,41.9978054]},"n1820937939":{"id":"n1820937939","loc":[-85.2441532,42.0176082]},"n1820937941":{"id":"n1820937941","loc":[-85.105553,42.0877928]},"n1820937943":{"id":"n1820937943","loc":[-85.0879457,42.0958909]},"n1820937944":{"id":"n1820937944","loc":[-85.3187015,41.9704402]},"n1820937945":{"id":"n1820937945","loc":[-85.5624456,41.970626]},"n1820937946":{"id":"n1820937946","loc":[-85.0580176,42.1028644]},"n1820937948":{"id":"n1820937948","loc":[-85.3016061,41.9726286]},"n1820937949":{"id":"n1820937949","loc":[-85.4310388,42.0069418]},"n1820937950":{"id":"n1820937950","loc":[-85.2945144,41.9740723]},"n1820937951":{"id":"n1820937951","loc":[-85.1170222,42.082657]},"n1820937952":{"id":"n1820937952","loc":[-85.0864503,42.0947632]},"n1820937953":{"id":"n1820937953","loc":[-85.4285926,42.0059533]},"n1820937970":{"id":"n1820937970","loc":[-85.3629965,41.9938023]},"n1820937972":{"id":"n1820937972","loc":[-85.2438099,42.0199755]},"n1820937974":{"id":"n1820937974","loc":[-85.1327654,42.0699285]},"n1820937977":{"id":"n1820937977","loc":[-85.1515956,42.0611935]},"n1820937978":{"id":"n1820937978","loc":[-85.0107369,42.0896638]},"n1820937979":{"id":"n1820937979","loc":[-85.1152626,42.0862083]},"n1820937980":{"id":"n1820937980","loc":[-85.4531831,42.0062881]},"n1820937981":{"id":"n1820937981","loc":[-85.0341473,42.0985924]},"n1820937982":{"id":"n1820937982","loc":[-85.0877485,42.0960171]},"n1820937983":{"id":"n1820937983","loc":[-85.2756373,41.9951742]},"n1820937984":{"id":"n1820937984","loc":[-85.2965421,41.9714401]},"n1820937985":{"id":"n1820937985","loc":[-85.2409775,42.0226934]},"n1820937986":{"id":"n1820937986","loc":[-85.0170723,42.0900579]},"n1820937987":{"id":"n1820937987","loc":[-85.1034663,42.0880555]},"n1820937988":{"id":"n1820937988","loc":[-85.0585071,42.1031577]},"n1820937990":{"id":"n1820937990","loc":[-85.0819174,42.1032373]},"n1820937992":{"id":"n1820937992","loc":[-85.0546608,42.1030542]},"n1820937993":{"id":"n1820937993","loc":[-85.0100811,42.0906125]},"n1820937995":{"id":"n1820937995","loc":[-85.6304278,41.9432655]},"n1820937997":{"id":"n1820937997","loc":[-85.0255628,42.092778]},"n1820938011":{"id":"n1820938011","loc":[-85.2316756,42.0317146]},"n1820938012":{"id":"n1820938012","loc":[-85.4067917,42.008042]},"n1820938013":{"id":"n1820938013","loc":[-85.390398,42.0028759]},"n1820938014":{"id":"n1820938014","loc":[-85.0161604,42.0886527]},"n1820938015":{"id":"n1820938015","loc":[-85.125337,42.0744589]},"n1820938016":{"id":"n1820938016","loc":[-85.2151317,42.0404801]},"n1820938017":{"id":"n1820938017","loc":[-85.3165085,41.9706025]},"n1820938018":{"id":"n1820938018","loc":[-85.5641193,41.9640688]},"n1820938019":{"id":"n1820938019","loc":[-85.147583,42.0642203]},"n1820938022":{"id":"n1820938022","loc":[-85.2803781,41.9947886]},"n1820938024":{"id":"n1820938024","loc":[-85.2692469,41.9982053]},"n1820938026":{"id":"n1820938026","loc":[-85.4321975,42.0067505]},"n1820938028":{"id":"n1820938028","loc":[-85.572535,41.9633405]},"n1820938030":{"id":"n1820938030","loc":[-85.3237505,41.9716475]},"n1820938032":{"id":"n1820938032","loc":[-85.6487698,41.9141583]},"n1820938033":{"id":"n1820938033","loc":[-85.0526371,42.1038315]},"n1820938034":{"id":"n1820938034","loc":[-85.088069,42.0978731]},"n1820938035":{"id":"n1820938035","loc":[-85.2516312,42.0102267]},"n1820938039":{"id":"n1820938039","loc":[-85.2731374,41.9982958]},"n1820938040":{"id":"n1820938040","loc":[-85.5453224,41.9713439]},"n1820938041":{"id":"n1820938041","loc":[-85.4480548,42.0049647]},"n1820938043":{"id":"n1820938043","loc":[-85.2504081,42.010322]},"n1820938045":{"id":"n1820938045","loc":[-85.2663447,41.99919]},"n1820938046":{"id":"n1820938046","loc":[-85.0507287,42.102907]},"n1820938047":{"id":"n1820938047","loc":[-85.0408246,42.1024743]},"n1820938048":{"id":"n1820938048","loc":[-85.2796335,41.9866099]},"n1820938050":{"id":"n1820938050","loc":[-85.452475,42.0061127]},"n1820938051":{"id":"n1820938051","loc":[-85.2410569,42.0128147]},"n1820938052":{"id":"n1820938052","loc":[-85.0413302,42.1011477]},"n1820938053":{"id":"n1820938053","loc":[-85.6327409,41.9197627]},"n1820938056":{"id":"n1820938056","loc":[-85.1072039,42.0857994]},"n1820938057":{"id":"n1820938057","loc":[-85.2001114,42.0448145]},"n1820938058":{"id":"n1820938058","loc":[-85.2655347,41.9978186]},"n1820938059":{"id":"n1820938059","loc":[-85.2330918,42.0304874]},"n1820938060":{"id":"n1820938060","loc":[-85.2601113,41.9966545]},"n1820938061":{"id":"n1820938061","loc":[-85.5397863,41.9708494]},"n1820938062":{"id":"n1820938062","loc":[-85.2702085,41.9977217]},"n1820938063":{"id":"n1820938063","loc":[-85.2219982,42.03699]},"n1820938064":{"id":"n1820938064","loc":[-85.0668957,42.105121]},"n1820938065":{"id":"n1820938065","loc":[-85.2328665,42.0270769]},"n1820938066":{"id":"n1820938066","loc":[-85.3189654,41.9694778]},"n1820938067":{"id":"n1820938067","loc":[-85.3814115,42.0022915]},"n1820938068":{"id":"n1820938068","loc":[-85.2759108,41.9956008]},"n1820938069":{"id":"n1820938069","loc":[-85.0391938,42.1034853]},"n1820938070":{"id":"n1820938070","loc":[-85.2850623,41.9810353]},"n1820938071":{"id":"n1820938071","loc":[-85.538074,41.970855]},"n1820938073":{"id":"n1820938073","loc":[-85.1319661,42.0670932]},"n1820938074":{"id":"n1820938074","loc":[-85.2816763,41.9913678]},"n1820938075":{"id":"n1820938075","loc":[-85.3182144,41.9700282]},"n1820938076":{"id":"n1820938076","loc":[-85.5909028,41.9458989]},"n1820938077":{"id":"n1820938077","loc":[-85.4057617,42.0074361]},"n1820938078":{"id":"n1820938078","loc":[-85.2620438,41.9967729]},"n1820938079":{"id":"n1820938079","loc":[-85.1122143,42.0851107]},"n1820938080":{"id":"n1820938080","loc":[-85.2443785,42.0174567]},"n1820938081":{"id":"n1820938081","loc":[-85.0319733,42.0953853]},"n1820938082":{"id":"n1820938082","loc":[-85.0878276,42.09443]},"n1820938083":{"id":"n1820938083","loc":[-85.0271789,42.0935809]},"n1820938084":{"id":"n1820938084","loc":[-85.0326399,42.0974222]},"n1820938085":{"id":"n1820938085","loc":[-85.3989167,42.0065592]},"n1820938086":{"id":"n1820938086","loc":[-85.3263361,41.9721261]},"n1820938087":{"id":"n1820938087","loc":[-85.2547855,42.0037134]},"n1820938088":{"id":"n1820938088","loc":[-85.4373259,42.005746]},"n1820938089":{"id":"n1820938089","loc":[-85.3094275,41.9699245]},"n1820938090":{"id":"n1820938090","loc":[-85.2783246,41.9872793]},"n1820938092":{"id":"n1820938092","loc":[-85.0815633,42.1025169]},"n1820938093":{"id":"n1820938093","loc":[-85.1788511,42.0522134]},"n1820938095":{"id":"n1820938095","loc":[-85.2830345,41.9816733]},"n1820938096":{"id":"n1820938096","loc":[-85.0744984,42.1059835]},"n1820938097":{"id":"n1820938097","loc":[-85.2788396,41.9879333]},"n1820938098":{"id":"n1820938098","loc":[-85.3640093,41.9946531]},"n1820938099":{"id":"n1820938099","loc":[-85.291167,41.9787463]},"n1820938100":{"id":"n1820938100","loc":[-85.0772436,42.1038156]},"n1820938101":{"id":"n1820938101","loc":[-85.00563,42.0887482]},"n1820938102":{"id":"n1820938102","loc":[-85.0326881,42.0961245]},"n1820938104":{"id":"n1820938104","loc":[-85.0530448,42.1038634]},"n1820938105":{"id":"n1820938105","loc":[-85.2625266,41.9970639]},"n1820938106":{"id":"n1820938106","loc":[-85.2827556,41.9823512]},"n1820938107":{"id":"n1820938107","loc":[-85.2784319,41.9910752]},"n1820938108":{"id":"n1820938108","loc":[-85.0882099,42.094393]},"n1820938109":{"id":"n1820938109","loc":[-85.5718484,41.9645371]},"n1820938110":{"id":"n1820938110","loc":[-85.2559764,42.0099317]},"n1820938111":{"id":"n1820938111","loc":[-85.2969284,41.973179]},"n1820938113":{"id":"n1820938113","loc":[-85.3875055,42.0019726]},"n1820938114":{"id":"n1820938114","loc":[-85.4250779,42.0068199]},"n1820938115":{"id":"n1820938115","loc":[-85.0645367,42.104889]},"n1820938116":{"id":"n1820938116","loc":[-85.1636762,42.0623724]},"n1820938117":{"id":"n1820938117","loc":[-85.0757322,42.1055935]},"n1820938118":{"id":"n1820938118","loc":[-85.3695197,41.9981559]},"n1820938120":{"id":"n1820938120","loc":[-85.1297516,42.0671027]},"n1820938121":{"id":"n1820938121","loc":[-85.1057448,42.0875551]},"n1820938122":{"id":"n1820938122","loc":[-85.2805175,41.9943182]},"n1820938123":{"id":"n1820938123","loc":[-85.2545173,42.0040722]},"n1820938124":{"id":"n1820938124","loc":[-84.9966607,42.0871319]},"n1820938125":{"id":"n1820938125","loc":[-85.0099899,42.0904612]},"n1820938126":{"id":"n1820938126","loc":[-85.2489919,42.0091102]},"n1820938127":{"id":"n1820938127","loc":[-85.0342706,42.0979476]},"n1820938128":{"id":"n1820938128","loc":[-85.1080891,42.0855884]},"n1820938129":{"id":"n1820938129","loc":[-85.0128183,42.0905356]},"n1820938130":{"id":"n1820938130","loc":[-85.631608,41.9434251]},"n1820938131":{"id":"n1820938131","loc":[-85.2551975,42.0008524]},"n1820938132":{"id":"n1820938132","loc":[-85.6421823,41.9096233]},"n1820938133":{"id":"n1820938133","loc":[-85.0125059,42.0906284]},"n1820938134":{"id":"n1820938134","loc":[-85.5499358,41.9701793]},"n1820938135":{"id":"n1820938135","loc":[-85.5472107,41.9712323]},"n1820938136":{"id":"n1820938136","loc":[-85.2760758,41.9958691]},"n1820938137":{"id":"n1820938137","loc":[-85.276678,41.9960433]},"n1820938138":{"id":"n1820938138","loc":[-85.0570319,42.1024731]},"n1820938140":{"id":"n1820938140","loc":[-85.2394325,42.0227492]},"n1820938142":{"id":"n1820938142","loc":[-85.5666341,41.9638829]},"n1820938144":{"id":"n1820938144","loc":[-85.258101,41.9996353]},"n1820938147":{"id":"n1820938147","loc":[-85.2129645,42.0413565]},"n1820938149":{"id":"n1820938149","loc":[-84.9962369,42.0868373]},"n1820938151":{"id":"n1820938151","loc":[-85.2570386,42.0084968]},"n1820938153":{"id":"n1820938153","loc":[-85.3971142,42.0050285]},"n1820938155":{"id":"n1820938155","loc":[-85.1072093,42.0855566]},"n1820938157":{"id":"n1820938157","loc":[-85.2840323,41.9920959]},"n1820938159":{"id":"n1820938159","loc":[-85.1187924,42.0816458]},"n1820938161":{"id":"n1820938161","loc":[-85.2681324,41.9985788]},"n1820938163":{"id":"n1820938163","loc":[-85.0887034,42.0984969]},"n1820938165":{"id":"n1820938165","loc":[-85.4133405,42.0073141]},"n1820938166":{"id":"n1820938166","loc":[-85.0097445,42.0902888]},"n1820938167":{"id":"n1820938167","loc":[-85.0828133,42.1037388]},"n1820938168":{"id":"n1820938168","loc":[-85.0549599,42.1030833]},"n1820938169":{"id":"n1820938169","loc":[-85.4571528,42.0010421]},"n1820938178":{"id":"n1820938178","loc":[-85.2706644,41.9975941]},"n1820938180":{"id":"n1820938180","loc":[-85.2258606,42.0335794]},"n1820938182":{"id":"n1820938182","loc":[-85.2832276,41.9814659]},"n1820938184":{"id":"n1820938184","loc":[-85.1082299,42.0860928]},"n1820938185":{"id":"n1820938185","loc":[-85.3839392,42.0022381]},"n1820938186":{"id":"n1820938186","loc":[-85.2772131,41.995905]},"n1820938187":{"id":"n1820938187","loc":[-85.1044895,42.0879214]},"n1820938188":{"id":"n1820938188","loc":[-85.2135267,42.0407087]},"n1820938189":{"id":"n1820938189","loc":[-85.2543993,42.0044628]},"n1820938190":{"id":"n1820938190","loc":[-85.1501793,42.0617351]},"n1820938191":{"id":"n1820938191","loc":[-85.3350587,41.9820469]},"n1820938192":{"id":"n1820938192","loc":[-85.1350731,42.0655735]},"n1820938193":{"id":"n1820938193","loc":[-85.0404008,42.1028843]},"n1820938194":{"id":"n1820938194","loc":[-85.6323161,41.943042]},"n1820938195":{"id":"n1820938195","loc":[-85.1259593,42.0742837]},"n1820938196":{"id":"n1820938196","loc":[-85.4562988,42.0033758]},"n1820938197":{"id":"n1820938197","loc":[-85.256824,42.0056826]},"n1820938198":{"id":"n1820938198","loc":[-85.2742103,41.9963862]},"n1820938199":{"id":"n1820938199","loc":[-85.0380888,42.1037877]},"n1820938200":{"id":"n1820938200","loc":[-85.47404,41.9944721]},"n1820938201":{"id":"n1820938201","loc":[-85.103021,42.087948]},"n1820938202":{"id":"n1820938202","loc":[-85.4030151,42.0065113]},"n1820938203":{"id":"n1820938203","loc":[-85.2113981,42.040735]},"n1820938204":{"id":"n1820938204","loc":[-85.2603433,41.9965137]},"n1820938206":{"id":"n1820938206","loc":[-85.1669378,42.0607634]},"n1820938207":{"id":"n1820938207","loc":[-85.0642027,42.1046076]},"n1820938208":{"id":"n1820938208","loc":[-85.2812428,41.9915696]},"n1820938209":{"id":"n1820938209","loc":[-85.0839559,42.1038343]},"n1820938210":{"id":"n1820938210","loc":[-85.1239946,42.0769368]},"n1820938211":{"id":"n1820938211","loc":[-85.2311177,42.0283042]},"n1820938212":{"id":"n1820938212","loc":[-85.2791614,41.9882682]},"n1820938213":{"id":"n1820938213","loc":[-85.2674941,41.9987582]},"n1820938214":{"id":"n1820938214","loc":[-85.352787,41.9919579]},"n1820938215":{"id":"n1820938215","loc":[-85.0874146,42.0952182]},"n1820938216":{"id":"n1820938216","loc":[-85.0069711,42.0877092]},"n1820938217":{"id":"n1820938217","loc":[-85.2059049,42.0404004]},"n1820938218":{"id":"n1820938218","loc":[-85.2403552,42.0227332]},"n1820938219":{"id":"n1820938219","loc":[-85.2492923,42.0098915]},"n1820938220":{"id":"n1820938220","loc":[-85.269778,41.9979541]},"n1820938221":{"id":"n1820938221","loc":[-85.2097673,42.0389024]},"n1820938222":{"id":"n1820938222","loc":[-85.0845942,42.1032015]},"n1820938223":{"id":"n1820938223","loc":[-84.993206,42.0858142]},"n1820938224":{"id":"n1820938224","loc":[-85.2108187,42.0402729]},"n1820938225":{"id":"n1820938225","loc":[-84.9893959,42.0873043]},"n1820938226":{"id":"n1820938226","loc":[-85.2952332,41.9719984]},"n1820938227":{"id":"n1820938227","loc":[-85.4100961,42.0081536]},"n1820938228":{"id":"n1820938228","loc":[-85.3299088,41.9785696]},"n1820938229":{"id":"n1820938229","loc":[-85.2258176,42.0340097]},"n1820938230":{"id":"n1820938230","loc":[-85.3146739,41.9711449]},"n1820938231":{"id":"n1820938231","loc":[-85.5447645,41.9712801]},"n1820938232":{"id":"n1820938232","loc":[-85.5510087,41.9705941]},"n1820938233":{"id":"n1820938233","loc":[-85.5122389,41.9703445]},"n1820938234":{"id":"n1820938234","loc":[-85.2792687,41.9865381]},"n1820938235":{"id":"n1820938235","loc":[-85.1475229,42.0630151]},"n1820938237":{"id":"n1820938237","loc":[-85.0332889,42.0996034]},"n1820938238":{"id":"n1820938238","loc":[-85.2588882,41.9986877]},"n1820938239":{"id":"n1820938239","loc":[-85.0656458,42.1050892]},"n1820938240":{"id":"n1820938240","loc":[-84.9913915,42.086098]},"n1820938241":{"id":"n1820938241","loc":[-85.4752416,41.9944402]},"n1820938242":{"id":"n1820938242","loc":[-85.1214304,42.0791147]},"n1820938243":{"id":"n1820938243","loc":[-85.0075183,42.0886925]},"n1820938244":{"id":"n1820938244","loc":[-85.1052888,42.0872087]},"n1820938245":{"id":"n1820938245","loc":[-85.3104252,41.9703393]},"n1820938246":{"id":"n1820938246","loc":[-85.232109,42.0318158]},"n1820938247":{"id":"n1820938247","loc":[-85.0756075,42.1059528]},"n1820938248":{"id":"n1820938248","loc":[-85.0075612,42.0890866]},"n1820938249":{"id":"n1820938249","loc":[-85.1013312,42.0897474]},"n1820938250":{"id":"n1820938250","loc":[-85.1168076,42.0828919]},"n1820938251":{"id":"n1820938251","loc":[-85.2951367,41.9723334]},"n1820938252":{"id":"n1820938252","loc":[-85.0879363,42.0976053]},"n1820938253":{"id":"n1820938253","loc":[-85.0354763,42.1021838]},"n1820938254":{"id":"n1820938254","loc":[-85.2379627,42.0236339]},"n1820938255":{"id":"n1820938255","loc":[-85.1308245,42.0685364]},"n1820938256":{"id":"n1820938256","loc":[-85.0914446,42.0934774]},"n1820938257":{"id":"n1820938257","loc":[-85.2436812,42.014069]},"n1820938258":{"id":"n1820938258","loc":[-85.0682529,42.1056106]},"n1820938259":{"id":"n1820938259","loc":[-85.290652,41.9766805]},"n1820938260":{"id":"n1820938260","loc":[-85.0133494,42.0897434]},"n1820938261":{"id":"n1820938261","loc":[-85.2753047,41.9949429]},"n1820938262":{"id":"n1820938262","loc":[-85.0314691,42.0950788]},"n1820938263":{"id":"n1820938263","loc":[-85.3444786,41.9908359]},"n1820938264":{"id":"n1820938264","loc":[-85.0443115,42.1009061]},"n1820938265":{"id":"n1820938265","loc":[-85.0634853,42.1043159]},"n1820938267":{"id":"n1820938267","loc":[-85.3978223,42.0053952]},"n1820938268":{"id":"n1820938268","loc":[-85.0228659,42.0911885]},"n1820938269":{"id":"n1820938269","loc":[-85.0220237,42.0906272]},"n1820938270":{"id":"n1820938270","loc":[-85.1061525,42.0863369]},"n1820938271":{"id":"n1820938271","loc":[-85.2382309,42.0233708]},"n1820938272":{"id":"n1820938272","loc":[-85.310672,41.9702755]},"n1820938273":{"id":"n1820938273","loc":[-85.1448192,42.0652613]},"n1820938274":{"id":"n1820938274","loc":[-85.6036057,41.9403766]},"n1820938275":{"id":"n1820938275","loc":[-85.0778941,42.1032413]},"n1820938276":{"id":"n1820938276","loc":[-85.1279374,42.0723974]},"n1820938277":{"id":"n1820938277","loc":[-85.2806635,41.9847836]},"n1820938278":{"id":"n1820938278","loc":[-85.2653201,41.9976352]},"n1820938279":{"id":"n1820938279","loc":[-85.0351665,42.1001805]},"n1820938280":{"id":"n1820938280","loc":[-85.0718269,42.1056253]},"n1820938281":{"id":"n1820938281","loc":[-85.2574248,42.0075322]},"n1820938282":{"id":"n1820938282","loc":[-85.126666,42.0740778]},"n1820938283":{"id":"n1820938283","loc":[-85.077705,42.1034733]},"n1820938284":{"id":"n1820938284","loc":[-85.3535552,41.9919045]},"n1820938286":{"id":"n1820938286","loc":[-85.2810711,41.9866657]},"n1820938287":{"id":"n1820938287","loc":[-85.4567494,42.0019885]},"n1820938288":{"id":"n1820938288","loc":[-85.2642419,41.9992936]},"n1820938289":{"id":"n1820938289","loc":[-85.2643344,41.9980925]},"n1820938290":{"id":"n1820938290","loc":[-85.3270335,41.9776125]},"n1820938291":{"id":"n1820938291","loc":[-85.1200584,42.0795077]},"n1820938292":{"id":"n1820938292","loc":[-85.2290792,42.0340256]},"n1820938293":{"id":"n1820938293","loc":[-85.6015887,41.9401372]},"n1820938294":{"id":"n1820938294","loc":[-85.5370869,41.970488]},"n1820938295":{"id":"n1820938295","loc":[-85.3108866,41.9698048]},"n1820938297":{"id":"n1820938297","loc":[-85.1556511,42.0628184]},"n1820938298":{"id":"n1820938298","loc":[-85.0027922,42.0875221]},"n1820938300":{"id":"n1820938300","loc":[-85.3873338,42.0040614]},"n1820938301":{"id":"n1820938301","loc":[-85.0350753,42.1004034]},"n1820938302":{"id":"n1820938302","loc":[-85.6239476,41.9411906]},"n1820938304":{"id":"n1820938304","loc":[-85.0118246,42.0897964]},"n1820938306":{"id":"n1820938306","loc":[-85.4796877,41.995275]},"n1820938307":{"id":"n1820938307","loc":[-85.5388636,41.9707856]},"n1820938309":{"id":"n1820938309","loc":[-85.2971902,41.9727773]},"n1820938310":{"id":"n1820938310","loc":[-85.5426831,41.9715513]},"n1820938311":{"id":"n1820938311","loc":[-85.2798373,41.9836671]},"n1820938312":{"id":"n1820938312","loc":[-85.2432198,42.0104017]},"n1820938313":{"id":"n1820938313","loc":[-85.2650412,41.9987554]},"n1820938317":{"id":"n1820938317","loc":[-85.0015423,42.0882386]},"n1820938318":{"id":"n1820938318","loc":[-85.1409783,42.064879]},"n1820938319":{"id":"n1820938319","loc":[-85.1691908,42.058995]},"n1820938320":{"id":"n1820938320","loc":[-85.1059165,42.0864882]},"n1820938321":{"id":"n1820938321","loc":[-85.3664941,41.9965771]},"n1820938323":{"id":"n1820938323","loc":[-85.3143198,41.9710971]},"n1820938324":{"id":"n1820938324","loc":[-85.0016067,42.0880675]},"n1820938325":{"id":"n1820938325","loc":[-85.0148139,42.0887164]},"n1820938326":{"id":"n1820938326","loc":[-85.0324682,42.0959056]},"n1820938327":{"id":"n1820938327","loc":[-85.0898661,42.0939921]},"n1820938328":{"id":"n1820938328","loc":[-85.2556427,42.0004936]},"n1820938329":{"id":"n1820938329","loc":[-85.6287112,41.9407437]},"n1820938330":{"id":"n1820938330","loc":[-84.9913392,42.0866701]},"n1820938331":{"id":"n1820938331","loc":[-85.2685777,41.9984632]},"n1820938332":{"id":"n1820938332","loc":[-85.0078884,42.0901614]},"n1820938333":{"id":"n1820938333","loc":[-84.999642,42.0878616]},"n1820938334":{"id":"n1820938334","loc":[-85.0188909,42.0899186]},"n1820938335":{"id":"n1820938335","loc":[-85.2830238,41.9819843]},"n1820938336":{"id":"n1820938336","loc":[-85.2491421,42.0096204]},"n1820938337":{"id":"n1820938337","loc":[-85.0585701,42.1034295]},"n1820938338":{"id":"n1820938338","loc":[-85.0651965,42.1051636]},"n1820938339":{"id":"n1820938339","loc":[-85.0583944,42.104292]},"n1820938340":{"id":"n1820938340","loc":[-85.119876,42.0801567]},"n1820938341":{"id":"n1820938341","loc":[-85.0943937,42.0931323]},"n1820938342":{"id":"n1820938342","loc":[-85.1504583,42.0613209]},"n1820938343":{"id":"n1820938343","loc":[-85.0425426,42.1019836]},"n1820938345":{"id":"n1820938345","loc":[-84.9991391,42.0878206]},"n1820938346":{"id":"n1820938346","loc":[-85.2563841,42.0094614]},"n1820938347":{"id":"n1820938347","loc":[-85.0515387,42.103297]},"n1820938348":{"id":"n1820938348","loc":[-85.0857261,42.1003636]},"n1820938349":{"id":"n1820938349","loc":[-85.078971,42.1029241]},"n1820938350":{"id":"n1820938350","loc":[-85.5699558,41.958931]},"n1820938351":{"id":"n1820938351","loc":[-85.3181285,41.9696533]},"n1820938352":{"id":"n1820938352","loc":[-85.5998506,41.9402329]},"n1820938353":{"id":"n1820938353","loc":[-85.2567277,42.000317]},"n1820938354":{"id":"n1820938354","loc":[-85.3082795,41.9708338]},"n1820938355":{"id":"n1820938355","loc":[-85.3127856,41.9692784]},"n1820938356":{"id":"n1820938356","loc":[-85.0340775,42.1010721]},"n1820938357":{"id":"n1820938357","loc":[-85.3158111,41.9706583]},"n1820938359":{"id":"n1820938359","loc":[-85.2312035,42.0280412]},"n1820938360":{"id":"n1820938360","loc":[-85.2448613,42.018477]},"n1820938361":{"id":"n1820938361","loc":[-85.29077,41.9759068]},"n1820938364":{"id":"n1820938364","loc":[-85.3677387,41.9976615]},"n1820938365":{"id":"n1820938365","loc":[-85.0785204,42.1030355]},"n1820938366":{"id":"n1820938366","loc":[-85.2262039,42.0333722]},"n1820938367":{"id":"n1820938367","loc":[-85.1226011,42.0780902]},"n1820938368":{"id":"n1820938368","loc":[-85.3229673,41.971129]},"n1820938369":{"id":"n1820938369","loc":[-85.385334,42.0000056]},"n1820938370":{"id":"n1820938370","loc":[-85.000098,42.0879094]},"n1820938372":{"id":"n1820938372","loc":[-85.3852481,42.0025091]},"n1820938373":{"id":"n1820938373","loc":[-85.3770513,41.9982515]},"n1820938374":{"id":"n1820938374","loc":[-85.6278314,41.9405362]},"n1820938375":{"id":"n1820938375","loc":[-85.6355133,41.9344068]},"n1820938376":{"id":"n1820938376","loc":[-85.635642,41.9324753]},"n1820938377":{"id":"n1820938377","loc":[-85.3154463,41.970778]},"n1820938378":{"id":"n1820938378","loc":[-85.0920334,42.093411]},"n1820938379":{"id":"n1820938379","loc":[-85.3269155,41.9722297]},"n1820938381":{"id":"n1820938381","loc":[-85.1134334,42.0849184]},"n1820938382":{"id":"n1820938382","loc":[-85.005968,42.088585]},"n1820938384":{"id":"n1820938384","loc":[-85.1245203,42.0757183]},"n1820938385":{"id":"n1820938385","loc":[-85.020704,42.0905396]},"n1820938386":{"id":"n1820938386","loc":[-85.119585,42.0808984]},"n1820938387":{"id":"n1820938387","loc":[-85.0072447,42.0880117]},"n1820938388":{"id":"n1820938388","loc":[-85.2742908,41.9960273]},"n1820938389":{"id":"n1820938389","loc":[-85.3275807,41.9696852]},"n1820938390":{"id":"n1820938390","loc":[-85.2385635,42.0231556]},"n1820938392":{"id":"n1820938392","loc":[-85.0202856,42.0900778]},"n1820938393":{"id":"n1820938393","loc":[-85.2067847,42.0395398]},"n1820938394":{"id":"n1820938394","loc":[-85.5183544,41.9713495]},"n1820938396":{"id":"n1820938396","loc":[-85.5073037,41.9736787]},"n1820938397":{"id":"n1820938397","loc":[-85.2519638,42.0114225]},"n1820938398":{"id":"n1820938398","loc":[-85.287487,41.9793285]},"n1820938399":{"id":"n1820938399","loc":[-85.2298088,42.0336431]},"n1820938400":{"id":"n1820938400","loc":[-85.229444,42.0339141]},"n1820938401":{"id":"n1820938401","loc":[-85.2421791,42.0220239]},"n1820938402":{"id":"n1820938402","loc":[-85.2976687,41.9737612]},"n1820938403":{"id":"n1820938403","loc":[-85.3622069,41.993473]},"n1820938404":{"id":"n1820938404","loc":[-85.2465458,42.014906]},"n1820938405":{"id":"n1820938405","loc":[-85.5724663,41.9639412]},"n1820938406":{"id":"n1820938406","loc":[-85.3708501,41.9982037]},"n1820938408":{"id":"n1820938408","loc":[-85.2564592,42.0055311]},"n1820938409":{"id":"n1820938409","loc":[-85.1192846,42.0810856]},"n1820938410":{"id":"n1820938410","loc":[-85.5623812,41.971663]},"n1820938411":{"id":"n1820938411","loc":[-85.3221948,41.9719665]},"n1820938412":{"id":"n1820938412","loc":[-85.5168738,41.9710305]},"n1820938413":{"id":"n1820938413","loc":[-85.4546852,42.0061127]},"n1820938414":{"id":"n1820938414","loc":[-85.5896153,41.9463617]},"n1820938415":{"id":"n1820938415","loc":[-85.2978189,41.9722138]},"n1820938416":{"id":"n1820938416","loc":[-85.1021681,42.0883581]},"n1820938417":{"id":"n1820938417","loc":[-85.2797193,41.9912984]},"n1820938419":{"id":"n1820938419","loc":[-85.2362461,42.0248533]},"n1820938420":{"id":"n1820938420","loc":[-85.4833639,41.9846252]},"n1820938422":{"id":"n1820938422","loc":[-85.3281064,41.9689433]},"n1820938424":{"id":"n1820938424","loc":[-85.2416963,42.0130088]},"n1820938425":{"id":"n1820938425","loc":[-85.5718655,41.9564577]},"n1820938426":{"id":"n1820938426","loc":[-85.0512812,42.1030701]},"n1820938427":{"id":"n1820938427","loc":[-85.1273527,42.0723616]},"n1820938428":{"id":"n1820938428","loc":[-85.0215033,42.0904083]},"n1820938429":{"id":"n1820938429","loc":[-85.6169953,41.942228]},"n1820938430":{"id":"n1820938430","loc":[-85.2829165,41.9907243]},"n1820938431":{"id":"n1820938431","loc":[-85.2240796,42.0374203]},"n1820938432":{"id":"n1820938432","loc":[-85.0167598,42.0898442]},"n1820938433":{"id":"n1820938433","loc":[-85.2132649,42.0411334]},"n1820938434":{"id":"n1820938434","loc":[-85.2293839,42.031513]},"n1820938435":{"id":"n1820938435","loc":[-85.1203374,42.0792608]},"n1820938436":{"id":"n1820938436","loc":[-85.109571,42.086268]},"n1820938437":{"id":"n1820938437","loc":[-85.1079026,42.0853842]},"n1820938438":{"id":"n1820938438","loc":[-85.109237,42.0862413]},"n1820938439":{"id":"n1820938439","loc":[-85.2259936,42.0350831]},"n1820938440":{"id":"n1820938440","loc":[-85.3669705,41.99679]},"n1820938441":{"id":"n1820938441","loc":[-85.2418143,42.0223507]},"n1820938442":{"id":"n1820938442","loc":[-85.3101248,41.9702515]},"n1820938443":{"id":"n1820938443","loc":[-85.069315,42.1059688]},"n1820938444":{"id":"n1820938444","loc":[-85.205862,42.0410378]},"n1820938445":{"id":"n1820938445","loc":[-85.0388076,42.1036604]},"n1820938446":{"id":"n1820938446","loc":[-85.2225389,42.0370115]},"n1820938447":{"id":"n1820938447","loc":[-85.3241474,41.9719346]},"n1820938448":{"id":"n1820938448","loc":[-85.3125496,41.9690789]},"n1820938449":{"id":"n1820938449","loc":[-85.1146497,42.0857039]},"n1820938450":{"id":"n1820938450","loc":[-85.1333944,42.0714963]},"n1820938451":{"id":"n1820938451","loc":[-85.5619306,41.9720937]},"n1820938452":{"id":"n1820938452","loc":[-85.2553651,42.0006479]},"n1820938453":{"id":"n1820938453","loc":[-85.3151137,41.9710093]},"n1820938454":{"id":"n1820938454","loc":[-85.2592315,41.9977947]},"n1820938455":{"id":"n1820938455","loc":[-85.2655723,41.9995966]},"n1820938456":{"id":"n1820938456","loc":[-85.4820652,41.9959233]},"n1820938459":{"id":"n1820938459","loc":[-85.450737,42.0055068]},"n1820938460":{"id":"n1820938460","loc":[-85.2428658,42.0205573]},"n1820938461":{"id":"n1820938461","loc":[-85.0835576,42.1021559]},"n1820938462":{"id":"n1820938462","loc":[-85.244636,42.0194733]},"n1820938463":{"id":"n1820938463","loc":[-85.5702562,41.9581332]},"n1820938465":{"id":"n1820938465","loc":[-85.5680031,41.9659515]},"n1820938467":{"id":"n1820938467","loc":[-85.2798752,41.9948353]},"n1820938468":{"id":"n1820938468","loc":[-85.0477407,42.1015537]},"n1820938469":{"id":"n1820938469","loc":[-85.6403842,41.913732]},"n1820938470":{"id":"n1820938470","loc":[-85.0396029,42.103289]},"n1820938471":{"id":"n1820938471","loc":[-85.2824702,41.9907777]},"n1820938472":{"id":"n1820938472","loc":[-85.2969284,41.9735538]},"n1820938474":{"id":"n1820938474","loc":[-85.401041,42.0070853]},"n1820938475":{"id":"n1820938475","loc":[-85.4116625,42.0073883]},"n1820938476":{"id":"n1820938476","loc":[-85.0437764,42.1016214]},"n1820938477":{"id":"n1820938477","loc":[-85.3643269,41.9958436]},"n1820938478":{"id":"n1820938478","loc":[-85.3895182,42.0009465]},"n1820938479":{"id":"n1820938479","loc":[-85.636157,41.9333373]},"n1820938480":{"id":"n1820938480","loc":[-85.2811355,41.9858044]},"n1820938481":{"id":"n1820938481","loc":[-85.0239052,42.092153]},"n1820938482":{"id":"n1820938482","loc":[-85.2558798,42.0053557]},"n1820938483":{"id":"n1820938483","loc":[-85.2544422,42.0047339]},"n1820938484":{"id":"n1820938484","loc":[-85.4864683,41.9843183]},"n1820938485":{"id":"n1820938485","loc":[-85.2554185,42.0031075]},"n1820938486":{"id":"n1820938486","loc":[-85.3082795,41.9712486]},"n1820938487":{"id":"n1820938487","loc":[-85.2433378,42.0133436]},"n1820938488":{"id":"n1820938488","loc":[-85.0216696,42.0904162]},"n1820938489":{"id":"n1820938489","loc":[-85.2546138,42.0050289]},"n1820938490":{"id":"n1820938490","loc":[-85.2717521,41.9977349]},"n1820938491":{"id":"n1820938491","loc":[-85.0100489,42.0908195]},"n1820938492":{"id":"n1820938492","loc":[-85.207879,42.0392211]},"n1820938493":{"id":"n1820938493","loc":[-85.0007363,42.0882836]},"n1820938494":{"id":"n1820938494","loc":[-85.5775303,41.9504097]},"n1820938495":{"id":"n1820938495","loc":[-85.1131584,42.0847683]},"n1820938496":{"id":"n1820938496","loc":[-85.0887825,42.0941633]},"n1820938497":{"id":"n1820938497","loc":[-85.1185926,42.0818938]},"n1820938498":{"id":"n1820938498","loc":[-85.2748487,41.9948712]},"n1820938499":{"id":"n1820938499","loc":[-85.2566952,42.0090788]},"n1820938500":{"id":"n1820938500","loc":[-85.0774757,42.1036234]},"n1820938501":{"id":"n1820938501","loc":[-85.4190869,42.008903]},"n1820938502":{"id":"n1820938502","loc":[-85.1140395,42.0850577]},"n1820938503":{"id":"n1820938503","loc":[-85.1136104,42.0848627]},"n1820938504":{"id":"n1820938504","loc":[-85.5828089,41.9480638]},"n1820938505":{"id":"n1820938505","loc":[-85.625514,41.9405202]},"n1820938506":{"id":"n1820938506","loc":[-85.2063384,42.0398322]},"n1820938507":{"id":"n1820938507","loc":[-85.3395476,41.9851636]},"n1820938508":{"id":"n1820938508","loc":[-85.0328853,42.0963606]},"n1820938510":{"id":"n1820938510","loc":[-85.1170369,42.0843702]},"n1820938511":{"id":"n1820938511","loc":[-85.2784748,41.9868487]},"n1820938512":{"id":"n1820938512","loc":[-85.6310501,41.9435528]},"n1820938514":{"id":"n1820938514","loc":[-85.0334284,42.0981028]},"n1820938515":{"id":"n1820938515","loc":[-84.9912091,42.0868226]},"n1820938516":{"id":"n1820938516","loc":[-85.2806141,41.9940351]},"n1820938517":{"id":"n1820938517","loc":[-85.1233025,42.0776734]},"n1820938518":{"id":"n1820938518","loc":[-85.2047891,42.0429023]},"n1820938519":{"id":"n1820938519","loc":[-85.1475443,42.0648312]},"n1820938520":{"id":"n1820938520","loc":[-85.2644685,41.9990891]},"n1820938521":{"id":"n1820938521","loc":[-85.1056281,42.0872553]},"n1820938522":{"id":"n1820938522","loc":[-85.4813184,41.9930105]},"n1820938523":{"id":"n1820938523","loc":[-85.321551,41.9722936]},"n1820938524":{"id":"n1820938524","loc":[-85.1564664,42.0631211]},"n1820938525":{"id":"n1820938525","loc":[-85.4149885,42.0079144]},"n1820938527":{"id":"n1820938527","loc":[-85.2861888,41.9803653]},"n1820938528":{"id":"n1820938528","loc":[-85.1301379,42.0682178]},"n1820938529":{"id":"n1820938529","loc":[-85.4156537,42.0084247]},"n1820938530":{"id":"n1820938530","loc":[-85.245151,42.0176082]},"n1820938531":{"id":"n1820938531","loc":[-85.457818,42.0001651]},"n1820938532":{"id":"n1820938532","loc":[-85.310951,41.9694538]},"n1820938533":{"id":"n1820938533","loc":[-85.1509089,42.0611298]},"n1820938534":{"id":"n1820938534","loc":[-85.1108249,42.086321]},"n1820938535":{"id":"n1820938535","loc":[-85.1260344,42.0740687]},"n1820938536":{"id":"n1820938536","loc":[-85.4561228,42.0042791]},"n1820938537":{"id":"n1820938537","loc":[-85.2805082,41.9945761]},"n1820938538":{"id":"n1820938538","loc":[-85.273352,41.9981921]},"n1820938539":{"id":"n1820938539","loc":[-85.1084216,42.0864364]},"n1820938540":{"id":"n1820938540","loc":[-85.5009737,41.9773637]},"n1820938541":{"id":"n1820938541","loc":[-85.3960843,42.0051879]},"n1820938542":{"id":"n1820938542","loc":[-85.3425088,41.9865034]},"n1820938545":{"id":"n1820938545","loc":[-84.9937907,42.0860849]},"n1820938546":{"id":"n1820938546","loc":[-85.1084176,42.086065]},"n1820938547":{"id":"n1820938547","loc":[-85.3492851,41.9924786]},"n1820938548":{"id":"n1820938548","loc":[-85.2512235,42.0101147]},"n1820938549":{"id":"n1820938549","loc":[-85.3717298,41.9979326]},"n1820938551":{"id":"n1820938551","loc":[-85.2573712,42.0064081]},"n1820938552":{"id":"n1820938552","loc":[-85.2514596,42.010139]},"n1820938553":{"id":"n1820938553","loc":[-85.416512,42.0088073]},"n1820938554":{"id":"n1820938554","loc":[-85.4365964,42.0061606]},"n1820938555":{"id":"n1820938555","loc":[-85.4552431,42.0057301]},"n1820938556":{"id":"n1820938556","loc":[-85.2916283,41.9778769]},"n1820938557":{"id":"n1820938557","loc":[-85.100709,42.0902968]},"n1820938558":{"id":"n1820938558","loc":[-85.4703064,41.9965771]},"n1820938559":{"id":"n1820938559","loc":[-85.3134722,41.9696134]},"n1820938560":{"id":"n1820938560","loc":[-85.4834213,41.9885768]},"n1820938561":{"id":"n1820938561","loc":[-85.2740641,41.9975236]},"n1820938562":{"id":"n1820938562","loc":[-85.148334,42.0623405]},"n1820938563":{"id":"n1820938563","loc":[-85.2358598,42.0263675]},"n1820938565":{"id":"n1820938565","loc":[-85.2902979,41.9790892]},"n1820938566":{"id":"n1820938566","loc":[-85.2528865,42.0112869]},"n1820938567":{"id":"n1820938567","loc":[-85.2595319,41.9973003]},"n1820938568":{"id":"n1820938568","loc":[-85.071151,42.105689]},"n1820938570":{"id":"n1820938570","loc":[-85.299278,41.9732188]},"n1820938571":{"id":"n1820938571","loc":[-85.0354669,42.1024771]},"n1820938583":{"id":"n1820938583","loc":[-85.3313937,41.972562]},"n1820938585":{"id":"n1820938585","loc":[-85.0756933,42.1058334]},"n1820938587":{"id":"n1820938587","loc":[-85.3130324,41.9694219]},"n1820938590":{"id":"n1820938590","loc":[-85.0934227,42.0931681]},"n1820938592":{"id":"n1820938592","loc":[-85.3517956,41.9922553]},"n1820938593":{"id":"n1820938593","loc":[-85.4023971,42.0065169]},"n1820938594":{"id":"n1820938594","loc":[-85.3506798,41.9925583]},"n1820938595":{"id":"n1820938595","loc":[-85.3673524,41.9971193]},"n1820938596":{"id":"n1820938596","loc":[-85.1073608,42.0853523]},"n1820938597":{"id":"n1820938597","loc":[-85.2976579,41.972477]},"n1820938598":{"id":"n1820938598","loc":[-85.5616517,41.9694295]},"n1820938599":{"id":"n1820938599","loc":[-85.3552074,41.9921915]},"n1820938600":{"id":"n1820938600","loc":[-85.4665126,41.9999953]},"n1820938601":{"id":"n1820938601","loc":[-85.2740695,41.9966226]},"n1820938602":{"id":"n1820938602","loc":[-85.279376,41.9886669]},"n1820938603":{"id":"n1820938603","loc":[-85.0771109,42.1040413]},"n1820938604":{"id":"n1820938604","loc":[-85.2636049,41.9977895]},"n1820938605":{"id":"n1820938605","loc":[-85.3762145,41.9976456]},"n1820938606":{"id":"n1820938606","loc":[-85.2321369,42.0289577]},"n1820938620":{"id":"n1820938620","loc":[-85.4947724,41.9776189]},"n1820938622":{"id":"n1820938622","loc":[-85.1547069,42.0622768]},"n1820938624":{"id":"n1820938624","loc":[-85.0005056,42.0880249]},"n1820938626":{"id":"n1820938626","loc":[-85.0735596,42.1059357]},"n1820938628":{"id":"n1820938628","loc":[-85.4665298,41.99932]},"n1820938629":{"id":"n1820938629","loc":[-85.434515,42.0065273]},"n1820938630":{"id":"n1820938630","loc":[-85.117462,42.0823823]},"n1820938631":{"id":"n1820938631","loc":[-85.0131777,42.0890707]},"n1820938632":{"id":"n1820938632","loc":[-85.0875326,42.0961934]},"n1820938634":{"id":"n1820938634","loc":[-85.6433839,41.9112042]},"n1820938635":{"id":"n1820938635","loc":[-85.1366181,42.064969]},"n1820938636":{"id":"n1820938636","loc":[-85.073109,42.1057925]},"n1820938638":{"id":"n1820938638","loc":[-85.161406,42.0632541]},"n1820938640":{"id":"n1820938640","loc":[-85.6343932,41.9188845]},"n1820938642":{"id":"n1820938642","loc":[-85.2500004,42.010306]},"n1820938644":{"id":"n1820938644","loc":[-85.291918,41.9753166]},"n1820938663":{"id":"n1820938663","loc":[-85.2841611,41.9916812]},"n1820938664":{"id":"n1820938664","loc":[-85.1052955,42.0868134]},"n1820938665":{"id":"n1820938665","loc":[-85.4606118,42.0005534]},"n1820938666":{"id":"n1820938666","loc":[-85.5672736,41.9642922]},"n1820938667":{"id":"n1820938667","loc":[-85.6348481,41.9316932]},"n1820938668":{"id":"n1820938668","loc":[-85.0224904,42.0909576]},"n1820938669":{"id":"n1820938669","loc":[-85.0133856,42.0899755]},"n1820938670":{"id":"n1820938670","loc":[-85.344779,41.991139]},"n1820938671":{"id":"n1820938671","loc":[-85.632874,41.9425313]},"n1820938673":{"id":"n1820938673","loc":[-85.4941501,41.9779698]},"n1820938675":{"id":"n1820938675","loc":[-85.0862559,42.0997519]},"n1820938676":{"id":"n1820938676","loc":[-85.0097874,42.0898032]},"n1820938678":{"id":"n1820938678","loc":[-84.9913553,42.0863675]},"n1820938680":{"id":"n1820938680","loc":[-85.0533666,42.1038315]},"n1820938682":{"id":"n1820938682","loc":[-85.2950294,41.9743914]},"n1820938684":{"id":"n1820938684","loc":[-85.2517385,42.0104499]},"n1820938686":{"id":"n1820938686","loc":[-85.0247971,42.0922514]},"n1820938688":{"id":"n1820938688","loc":[-85.0807037,42.1026017]},"n1820938690":{"id":"n1820938690","loc":[-85.52462,41.9722748]},"n1820938694":{"id":"n1820938694","loc":[-85.2586535,41.9988818]},"n1820938695":{"id":"n1820938695","loc":[-85.0931612,42.092948]},"n1820938697":{"id":"n1820938697","loc":[-85.2470822,42.016564]},"n1820938698":{"id":"n1820938698","loc":[-85.4143018,42.0075158]},"n1820938699":{"id":"n1820938699","loc":[-85.0771484,42.104487]},"n1820938700":{"id":"n1820938700","loc":[-85.0291208,42.0942775]},"n1820938701":{"id":"n1820938701","loc":[-85.6367964,41.9185971]},"n1820938702":{"id":"n1820938702","loc":[-85.085419,42.1010693]},"n1820938703":{"id":"n1820938703","loc":[-85.0583877,42.1040584]},"n1820938705":{"id":"n1820938705","loc":[-85.2573379,42.0003182]},"n1820938706":{"id":"n1820938706","loc":[-85.2655937,41.9981575]},"n1820938707":{"id":"n1820938707","loc":[-85.023181,42.0915758]},"n1820938708":{"id":"n1820938708","loc":[-85.2318687,42.0274674]},"n1820938709":{"id":"n1820938709","loc":[-85.1056389,42.0866184]},"n1820938710":{"id":"n1820938710","loc":[-85.5276265,41.9700978]},"n1820938711":{"id":"n1820938711","loc":[-85.0864128,42.0945761]},"n1820938712":{"id":"n1820938712","loc":[-84.9897071,42.0871888]},"n1820938714":{"id":"n1820938714","loc":[-85.1328845,42.0665611]},"n1820938715":{"id":"n1820938715","loc":[-85.0336537,42.0991377]},"n1820938716":{"id":"n1820938716","loc":[-85.087597,42.0986692]},"n1820938717":{"id":"n1820938717","loc":[-85.1241394,42.0761882]},"n1820938718":{"id":"n1820938718","loc":[-85.1176002,42.0847723]},"n1820938719":{"id":"n1820938719","loc":[-85.2423615,42.0216572]},"n1820938721":{"id":"n1820938721","loc":[-85.2196378,42.0387908]},"n1820938722":{"id":"n1820938722","loc":[-85.0164272,42.0890082]},"n1820938723":{"id":"n1820938723","loc":[-85.5917182,41.9451807]},"n1820938724":{"id":"n1820938724","loc":[-85.2458806,42.0086638]},"n1820938725":{"id":"n1820938725","loc":[-85.1264474,42.0740527]},"n1820938726":{"id":"n1820938726","loc":[-85.1961631,42.04738]},"n1820938727":{"id":"n1820938727","loc":[-85.2784643,41.9943648]},"n1820938728":{"id":"n1820938728","loc":[-85.2905554,41.9763216]},"n1820938729":{"id":"n1820938729","loc":[-85.2913386,41.9771511]},"n1820938730":{"id":"n1820938730","loc":[-85.0112519,42.0895683]},"n1820938732":{"id":"n1820938732","loc":[-85.4290261,42.0064531]},"n1820938733":{"id":"n1820938733","loc":[-85.3867073,42.0031629]},"n1820938734":{"id":"n1820938734","loc":[-85.4943647,41.9836005]},"n1820938735":{"id":"n1820938735","loc":[-85.4900303,41.9860728]},"n1820938736":{"id":"n1820938736","loc":[-85.0866153,42.0944539]},"n1820938737":{"id":"n1820938737","loc":[-85.0869532,42.0990911]},"n1820938738":{"id":"n1820938738","loc":[-85.6321659,41.9208851]},"n1820938739":{"id":"n1820938739","loc":[-85.5930485,41.9433453]},"n1820938740":{"id":"n1820938740","loc":[-85.0406851,42.102733]},"n1820938741":{"id":"n1820938741","loc":[-85.1051131,42.0869846]},"n1820938742":{"id":"n1820938742","loc":[-85.1377554,42.0648893]},"n1820938743":{"id":"n1820938743","loc":[-85.2795694,41.994604]},"n1820938745":{"id":"n1820938745","loc":[-85.4948153,41.9826594]},"n1820938746":{"id":"n1820938746","loc":[-85.4488916,42.0050923]},"n1820938747":{"id":"n1820938747","loc":[-85.1052526,42.0866144]},"n1820938748":{"id":"n1820938748","loc":[-85.1468749,42.0653991]},"n1820938749":{"id":"n1820938749","loc":[-85.0856886,42.1006104]},"n1820938750":{"id":"n1820938750","loc":[-85.2144022,42.0404004]},"n1820938751":{"id":"n1820938751","loc":[-85.277771,41.9907458]},"n1820938752":{"id":"n1820938752","loc":[-85.1474542,42.0636149]},"n1820938753":{"id":"n1820938753","loc":[-85.0820515,42.1028075]},"n1820938754":{"id":"n1820938754","loc":[-85.1122948,42.08525]},"n1820938756":{"id":"n1820938756","loc":[-85.0173352,42.0901933]},"n1820938757":{"id":"n1820938757","loc":[-85.2259721,42.0354018]},"n1820938758":{"id":"n1820938758","loc":[-85.0872389,42.0987795]},"n1820938759":{"id":"n1820938759","loc":[-85.2291436,42.031874]},"n1820938760":{"id":"n1820938760","loc":[-85.3802485,42.0016002]},"n1820938761":{"id":"n1820938761","loc":[-85.3945822,42.0057938]},"n1820938762":{"id":"n1820938762","loc":[-85.5273237,41.9713017]},"n1820938763":{"id":"n1820938763","loc":[-85.2868862,41.9798629]},"n1820938764":{"id":"n1820938764","loc":[-85.2516677,42.0107899]},"n1820938766":{"id":"n1820938766","loc":[-85.3183002,41.9693103]},"n1820938768":{"id":"n1820938768","loc":[-85.2159042,42.0401932]},"n1820938770":{"id":"n1820938770","loc":[-85.0094481,42.0911141]},"n1820938771":{"id":"n1820938771","loc":[-85.0244538,42.0922155]},"n1820938772":{"id":"n1820938772","loc":[-85.231697,42.028862]},"n1820938773":{"id":"n1820938773","loc":[-85.2102394,42.0390617]},"n1820938774":{"id":"n1820938774","loc":[-85.2463419,42.0151212]},"n1820938775":{"id":"n1820938775","loc":[-85.0726195,42.1056424]},"n1820938776":{"id":"n1820938776","loc":[-85.0060431,42.0883262]},"n1820938778":{"id":"n1820938778","loc":[-85.425889,42.0056982]},"n1820938779":{"id":"n1820938779","loc":[-85.1183042,42.0820638]},"n1820938780":{"id":"n1820938780","loc":[-85.441596,42.0058257]},"n1820938781":{"id":"n1820938781","loc":[-85.1124879,42.0847086]},"n1820938782":{"id":"n1820938782","loc":[-85.2452733,42.0153894]},"n1820938783":{"id":"n1820938783","loc":[-85.2741191,41.9969244]},"n1820938784":{"id":"n1820938784","loc":[-85.2829487,41.9822236]},"n1820938785":{"id":"n1820938785","loc":[-85.3202743,41.972142]},"n1820938786":{"id":"n1820938786","loc":[-85.2345402,42.0266465]},"n1820938787":{"id":"n1820938787","loc":[-85.3037626,41.9724611]},"n1820938789":{"id":"n1820938789","loc":[-85.2474792,42.0161973]},"n1820938790":{"id":"n1820938790","loc":[-85.2951045,41.9727323]},"n1820938791":{"id":"n1820938791","loc":[-85.322345,41.9712726]},"n1820938792":{"id":"n1820938792","loc":[-85.2402372,42.0110394]},"n1820938793":{"id":"n1820938793","loc":[-85.5135693,41.9698659]},"n1820938794":{"id":"n1820938794","loc":[-85.4695339,41.9967366]},"n1820938796":{"id":"n1820938796","loc":[-85.0418492,42.1011131]},"n1820938797":{"id":"n1820938797","loc":[-85.3334107,41.9806337]},"n1820938798":{"id":"n1820938798","loc":[-85.5625314,41.9711685]},"n1820938799":{"id":"n1820938799","loc":[-85.3755707,41.9973585]},"n1820938800":{"id":"n1820938800","loc":[-85.5227532,41.9722429]},"n1820938801":{"id":"n1820938801","loc":[-85.4267687,42.0052836]},"n1820938803":{"id":"n1820938803","loc":[-85.0284704,42.0940837]},"n1820938804":{"id":"n1820938804","loc":[-85.015585,42.0885305]},"n1820938805":{"id":"n1820938805","loc":[-85.0765905,42.1053865]},"n1820938806":{"id":"n1820938806","loc":[-85.2614953,41.9964551]},"n1820938808":{"id":"n1820938808","loc":[-85.0307355,42.0947313]},"n1820938810":{"id":"n1820938810","loc":[-85.3894753,42.0003565]},"n1820938812":{"id":"n1820938812","loc":[-85.0868848,42.095006]},"n1820938813":{"id":"n1820938813","loc":[-85.3854198,42.0009465]},"n1820938814":{"id":"n1820938814","loc":[-85.2659692,41.9993534]},"n1820938815":{"id":"n1820938815","loc":[-85.1234259,42.0765266]},"n1820938816":{"id":"n1820938816","loc":[-85.1426906,42.0648893]},"n1820938818":{"id":"n1820938818","loc":[-85.1014533,42.0893067]},"n1820938819":{"id":"n1820938819","loc":[-85.0883064,42.098067]},"n1820938820":{"id":"n1820938820","loc":[-85.0503156,42.102704]},"n1820938821":{"id":"n1820938821","loc":[-85.1179649,42.0821884]},"n1820938822":{"id":"n1820938822","loc":[-85.3484697,41.9921596]},"n1820938823":{"id":"n1820938823","loc":[-85.3732962,41.9970874]},"n1820938824":{"id":"n1820938824","loc":[-85.2784104,41.9898312]},"n1820938825":{"id":"n1820938825","loc":[-85.4441709,42.0052198]},"n1820938826":{"id":"n1820938826","loc":[-85.3925438,42.0038326]},"n1820938829":{"id":"n1820938829","loc":[-85.5717582,41.9621861]},"n1820938830":{"id":"n1820938830","loc":[-85.0866314,42.0995051]},"n1820938831":{"id":"n1820938831","loc":[-85.576672,41.9522769]},"n1820938832":{"id":"n1820938832","loc":[-85.1587238,42.0636205]},"n1820938833":{"id":"n1820938833","loc":[-85.3804245,41.9999155]},"n1820938834":{"id":"n1820938834","loc":[-85.280083,41.9948843]},"n1820938836":{"id":"n1820938836","loc":[-85.561892,41.9686693]},"n1820938837":{"id":"n1820938837","loc":[-85.0158975,42.0885253]},"n1820938838":{"id":"n1820938838","loc":[-85.4248204,42.007633]},"n1820938839":{"id":"n1820938839","loc":[-85.0352738,42.1039657]},"n1820938840":{"id":"n1820938840","loc":[-85.211956,42.0411812]},"n1820938841":{"id":"n1820938841","loc":[-85.4816575,41.9908997]},"n1820938842":{"id":"n1820938842","loc":[-85.3807635,42.0020308]},"n1820938843":{"id":"n1820938843","loc":[-85.0100865,42.0898521]},"n1820938844":{"id":"n1820938844","loc":[-85.0103936,42.0897434]},"n1820938848":{"id":"n1820938848","loc":[-85.2430052,42.0131363]},"n1820938849":{"id":"n1820938849","loc":[-85.112559,42.0853723]},"n1820938851":{"id":"n1820938851","loc":[-85.3641553,41.9952535]},"n1820938852":{"id":"n1820938852","loc":[-85.2087373,42.0390777]},"n1820938853":{"id":"n1820938853","loc":[-85.2473933,42.0148263]},"n1820938854":{"id":"n1820938854","loc":[-85.0213464,42.090509]},"n1820938855":{"id":"n1820938855","loc":[-85.0673208,42.1052353]},"n1820938856":{"id":"n1820938856","loc":[-85.1003053,42.0905528]},"n1820938857":{"id":"n1820938857","loc":[-85.2617367,41.9965389]},"n1820938858":{"id":"n1820938858","loc":[-85.280363,41.9916015]},"n1820938859":{"id":"n1820938859","loc":[-85.0038866,42.0873469]},"n1820938860":{"id":"n1820938860","loc":[-85.2476401,42.0151451]},"n1820938861":{"id":"n1820938861","loc":[-85.193717,42.0499294]},"n1820938862":{"id":"n1820938862","loc":[-85.3478689,41.9917609]},"n1820938863":{"id":"n1820938863","loc":[-85.5638017,41.9648881]},"n1820938864":{"id":"n1820938864","loc":[-85.4356308,42.0064476]},"n1820938865":{"id":"n1820938865","loc":[-85.0561722,42.1023509]},"n1820938867":{"id":"n1820938867","loc":[-85.2256031,42.0356034]},"n1820938868":{"id":"n1820938868","loc":[-85.6102576,41.9420844]},"n1820938869":{"id":"n1820938869","loc":[-85.2285213,42.0339938]},"n1820938870":{"id":"n1820938870","loc":[-85.0326238,42.0978003]},"n1820938871":{"id":"n1820938871","loc":[-85.0131389,42.0903736]},"n1820938872":{"id":"n1820938872","loc":[-85.2550859,42.0012259]},"n1820938873":{"id":"n1820938873","loc":[-85.1130029,42.0846966]},"n1820938874":{"id":"n1820938874","loc":[-85.1579041,42.06336]},"n1820938875":{"id":"n1820938875","loc":[-85.0430522,42.1020234]},"n1820938876":{"id":"n1820938876","loc":[-85.2786679,41.9865935]},"n1820938877":{"id":"n1820938877","loc":[-85.1221666,42.0788706]},"n1820938878":{"id":"n1820938878","loc":[-85.2554614,42.0103303]},"n1820938879":{"id":"n1820938879","loc":[-85.2349801,42.0265748]},"n1820938880":{"id":"n1820938880","loc":[-85.0997434,42.0907864]},"n1820938881":{"id":"n1820938881","loc":[-85.0045464,42.0878167]},"n1820938882":{"id":"n1820938882","loc":[-85.2728048,41.9982519]},"n1820938883":{"id":"n1820938883","loc":[-85.3111333,41.9691587]},"n1820938884":{"id":"n1820938884","loc":[-85.3219802,41.9721899]},"n1820938885":{"id":"n1820938885","loc":[-85.3091378,41.9699325]},"n1820938887":{"id":"n1820938887","loc":[-85.4242367,42.0085203]},"n1820938888":{"id":"n1820938888","loc":[-84.9968377,42.0874504]},"n1820938890":{"id":"n1820938890","loc":[-85.5443139,41.9714078]},"n1820938891":{"id":"n1820938891","loc":[-85.6404013,41.9154676]},"n1820938892":{"id":"n1820938892","loc":[-85.3644986,41.9962582]},"n1820938893":{"id":"n1820938893","loc":[-85.0496772,42.1018323]},"n1820938894":{"id":"n1820938894","loc":[-85.297261,41.9737373]},"n1820938895":{"id":"n1820938895","loc":[-85.0327096,42.098071]},"n1820938896":{"id":"n1820938896","loc":[-85.3856773,41.9996867]},"n1820938897":{"id":"n1820938897","loc":[-85.0493862,42.1015509]},"n1820938898":{"id":"n1820938898","loc":[-84.9969879,42.0876614]},"n1820938899":{"id":"n1820938899","loc":[-85.0848625,42.1013587]},"n1820938900":{"id":"n1820938900","loc":[-85.5853195,41.9479201]},"n1820938901":{"id":"n1820938901","loc":[-85.6329169,41.9387964]},"n1820938902":{"id":"n1820938902","loc":[-85.0843046,42.1029468]},"n1820938903":{"id":"n1820938903","loc":[-85.1228747,42.0778474]},"n1820938904":{"id":"n1820938904","loc":[-85.4855456,41.984095]},"n1820938905":{"id":"n1820938905","loc":[-85.0573269,42.1026801]},"n1820938906":{"id":"n1820938906","loc":[-85.2425868,42.0131523]},"n1820938907":{"id":"n1820938907","loc":[-85.1149622,42.0860053]},"n1820938908":{"id":"n1820938908","loc":[-85.4833097,41.9951578]},"n1820938909":{"id":"n1820938909","loc":[-85.075979,42.1056372]},"n1820938910":{"id":"n1820938910","loc":[-85.0338509,42.0977139]},"n1820938911":{"id":"n1820938911","loc":[-85.6384272,41.9115715]},"n1820938912":{"id":"n1820938912","loc":[-85.0458363,42.1004074]},"n1820938913":{"id":"n1820938913","loc":[-85.0592138,42.1048305]},"n1820938914":{"id":"n1820938914","loc":[-85.2807493,41.9916653]},"n1820938915":{"id":"n1820938915","loc":[-85.1103274,42.0864193]},"n1820938916":{"id":"n1820938916","loc":[-85.6267156,41.9404404]},"n1820938918":{"id":"n1820938918","loc":[-85.0331374,42.0982911]},"n1820938919":{"id":"n1820938919","loc":[-85.5637331,41.965409]},"n1820938920":{"id":"n1820938920","loc":[-85.5457515,41.9714237]},"n1820938922":{"id":"n1820938922","loc":[-85.082073,42.1030104]},"n1820938923":{"id":"n1820938923","loc":[-85.0780765,42.103102]},"n1820938924":{"id":"n1820938924","loc":[-85.4208035,42.0089508]},"n1820938925":{"id":"n1820938925","loc":[-85.3469934,41.9914795]},"n1820938926":{"id":"n1820938926","loc":[-85.0322,42.095619]},"n1820938927":{"id":"n1820938927","loc":[-85.4784431,41.9949401]},"n1820938928":{"id":"n1820938928","loc":[-85.1303095,42.0667523]},"n1820938929":{"id":"n1820938929","loc":[-85.2463784,42.0084781]},"n1820938930":{"id":"n1820938930","loc":[-85.6299986,41.9427707]},"n1820938931":{"id":"n1820938931","loc":[-85.6325907,41.9238499]},"n1820938932":{"id":"n1820938932","loc":[-85.4808464,41.9914476]},"n1820938934":{"id":"n1820938934","loc":[-85.2411599,42.0105292]},"n1820938935":{"id":"n1820938935","loc":[-85.0163213,42.0892379]},"n1820938936":{"id":"n1820938936","loc":[-85.3290934,41.9682322]},"n1820938937":{"id":"n1820938937","loc":[-85.4925623,41.9853231]},"n1820938938":{"id":"n1820938938","loc":[-85.0338294,42.09892]},"n1820938940":{"id":"n1820938940","loc":[-85.4174561,42.008903]},"n1820938941":{"id":"n1820938941","loc":[-85.1165595,42.0838845]},"n1820938942":{"id":"n1820938942","loc":[-85.2954585,41.9717192]},"n1820938943":{"id":"n1820938943","loc":[-85.6330199,41.9257338]},"n1820938944":{"id":"n1820938944","loc":[-85.2294654,42.0324478]},"n1820938945":{"id":"n1820938945","loc":[-85.5601282,41.9728914]},"n1820938946":{"id":"n1820938946","loc":[-85.1176324,42.08568]},"n1820938947":{"id":"n1820938947","loc":[-85.0210245,42.0906005]},"n1820938948":{"id":"n1820938948","loc":[-85.0251887,42.09253]},"n1820938949":{"id":"n1820938949","loc":[-85.0895832,42.0939551]},"n1820938950":{"id":"n1820938950","loc":[-84.9915109,42.085842]},"n1820938951":{"id":"n1820938951","loc":[-85.2187366,42.0393486]},"n1820938952":{"id":"n1820938952","loc":[-85.006605,42.087579]},"n1820938953":{"id":"n1820938953","loc":[-85.046641,42.1012393]},"n1820938954":{"id":"n1820938954","loc":[-85.052102,42.103695]},"n1820938955":{"id":"n1820938955","loc":[-85.283925,41.9912825]},"n1820938956":{"id":"n1820938956","loc":[-85.2326626,42.0316349]},"n1820938957":{"id":"n1820938957","loc":[-85.1174298,42.0859694]},"n1820938958":{"id":"n1820938958","loc":[-85.3802056,41.9994794]},"n1820938959":{"id":"n1820938959","loc":[-85.4586334,41.9999737]},"n1820938960":{"id":"n1820938960","loc":[-85.4302234,42.0069418]},"n1820938961":{"id":"n1820938961","loc":[-85.092201,42.0930674]},"n1820938962":{"id":"n1820938962","loc":[-85.3684511,41.9979382]},"n1820938963":{"id":"n1820938963","loc":[-85.4618735,42.0011856]},"n1820938964":{"id":"n1820938964","loc":[-85.4828205,41.9877793]},"n1820938965":{"id":"n1820938965","loc":[-85.0837789,42.1025726]},"n1820938966":{"id":"n1820938966","loc":[-85.0176195,42.090253]},"n1820938967":{"id":"n1820938967","loc":[-85.3801627,42.001074]},"n1820938968":{"id":"n1820938968","loc":[-85.4767007,41.994488]},"n1820938969":{"id":"n1820938969","loc":[-85.274268,41.9957495]},"n1820938970":{"id":"n1820938970","loc":[-85.2977438,41.9719506]},"n1820938971":{"id":"n1820938971","loc":[-85.2425546,42.0208682]},"n1820938972":{"id":"n1820938972","loc":[-85.2557082,42.002382]},"n1820938973":{"id":"n1820938973","loc":[-85.3187937,41.9691986]},"n1820938975":{"id":"n1820938975","loc":[-85.2448077,42.0153045]},"n1820938977":{"id":"n1820938977","loc":[-85.0343015,42.0997718]},"n1820938978":{"id":"n1820938978","loc":[-85.2449364,42.01874]},"n1820938979":{"id":"n1820938979","loc":[-85.2598391,41.9969602]},"n1820938980":{"id":"n1820938980","loc":[-85.4294724,42.0067665]},"n1820938981":{"id":"n1820938981","loc":[-85.428082,42.0055124]},"n1820938983":{"id":"n1820938983","loc":[-85.5436315,41.9717484]},"n1820938985":{"id":"n1820938985","loc":[-85.5978336,41.9407437]},"n1820938986":{"id":"n1820938986","loc":[-85.491661,41.9860249]},"n1820938987":{"id":"n1820938987","loc":[-85.4942789,41.9801392]},"n1820938988":{"id":"n1820938988","loc":[-85.0416306,42.1010841]},"n1820938989":{"id":"n1820938989","loc":[-85.2653644,41.9984433]},"n1820938990":{"id":"n1820938990","loc":[-85.1028266,42.0881124]},"n1820938991":{"id":"n1820938991","loc":[-85.0163146,42.0887932]},"n1820938992":{"id":"n1820938992","loc":[-85.5282209,41.9678112]},"n1820938993":{"id":"n1820938993","loc":[-85.5442752,41.9715888]},"n1820938994":{"id":"n1820938994","loc":[-85.5634327,41.9658558]},"n1820938995":{"id":"n1820938995","loc":[-85.0384227,42.1037627]},"n1820938996":{"id":"n1820938996","loc":[-85.1144258,42.0854439]},"n1820938997":{"id":"n1820938997","loc":[-85.1870651,42.0506305]},"n1820938998":{"id":"n1820938998","loc":[-85.1256159,42.0747376]},"n1820938999":{"id":"n1820938999","loc":[-85.3272695,41.9715836]},"n1820939000":{"id":"n1820939000","loc":[-85.0543067,42.103098]},"n1820939001":{"id":"n1820939001","loc":[-85.4678173,41.9973585]},"n1820939003":{"id":"n1820939003","loc":[-85.0266626,42.0933154]},"n1820939004":{"id":"n1820939004","loc":[-85.0353046,42.1019728]},"n1820939005":{"id":"n1820939005","loc":[-85.1237961,42.0762798]},"n1820939006":{"id":"n1820939006","loc":[-85.2812214,41.9826702]},"n1820939007":{"id":"n1820939007","loc":[-85.2927763,41.9747343]},"n1820939008":{"id":"n1820939008","loc":[-85.3270979,41.9720862]},"n1820939009":{"id":"n1820939009","loc":[-85.488657,41.9856581]},"n1820939010":{"id":"n1820939010","loc":[-85.3087301,41.9701399]},"n1820939011":{"id":"n1820939011","loc":[-85.0276939,42.093768]},"n1820939012":{"id":"n1820939012","loc":[-85.2956516,41.9748779]},"n1820939013":{"id":"n1820939013","loc":[-85.1298579,42.0726443]},"n1820939014":{"id":"n1820939014","loc":[-85.105144,42.0870893]},"n1820939015":{"id":"n1820939015","loc":[-85.0677486,42.1053917]},"n1820939016":{"id":"n1820939016","loc":[-85.0333681,42.0993459]},"n1820939017":{"id":"n1820939017","loc":[-85.6384272,41.910805]},"n1820939018":{"id":"n1820939018","loc":[-85.399496,42.006894]},"n1820939019":{"id":"n1820939019","loc":[-85.2648427,41.9998318]},"n1820939020":{"id":"n1820939020","loc":[-85.1237424,42.0766779]},"n1820939021":{"id":"n1820939021","loc":[-85.2515025,42.0109442]},"n1820939022":{"id":"n1820939022","loc":[-85.5566306,41.9718385]},"n1820939023":{"id":"n1820939023","loc":[-85.090644,42.0938369]},"n1820939024":{"id":"n1820939024","loc":[-85.1245525,42.074914]},"n1820939025":{"id":"n1820939025","loc":[-85.1099934,42.0863926]},"n1820939026":{"id":"n1820939026","loc":[-85.1251653,42.0744589]},"n1820939027":{"id":"n1820939027","loc":[-85.401792,42.0068143]},"n1820939028":{"id":"n1820939028","loc":[-85.0094763,42.0899584]},"n1820939029":{"id":"n1820939029","loc":[-85.1330779,42.0705605]},"n1820939030":{"id":"n1820939030","loc":[-85.4935064,41.984398]},"n1820939031":{"id":"n1820939031","loc":[-85.5713334,41.9613939]},"n1820939032":{"id":"n1820939032","loc":[-85.0873945,42.0964669]},"n1820939033":{"id":"n1820939033","loc":[-85.0886497,42.0986481]},"n1820939034":{"id":"n1820939034","loc":[-85.3276343,41.9758897]},"n1820939035":{"id":"n1820939035","loc":[-85.1304386,42.0727387]},"n1820939036":{"id":"n1820939036","loc":[-85.2551932,42.0052999]},"n1820939037":{"id":"n1820939037","loc":[-85.2206936,42.0384458]},"n1820939038":{"id":"n1820939038","loc":[-85.2313645,42.0286389]},"n1820939039":{"id":"n1820939039","loc":[-85.0754586,42.1059835]},"n1820939040":{"id":"n1820939040","loc":[-85.0663324,42.1050812]},"n1820939041":{"id":"n1820939041","loc":[-85.2406234,42.0106887]},"n1820939042":{"id":"n1820939042","loc":[-85.0685962,42.1058175]},"n1820939043":{"id":"n1820939043","loc":[-85.0689462,42.1059437]},"n1820939044":{"id":"n1820939044","loc":[-85.0586144,42.1046144]},"n1820939045":{"id":"n1820939045","loc":[-85.3650565,41.9965452]},"n1820939047":{"id":"n1820939047","loc":[-85.5752558,41.9536014]},"n1820939048":{"id":"n1820939048","loc":[-85.5110159,41.9710624]},"n1820939050":{"id":"n1820939050","loc":[-85.2832641,41.9926477]},"n1820939051":{"id":"n1820939051","loc":[-85.0078402,42.0898947]},"n1820939052":{"id":"n1820939052","loc":[-85.3882737,42.0017916]},"n1820939053":{"id":"n1820939053","loc":[-85.1718945,42.0564937]},"n1820939054":{"id":"n1820939054","loc":[-85.0947048,42.0929293]},"n1820939055":{"id":"n1820939055","loc":[-85.4456944,42.0051082]},"n1820939056":{"id":"n1820939056","loc":[-85.3139872,41.9706903]},"n1820939057":{"id":"n1820939057","loc":[-85.3893895,42.0034021]},"n1820939058":{"id":"n1820939058","loc":[-85.2425332,42.0106089]},"n1820939059":{"id":"n1820939059","loc":[-85.6085624,41.9420844]},"n1820939060":{"id":"n1820939060","loc":[-85.210411,42.0397789]},"n1820939061":{"id":"n1820939061","loc":[-85.2762542,41.9960473]},"n1820939062":{"id":"n1820939062","loc":[-85.4686584,41.9969973]},"n1820939063":{"id":"n1820939063","loc":[-85.3860421,42.0018394]},"n1820939064":{"id":"n1820939064","loc":[-85.5636944,41.9644414]},"n1820939065":{"id":"n1820939065","loc":[-85.3267331,41.9766554]},"n1820939066":{"id":"n1820939066","loc":[-85.0868996,42.0943822]},"n1820939067":{"id":"n1820939067","loc":[-85.104861,42.0880038]},"n1820939068":{"id":"n1820939068","loc":[-85.5537123,41.9695093]},"n1820939069":{"id":"n1820939069","loc":[-85.6325092,41.9396743]},"n1820939070":{"id":"n1820939070","loc":[-85.3869648,42.0024454]},"n1820939071":{"id":"n1820939071","loc":[-85.2775349,41.9957335]},"n1820939072":{"id":"n1820939072","loc":[-85.2055616,42.0421533]},"n1820939073":{"id":"n1820939073","loc":[-85.4731431,41.9946531]},"n1820939074":{"id":"n1820939074","loc":[-85.0399609,42.1030833]},"n1820939075":{"id":"n1820939075","loc":[-85.3055758,41.9725169]},"n1820939076":{"id":"n1820939076","loc":[-85.4834599,41.994488]},"n1820939077":{"id":"n1820939077","loc":[-85.3819866,42.0023018]},"n1820939078":{"id":"n1820939078","loc":[-85.1218756,42.0789992]},"n1820939079":{"id":"n1820939079","loc":[-85.2793159,41.9944458]},"n1820939080":{"id":"n1820939080","loc":[-85.2495498,42.0101466]},"n1820939081":{"id":"n1820939081","loc":[-85.0035969,42.0872434]},"n1820939082":{"id":"n1820939082","loc":[-85.1054243,42.0865626]},"n1820939083":{"id":"n1820939083","loc":[-85.0917665,42.0934774]},"n1820939084":{"id":"n1820939084","loc":[-85.3442211,41.988938]},"n1820939086":{"id":"n1820939086","loc":[-85.273989,41.9953588]},"n1820939087":{"id":"n1820939087","loc":[-85.1142541,42.0852488]},"n1820939089":{"id":"n1820939089","loc":[-85.1526684,42.0615758]},"n1820939090":{"id":"n1820939090","loc":[-85.2538843,42.0110159]},"n1820939091":{"id":"n1820939091","loc":[-85.28341,41.9909635]},"n1820939092":{"id":"n1820939092","loc":[-85.3963178,42.0050217]},"n1820939093":{"id":"n1820939093","loc":[-85.0851682,42.1012472]},"n1820939095":{"id":"n1820939095","loc":[-85.2811784,41.986243]},"n1820939096":{"id":"n1820939096","loc":[-85.4274125,42.0052995]},"n1820939097":{"id":"n1820939097","loc":[-85.0871262,42.0951652]},"n1820939099":{"id":"n1820939099","loc":[-85.1314253,42.0671665]},"n1820939100":{"id":"n1820939100","loc":[-85.2778997,41.991001]},"n1820939101":{"id":"n1820939101","loc":[-85.112107,42.0862812]},"n1820939102":{"id":"n1820939102","loc":[-85.299911,41.9729955]},"n1820939103":{"id":"n1820939103","loc":[-85.639822,41.9094796]},"n1820939104":{"id":"n1820939104","loc":[-85.122294,42.0785334]},"n1820939105":{"id":"n1820939105","loc":[-85.2476294,42.015719]},"n1820939106":{"id":"n1820939106","loc":[-85.4946007,41.9814631]},"n1820939107":{"id":"n1820939107","loc":[-85.0879524,42.0986919]},"n1820939108":{"id":"n1820939108","loc":[-85.0342814,42.098274]},"n1820939109":{"id":"n1820939109","loc":[-85.2450695,42.0095463]},"n1820939110":{"id":"n1820939110","loc":[-85.3847546,42.0024135]},"n1820939111":{"id":"n1820939111","loc":[-85.2961344,41.9742558]},"n1820939112":{"id":"n1820939112","loc":[-85.27899,41.994317]},"n1820939114":{"id":"n1820939114","loc":[-85.1017644,42.0886618]},"n1820939115":{"id":"n1820939115","loc":[-85.076215,42.1056333]},"n1820939116":{"id":"n1820939116","loc":[-85.1198009,42.0805349]},"n1820939117":{"id":"n1820939117","loc":[-85.11988,42.0798513]},"n1820939118":{"id":"n1820939118","loc":[-85.147819,42.0625476]},"n1820939119":{"id":"n1820939119","loc":[-85.0585969,42.1029042]},"n1820939120":{"id":"n1820939120","loc":[-85.1248596,42.0745744]},"n1820939121":{"id":"n1820939121","loc":[-85.3023786,41.9725249]},"n1820939123":{"id":"n1820939123","loc":[-85.0119332,42.0900699]},"n1820939124":{"id":"n1820939124","loc":[-85.2466852,42.0170343]},"n1820939125":{"id":"n1820939125","loc":[-85.0033019,42.0872792]},"n1820939126":{"id":"n1820939126","loc":[-85.0042084,42.0875778]},"n1820939128":{"id":"n1820939128","loc":[-85.0052961,42.0885424]},"n1820939130":{"id":"n1820939130","loc":[-85.0647942,42.10508]},"n1820939131":{"id":"n1820939131","loc":[-85.2824123,41.9825107]},"n1820939132":{"id":"n1820939132","loc":[-85.3210039,41.9723255]},"n1820939133":{"id":"n1820939133","loc":[-85.0491033,42.1014184]},"n1820939134":{"id":"n1820939134","loc":[-85.1127776,42.0855168]},"n1820939135":{"id":"n1820939135","loc":[-85.1243768,42.0759322]},"n1820939137":{"id":"n1820939137","loc":[-85.125974,42.0747547]},"n1820939138":{"id":"n1820939138","loc":[-85.1071248,42.0859973]},"n1820939139":{"id":"n1820939139","loc":[-85.5326175,41.9674833]},"n1820939140":{"id":"n1820939140","loc":[-85.1338715,42.0660833]},"n1820939142":{"id":"n1820939142","loc":[-85.649671,41.9135675]},"n1820939144":{"id":"n1820939144","loc":[-85.0236545,42.0920444]},"n1820939145":{"id":"n1820939145","loc":[-85.1084391,42.0859376]},"n1820939146":{"id":"n1820939146","loc":[-85.1539988,42.0618626]},"n1820939147":{"id":"n1820939147","loc":[-85.2354521,42.026511]},"n1820939148":{"id":"n1820939148","loc":[-85.2362246,42.0260408]},"n1820939149":{"id":"n1820939149","loc":[-85.2401342,42.0115233]},"n1820939150":{"id":"n1820939150","loc":[-85.295319,41.9747423]},"n1820939151":{"id":"n1820939151","loc":[-85.1164696,42.0835409]},"n1820939152":{"id":"n1820939152","loc":[-85.3232891,41.9712885]},"n1820939153":{"id":"n1820939153","loc":[-85.2574463,42.0068944]},"n1820939155":{"id":"n1820939155","loc":[-85.5704064,41.9598246]},"n1820939156":{"id":"n1820939156","loc":[-85.0349077,42.0998116]},"n1820939157":{"id":"n1820939157","loc":[-85.0949529,42.0925619]},"n1820939159":{"id":"n1820939159","loc":[-85.0179829,42.0902343]},"n1820939160":{"id":"n1820939160","loc":[-85.0405832,42.1016942]},"n1820939161":{"id":"n1820939161","loc":[-85.2534015,42.0111833]},"n1820939162":{"id":"n1820939162","loc":[-85.0839881,42.102708]},"n1820939163":{"id":"n1820939163","loc":[-85.0341996,42.1008385]},"n1820939164":{"id":"n1820939164","loc":[-85.1037761,42.0879731]},"n1820939173":{"id":"n1820939173","loc":[-85.0460616,42.1005786]},"n1820939177":{"id":"n1820939177","loc":[-85.0061651,42.0878059]},"n1820939181":{"id":"n1820939181","loc":[-85.1456775,42.0654684]},"n1820939183":{"id":"n1820939183","loc":[-85.1325508,42.0718439]},"n1820939185":{"id":"n1820939185","loc":[-85.2485842,42.008329]},"n1820939187":{"id":"n1820939187","loc":[-85.2744128,41.9949322]},"n1820939189":{"id":"n1820939189","loc":[-85.2579025,41.9999542]},"n1820939191":{"id":"n1820939191","loc":[-85.3358998,41.9828987]},"n1820939193":{"id":"n1820939193","loc":[-85.3192658,41.9716714]},"n1820939194":{"id":"n1820939194","loc":[-85.6400795,41.9130725]},"n1820939195":{"id":"n1820939195","loc":[-85.3278489,41.9780591]},"n1820939196":{"id":"n1820939196","loc":[-85.2800197,41.983061]},"n1820939197":{"id":"n1820939197","loc":[-85.3278167,41.9692943]},"n1820939198":{"id":"n1820939198","loc":[-85.3366894,41.9838653]},"n1820939199":{"id":"n1820939199","loc":[-85.0328383,42.0969923]},"n1820939201":{"id":"n1820939201","loc":[-85.3259284,41.9720383]},"n1820939217":{"id":"n1820939217","loc":[-85.1840181,42.0503277]},"n1820939220":{"id":"n1820939220","loc":[-85.422563,42.0089986]},"n1820939222":{"id":"n1820939222","loc":[-85.555386,41.9707856]},"n1820939224":{"id":"n1820939224","loc":[-85.3830809,42.002254]},"n1820939226":{"id":"n1820939226","loc":[-84.9917938,42.0857517]},"n1820939227":{"id":"n1820939227","loc":[-85.2936775,41.9740484]},"n1820939228":{"id":"n1820939228","loc":[-85.2632133,41.9975024]},"n1820939229":{"id":"n1820939229","loc":[-85.2809424,41.9853259]},"n1820939230":{"id":"n1820939230","loc":[-85.242104,42.0131204]},"n1820939232":{"id":"n1820939232","loc":[-85.2610246,41.9963901]},"n1820939233":{"id":"n1820939233","loc":[-85.2335531,42.0268378]},"n1820939234":{"id":"n1820939234","loc":[-85.3188839,41.9713575]},"n1820939235":{"id":"n1820939235","loc":[-85.2413637,42.0225658]},"n1820939237":{"id":"n1820939237","loc":[-85.0010796,42.0887215]},"n1820939239":{"id":"n1820939239","loc":[-85.2241697,42.0362624]},"n1820939243":{"id":"n1820939243","loc":[-85.0368456,42.1040134]},"n1820939244":{"id":"n1820939244","loc":[-85.1327986,42.069524]},"n1820939260":{"id":"n1820939260","loc":[-85.5408163,41.9711206]},"n1820939261":{"id":"n1820939261","loc":[-85.2959199,41.9746546]},"n1820939262":{"id":"n1820939262","loc":[-85.3298659,41.9683598]},"n1820939263":{"id":"n1820939263","loc":[-85.2240581,42.0358425]},"n1820939264":{"id":"n1820939264","loc":[-85.2438206,42.0101944]},"n1820939265":{"id":"n1820939265","loc":[-85.3984489,42.0059589]},"n1820939266":{"id":"n1820939266","loc":[-85.2330811,42.0294279]},"n1820939268":{"id":"n1820939268","loc":[-85.1126877,42.0857704]},"n1820939271":{"id":"n1820939271","loc":[-85.254925,42.0106253]},"n1820939273":{"id":"n1820939273","loc":[-85.4328046,42.0064662]},"n1820939277":{"id":"n1820939277","loc":[-85.289622,41.9789616]},"n1820939279":{"id":"n1820939279","loc":[-85.4574532,42.0004043]},"n1820939281":{"id":"n1820939281","loc":[-85.4803486,41.9867211]},"n1820939283":{"id":"n1820939283","loc":[-85.157475,42.0631848]},"n1820939285":{"id":"n1820939285","loc":[-85.2571458,42.0059935]},"n1820939287":{"id":"n1820939287","loc":[-85.2818544,41.9825984]},"n1820939289":{"id":"n1820939289","loc":[-85.2298302,42.0328781]},"n1820939291":{"id":"n1820939291","loc":[-85.4819523,41.984821]},"n1820939301":{"id":"n1820939301","loc":[-85.3139765,41.9701159]},"n1820939304":{"id":"n1820939304","loc":[-85.0424447,42.101742]},"n1820939306":{"id":"n1820939306","loc":[-85.6360283,41.9338482]},"n1820939310":{"id":"n1820939310","loc":[-85.3463025,41.9913622]},"n1820939312":{"id":"n1820939312","loc":[-85.4664869,41.9988097]},"n1820939314":{"id":"n1820939314","loc":[-85.149364,42.0622449]},"n1820939316":{"id":"n1820939316","loc":[-85.2460415,42.0153125]},"n1820939318":{"id":"n1820939318","loc":[-85.4806103,41.9924523]},"n1820939320":{"id":"n1820939320","loc":[-85.2449042,42.0190987]},"n1820939322":{"id":"n1820939322","loc":[-85.5280165,41.9689263]},"n1820939324":{"id":"n1820939324","loc":[-85.0051204,42.0882625]},"n1820939326":{"id":"n1820939326","loc":[-85.1240925,42.0771546]},"n1820939329":{"id":"n1820939329","loc":[-85.2261653,42.0342225]},"n1820939331":{"id":"n1820939331","loc":[-85.5259933,41.972211]},"n1820939333":{"id":"n1820939333","loc":[-85.0074754,42.0883183]},"n1820939335":{"id":"n1820939335","loc":[-85.0764014,42.1055549]},"n1820939336":{"id":"n1820939336","loc":[-85.2908773,41.9769597]},"n1820939337":{"id":"n1820939337","loc":[-85.4095382,42.0083449]},"n1820939346":{"id":"n1820939346","loc":[-85.2514166,42.0111753]},"n1820939348":{"id":"n1820939348","loc":[-85.0030377,42.0873799]},"n1820939350":{"id":"n1820939350","loc":[-85.3659362,41.9964974]},"n1820939352":{"id":"n1820939352","loc":[-85.226058,42.0348281]},"n1820939355":{"id":"n1820939355","loc":[-85.1902408,42.0507101]},"n1820939357":{"id":"n1820939357","loc":[-85.2781854,41.9946001]},"n1820939359":{"id":"n1820939359","loc":[-85.2139988,42.0405175]},"n1820939361":{"id":"n1820939361","loc":[-85.0086609,42.0908262]},"n1820939363":{"id":"n1820939363","loc":[-85.0627128,42.1043398]},"n1820939365":{"id":"n1820939365","loc":[-85.1311346,42.072501]},"n1820939369":{"id":"n1820939369","loc":[-85.248198,42.0082652]},"n1820939370":{"id":"n1820939370","loc":[-84.99792,42.087794]},"n1820939371":{"id":"n1820939371","loc":[-85.2786775,41.9942783]},"n1820939372":{"id":"n1820939372","loc":[-85.0342103,42.1013957]},"n1820939373":{"id":"n1820939373","loc":[-85.2022357,42.0444799]},"n1820939374":{"id":"n1820939374","loc":[-85.2279205,42.0337388]},"n1820939375":{"id":"n1820939375","loc":[-85.1337699,42.0712614]},"n1820939376":{"id":"n1820939376","loc":[-85.317517,41.9707062]},"n1820939377":{"id":"n1820939377","loc":[-85.1326326,42.070218]},"n1820939394":{"id":"n1820939394","loc":[-85.0197746,42.0899118]},"n1820939397":{"id":"n1820939397","loc":[-85.2590076,41.9984632]},"n1820939399":{"id":"n1820939399","loc":[-85.2469964,42.0083449]},"n1820939400":{"id":"n1820939400","loc":[-85.2470929,42.0146668]},"n1820939401":{"id":"n1820939401","loc":[-84.9984095,42.0878087]},"n1820939402":{"id":"n1820939402","loc":[-85.2372653,42.0243273]},"n1820939403":{"id":"n1820939403","loc":[-85.2454986,42.0091955]},"n1820939404":{"id":"n1820939404","loc":[-85.0539205,42.1035995]},"n1820939405":{"id":"n1820939405","loc":[-85.550601,41.9706101]},"n1820939406":{"id":"n1820939406","loc":[-85.0351343,42.0999656]},"n1820939407":{"id":"n1820939407","loc":[-85.0082908,42.0905755]},"n1820939408":{"id":"n1820939408","loc":[-85.0132904,42.0902251]},"n1820939410":{"id":"n1820939410","loc":[-85.0892546,42.094012]},"n1820939412":{"id":"n1820939412","loc":[-85.0350793,42.1030315]},"n1820939416":{"id":"n1820939416","loc":[-85.0012406,42.0886777]},"n1820939418":{"id":"n1820939418","loc":[-85.0577453,42.1029229]},"n1820939420":{"id":"n1820939420","loc":[-85.1230786,42.0776722]},"n1820939422":{"id":"n1820939422","loc":[-85.571136,41.9649304]},"n1820939436":{"id":"n1820939436","loc":[-85.1137968,42.0848997]},"n1820939437":{"id":"n1820939437","loc":[-85.3559584,41.9925105]},"n1820939438":{"id":"n1820939438","loc":[-85.0080172,42.0903565]},"n1820939439":{"id":"n1820939439","loc":[-85.0048897,42.0880913]},"n1820939441":{"id":"n1820939441","loc":[-85.0406959,42.1018574]},"n1820939443":{"id":"n1820939443","loc":[-85.3897328,42.0029078]},"n1820939445":{"id":"n1820939445","loc":[-85.122349,42.0782814]},"n1820939448":{"id":"n1820939448","loc":[-85.4872193,41.985036]},"n1820939450":{"id":"n1820939450","loc":[-85.0120459,42.0904919]},"n1820939452":{"id":"n1820939452","loc":[-85.6320543,41.921982]},"n1820939456":{"id":"n1820939456","loc":[-85.0844749,42.1036843]},"n1820939458":{"id":"n1820939458","loc":[-85.0968037,42.091296]},"n1820939463":{"id":"n1820939463","loc":[-85.5339747,41.9681841]},"n1820939465":{"id":"n1820939465","loc":[-85.4125423,42.0072129]},"n1820939467":{"id":"n1820939467","loc":[-85.6335563,41.9303626]},"n1820939469":{"id":"n1820939469","loc":[-85.2821014,41.9932126]},"n1820939471":{"id":"n1820939471","loc":[-85.374691,41.9969917]},"n1820939485":{"id":"n1820939485","loc":[-85.4471321,42.0049806]},"n1820939487":{"id":"n1820939487","loc":[-85.3752532,41.9972206]},"n1820939489":{"id":"n1820939489","loc":[-85.4517283,42.005927]},"n1820939492":{"id":"n1820939492","loc":[-85.4662552,42.0005693]},"n1820939494":{"id":"n1820939494","loc":[-85.0120083,42.0902928]},"n1820939496":{"id":"n1820939496","loc":[-85.044463,42.1004631]},"n1820939498":{"id":"n1820939498","loc":[-85.418293,42.0089667]},"n1820939500":{"id":"n1820939500","loc":[-85.0554762,42.1027358]},"n1820939504":{"id":"n1820939504","loc":[-85.1246289,42.0746858]},"n1820939507":{"id":"n1820939507","loc":[-85.0408139,42.1021838]},"n1820939508":{"id":"n1820939508","loc":[-85.1236204,42.0775169]},"n1820939509":{"id":"n1820939509","loc":[-85.0350109,42.1037428]},"n1820939510":{"id":"n1820939510","loc":[-85.0551583,42.1029878]},"n1820939511":{"id":"n1820939511","loc":[-85.0956771,42.0916662]},"n1820939512":{"id":"n1820939512","loc":[-85.2323408,42.0273638]},"n1820939513":{"id":"n1820939513","loc":[-85.1232771,42.0762388]},"n1820939531":{"id":"n1820939531","loc":[-85.264608,41.9997828]},"n1820939533":{"id":"n1820939533","loc":[-85.4198808,42.0087914]},"n1820939535":{"id":"n1820939535","loc":[-85.3080864,41.9715677]},"n1820939536":{"id":"n1820939536","loc":[-85.1189426,42.0812596]},"n1820939537":{"id":"n1820939537","loc":[-85.2642741,41.9996764]},"n1820939538":{"id":"n1820939538","loc":[-85.2572531,42.0079627]},"n1820939539":{"id":"n1820939539","loc":[-85.2907807,41.9790174]},"n1820939540":{"id":"n1820939540","loc":[-85.3171415,41.9707301]},"n1820939541":{"id":"n1820939541","loc":[-85.08777,42.0953841]},"n1820939542":{"id":"n1820939542","loc":[-85.1239262,42.0773218]},"n1820939543":{"id":"n1820939543","loc":[-84.9973956,42.0877968]},"n1820939544":{"id":"n1820939544","loc":[-85.011606,42.0896161]},"n1820939545":{"id":"n1820939545","loc":[-85.4077358,42.0082971]},"n1820939546":{"id":"n1820939546","loc":[-85.3614945,41.9933717]},"n1820939547":{"id":"n1820939547","loc":[-85.3189118,41.9697649]},"n1820939550":{"id":"n1820939550","loc":[-85.1262691,42.0740221]},"n1820939551":{"id":"n1820939551","loc":[-85.3863639,41.9994635]},"n1820939552":{"id":"n1820939552","loc":[-85.2836034,41.9923953]},"n1820939554":{"id":"n1820939554","loc":[-85.3222377,41.9715916]},"n1820939555":{"id":"n1820939555","loc":[-85.0122658,42.0906312]},"n1820939556":{"id":"n1820939556","loc":[-85.0022652,42.0877581]},"n1820939557":{"id":"n1820939557","loc":[-85.1011314,42.0899954]},"n1820939559":{"id":"n1820939559","loc":[-85.0008181,42.0885293]},"n1820939561":{"id":"n1820939561","loc":[-85.3637046,41.9942488]},"n1820939562":{"id":"n1820939562","loc":[-85.4500117,42.0052892]},"n1820939563":{"id":"n1820939563","loc":[-85.0537636,42.1036365]},"n1820939565":{"id":"n1820939565","loc":[-85.2367503,42.0246939]},"n1820939566":{"id":"n1820939566","loc":[-85.0448479,42.1002653]},"n1820939567":{"id":"n1820939567","loc":[-85.6337065,41.9295006]},"n1820939568":{"id":"n1820939568","loc":[-85.0879792,42.095623]},"n1820939569":{"id":"n1820939569","loc":[-85.6347623,41.9352369]},"n1820939570":{"id":"n1820939570","loc":[-85.1497931,42.0620378]},"n1820939571":{"id":"n1820939571","loc":[-85.5676169,41.9656324]},"n1820939572":{"id":"n1820939572","loc":[-85.638041,41.9166971]},"n1820939573":{"id":"n1820939573","loc":[-85.4993429,41.9781293]},"n1820939574":{"id":"n1820939574","loc":[-85.5352831,41.9692127]},"n1820939575":{"id":"n1820939575","loc":[-84.9924429,42.0857118]},"n1820939577":{"id":"n1820939577","loc":[-85.0581101,42.1026721]},"n1820939578":{"id":"n1820939578","loc":[-85.641088,41.9094477]},"n1820939579":{"id":"n1820939579","loc":[-85.2548821,42.0052282]},"n1820939580":{"id":"n1820939580","loc":[-85.1124463,42.0859734]},"n1820939581":{"id":"n1820939581","loc":[-85.1083479,42.0857624]},"n1820939583":{"id":"n1820939583","loc":[-85.1387424,42.0648893]},"n1820939584":{"id":"n1820939584","loc":[-85.5152645,41.9700892]},"n1820939585":{"id":"n1820939585","loc":[-85.5463738,41.9713439]},"n1820939586":{"id":"n1820939586","loc":[-85.360207,41.9933717]},"n1820939587":{"id":"n1820939587","loc":[-85.2402372,42.0120917]},"n1820939588":{"id":"n1820939588","loc":[-85.3936381,42.0047255]},"n1820939589":{"id":"n1820939589","loc":[-85.3310246,41.973784]},"n1820939590":{"id":"n1820939590","loc":[-85.0329403,42.096642]},"n1820939591":{"id":"n1820939591","loc":[-85.0097271,42.0910981]},"n1820939593":{"id":"n1820939593","loc":[-85.0446562,42.1003437]},"n1820939595":{"id":"n1820939595","loc":[-85.0856671,42.1008452]},"n1820939596":{"id":"n1820939596","loc":[-85.4087228,42.0083449]},"n1820939597":{"id":"n1820939597","loc":[-85.0609519,42.1052564]},"n1820939598":{"id":"n1820939598","loc":[-85.3432126,41.9874548]},"n1820939599":{"id":"n1820939599","loc":[-85.4041738,42.0067027]},"n1820939600":{"id":"n1820939600","loc":[-85.0825437,42.1035768]},"n1820939601":{"id":"n1820939601","loc":[-85.048422,42.101498]},"n1820939602":{"id":"n1820939602","loc":[-85.0336256,42.0999031]},"n1820939603":{"id":"n1820939603","loc":[-85.046818,42.1014104]},"n1820939605":{"id":"n1820939605","loc":[-85.2856524,41.98078]},"n1820939607":{"id":"n1820939607","loc":[-85.1118173,42.0864245]},"n1820939609":{"id":"n1820939609","loc":[-85.0443397,42.1006263]},"n1820939610":{"id":"n1820939610","loc":[-85.0336698,42.0978361]},"n1820939611":{"id":"n1820939611","loc":[-85.4630322,42.0014248]},"n1820939612":{"id":"n1820939612","loc":[-85.0613127,42.1052353]},"n1820939613":{"id":"n1820939613","loc":[-85.0137571,42.0887801]},"n1820939614":{"id":"n1820939614","loc":[-85.272487,41.9982013]},"n1820939616":{"id":"n1820939616","loc":[-85.4665727,41.9983791]},"n1820939617":{"id":"n1820939617","loc":[-85.1288078,42.0725476]},"n1820939618":{"id":"n1820939618","loc":[-85.4653282,42.00109]},"n1820939619":{"id":"n1820939619","loc":[-85.2314717,42.0276746]},"n1820939620":{"id":"n1820939620","loc":[-85.255982,42.0003569]},"n1820939621":{"id":"n1820939621","loc":[-85.2886779,41.9787223]},"n1820939622":{"id":"n1820939622","loc":[-85.22438,42.0367509]},"n1820939623":{"id":"n1820939623","loc":[-85.0334713,42.0998382]},"n1820939624":{"id":"n1820939624","loc":[-85.2236504,42.037484]},"n1820939625":{"id":"n1820939625","loc":[-85.636908,41.9175162]},"n1820939627":{"id":"n1820939627","loc":[-85.2669187,41.9989707]},"n1820939628":{"id":"n1820939628","loc":[-85.3247268,41.9720702]},"n1820939629":{"id":"n1820939629","loc":[-85.3785104,41.9987299]},"n1820939630":{"id":"n1820939630","loc":[-85.5267658,41.9720515]},"n1820939631":{"id":"n1820939631","loc":[-85.2445116,42.0098811]},"n1820939632":{"id":"n1820939632","loc":[-85.1271448,42.0725077]},"n1820939633":{"id":"n1820939633","loc":[-85.0345751,42.099724]},"n1820939634":{"id":"n1820939634","loc":[-85.4217476,42.0089986]},"n1820939635":{"id":"n1820939635","loc":[-85.3121848,41.9689433]},"n1820939636":{"id":"n1820939636","loc":[-85.2826419,41.9929985]},"n1820939637":{"id":"n1820939637","loc":[-85.3160257,41.9706344]},"n1820939638":{"id":"n1820939638","loc":[-85.5684967,41.9657919]},"n1820939640":{"id":"n1820939640","loc":[-85.225131,42.0356194]},"n1820939642":{"id":"n1820939642","loc":[-85.1324124,42.0693328]},"n1820939644":{"id":"n1820939644","loc":[-84.9994073,42.0878843]},"n1820939645":{"id":"n1820939645","loc":[-85.1087596,42.0863329]},"n1820939646":{"id":"n1820939646","loc":[-85.2915532,41.9782996]},"n1820939647":{"id":"n1820939647","loc":[-84.9988708,42.0877808]},"n1820939648":{"id":"n1820939648","loc":[-85.2243628,42.0356728]},"n1820939649":{"id":"n1820939649","loc":[-85.0427397,42.1020524]},"n1820939650":{"id":"n1820939650","loc":[-85.6388392,41.9100752]},"n1820939651":{"id":"n1820939651","loc":[-85.0133709,42.0888557]},"n1820939652":{"id":"n1820939652","loc":[-85.318798,41.9701211]},"n1820939653":{"id":"n1820939653","loc":[-85.6335778,41.9190602]},"n1820939654":{"id":"n1820939654","loc":[-85.6338396,41.9370247]},"n1820939655":{"id":"n1820939655","loc":[-85.0939069,42.0931988]},"n1820939656":{"id":"n1820939656","loc":[-85.5702347,41.9651378]},"n1820939657":{"id":"n1820939657","loc":[-85.4235286,42.0088392]},"n1820939658":{"id":"n1820939658","loc":[-85.2740856,41.9972206]},"n1820939659":{"id":"n1820939659","loc":[-85.4824299,41.9934195]},"n1820939660":{"id":"n1820939660","loc":[-85.3857846,42.0014408]},"n1820939661":{"id":"n1820939661","loc":[-85.0451658,42.10028]},"n1820939662":{"id":"n1820939662","loc":[-85.3893036,42.001377]},"n1820939664":{"id":"n1820939664","loc":[-85.2455845,42.0088607]},"n1820939665":{"id":"n1820939665","loc":[-85.2741071,41.9951116]},"n1820939666":{"id":"n1820939666","loc":[-85.1298375,42.0677718]},"n1820939667":{"id":"n1820939667","loc":[-85.5491848,41.9707377]},"n1820939669":{"id":"n1820939669","loc":[-85.2780298,41.995238]},"n1820939670":{"id":"n1820939670","loc":[-85.1330068,42.0716926]},"n1820939671":{"id":"n1820939671","loc":[-85.0811342,42.1025129]},"n1820939672":{"id":"n1820939672","loc":[-85.2325124,42.0290135]},"n1820939673":{"id":"n1820939673","loc":[-85.2975077,41.9716953]},"n1820939674":{"id":"n1820939674","loc":[-85.0951729,42.0922394]},"n1820939676":{"id":"n1820939676","loc":[-85.0363252,42.1043119]},"n1820939677":{"id":"n1820939677","loc":[-85.2960057,41.97349]},"n1820939678":{"id":"n1820939678","loc":[-85.3701849,41.9982515]},"n1820939679":{"id":"n1820939679","loc":[-85.3381486,41.9848861]},"n1820939680":{"id":"n1820939680","loc":[-85.2058448,42.0417286]},"n1820939682":{"id":"n1820939682","loc":[-85.0819335,42.1034443]},"n1820939683":{"id":"n1820939683","loc":[-85.3872223,41.9993359]},"n1820939684":{"id":"n1820939684","loc":[-85.095366,42.091909]},"n1820939685":{"id":"n1820939685","loc":[-85.2327914,42.0291888]},"n1820939686":{"id":"n1820939686","loc":[-85.0433459,42.1018773]},"n1820939687":{"id":"n1820939687","loc":[-85.0585339,42.1027318]},"n1820939688":{"id":"n1820939688","loc":[-85.0062885,42.0876347]},"n1820939689":{"id":"n1820939689","loc":[-85.246299,42.017377]},"n1820939690":{"id":"n1820939690","loc":[-85.2932376,41.9742877]},"n1820939691":{"id":"n1820939691","loc":[-85.2962846,41.9736815]},"n1820939692":{"id":"n1820939692","loc":[-85.6052365,41.9409193]},"n1820939693":{"id":"n1820939693","loc":[-85.2570536,42.0003341]},"n1820939694":{"id":"n1820939694","loc":[-85.0488458,42.1014064]},"n1820939695":{"id":"n1820939695","loc":[-85.4050321,42.0069578]},"n1820939696":{"id":"n1820939696","loc":[-85.4847517,41.9845894]},"n1820939697":{"id":"n1820939697","loc":[-85.0844655,42.1013826]},"n1820939698":{"id":"n1820939698","loc":[-85.1437206,42.0650008]},"n1820939699":{"id":"n1820939699","loc":[-85.1168183,42.0864034]},"n1820939700":{"id":"n1820939700","loc":[-85.5479831,41.9711366]},"n1820939701":{"id":"n1820939701","loc":[-85.0349948,42.1034124]},"n1820939702":{"id":"n1820939702","loc":[-85.0835589,42.1038821]},"n1820939703":{"id":"n1820939703","loc":[-85.0203875,42.0902649]},"n1820939704":{"id":"n1820939704","loc":[-85.0371191,42.1038184]},"n1820939705":{"id":"n1820939705","loc":[-85.1273312,42.0735681]},"n1820939707":{"id":"n1820939707","loc":[-85.1272239,42.0730226]},"n1820939710":{"id":"n1820939710","loc":[-85.0349881,42.1019012]},"n1820939712":{"id":"n1820939712","loc":[-85.2440459,42.0178313]},"n1820939713":{"id":"n1820939713","loc":[-85.2444751,42.0182618]},"n1820939714":{"id":"n1820939714","loc":[-85.0539996,42.1032863]},"n1820939715":{"id":"n1820939715","loc":[-85.2215905,42.0373246]},"n1820939716":{"id":"n1820939716","loc":[-85.0649712,42.1051994]},"n1820939717":{"id":"n1820939717","loc":[-85.0927146,42.0927581]},"n1820939718":{"id":"n1820939718","loc":[-85.3884668,42.0042312]},"n1820939719":{"id":"n1820939719","loc":[-85.0840672,42.1013241]},"n1820939720":{"id":"n1820939720","loc":[-85.304739,41.9725408]},"n1820939721":{"id":"n1820939721","loc":[-85.2243585,42.0371334]},"n1820939722":{"id":"n1820939722","loc":[-85.0599823,42.1049686]},"n1820939723":{"id":"n1820939723","loc":[-85.0298825,42.0944288]},"n1820939724":{"id":"n1820939724","loc":[-85.0366095,42.1042443]},"n1820939725":{"id":"n1820939725","loc":[-85.0698783,42.1058135]},"n1820939726":{"id":"n1820939726","loc":[-85.1054551,42.0873361]},"n1820939727":{"id":"n1820939727","loc":[-84.9952324,42.0864285]},"n1820939728":{"id":"n1820939728","loc":[-85.3442211,41.9897993]},"n1820939729":{"id":"n1820939729","loc":[-85.4386134,42.0056822]},"n1820939730":{"id":"n1820939730","loc":[-85.2438528,42.0146589]},"n1820939731":{"id":"n1820939731","loc":[-85.0355581,42.1041846]},"n1820939732":{"id":"n1820939732","loc":[-85.557682,41.9724447]},"n1820939734":{"id":"n1820939734","loc":[-85.2299418,42.033314]},"n1820939735":{"id":"n1820939735","loc":[-85.6297412,41.9419088]},"n1820939736":{"id":"n1820939736","loc":[-85.2645101,41.9980259]},"n1820939738":{"id":"n1820939738","loc":[-85.082195,42.1035649]},"n1820939739":{"id":"n1820939739","loc":[-85.234272,42.0267102]},"n1820939740":{"id":"n1820939740","loc":[-85.0130758,42.0895006]},"n1820939741":{"id":"n1820939741","loc":[-85.4594702,42.0000375]},"n1820939742":{"id":"n1820939742","loc":[-84.9946745,42.0863687]},"n1820939743":{"id":"n1820939743","loc":[-85.6438775,41.9120186]},"n1820939744":{"id":"n1820939744","loc":[-85.6372685,41.9168089]},"n1820939745":{"id":"n1820939745","loc":[-85.2789468,41.9893208]},"n1820939747":{"id":"n1820939747","loc":[-85.3775019,41.998427]},"n1820939749":{"id":"n1820939749","loc":[-85.0993571,42.0909178]},"n1820939750":{"id":"n1820939750","loc":[-85.1308503,42.0669339]},"n1820939751":{"id":"n1820939751","loc":[-85.4802566,41.9856659]},"n1820939752":{"id":"n1820939752","loc":[-85.2543563,42.0108804]},"n1820939753":{"id":"n1820939753","loc":[-85.1041033,42.0878815]},"n1820939755":{"id":"n1820939755","loc":[-85.4000969,42.0071651]},"n1820939757":{"id":"n1820939757","loc":[-85.3858275,42.0022381]},"n1820939758":{"id":"n1820939758","loc":[-85.3653998,41.996609]},"n1820939759":{"id":"n1820939759","loc":[-85.2432949,42.0202305]},"n1820939760":{"id":"n1820939760","loc":[-85.3878874,42.0042472]},"n1820939761":{"id":"n1820939761","loc":[-85.2516741,42.0114145]},"n1820939762":{"id":"n1820939762","loc":[-85.2788825,41.9865142]},"n1820939763":{"id":"n1820939763","loc":[-85.0009147,42.0886686]},"n1820939764":{"id":"n1820939764","loc":[-85.3918142,42.003434]},"n1820939765":{"id":"n1820939765","loc":[-85.5532832,41.9696848]},"n1820939766":{"id":"n1820939766","loc":[-85.5545063,41.969254]},"n1820939768":{"id":"n1820939768","loc":[-85.1327989,42.0704769]},"n1820939770":{"id":"n1820939770","loc":[-85.0588558,42.1047696]},"n1820939772":{"id":"n1820939772","loc":[-85.555798,41.9713017]},"n1820939773":{"id":"n1820939773","loc":[-85.0565853,42.1023589]},"n1820939774":{"id":"n1820939774","loc":[-85.2582941,41.9992765]},"n1820939775":{"id":"n1820939775","loc":[-85.3007264,41.9727642]},"n1820939776":{"id":"n1820939776","loc":[-85.2477045,42.0082652]},"n1820939777":{"id":"n1820939777","loc":[-85.2415247,42.0104973]},"n1821006698":{"id":"n1821006698","loc":[-85.6345227,41.9382009]},"n1821006700":{"id":"n1821006700","loc":[-85.6344894,41.938975]},"n1821006704":{"id":"n1821006704","loc":[-85.6351181,41.9370157]},"n1821006706":{"id":"n1821006706","loc":[-85.6357554,41.9361657]},"n1821006708":{"id":"n1821006708","loc":[-85.6351235,41.9368481]},"n1821006710":{"id":"n1821006710","loc":[-85.6352844,41.9364211]},"n1821006712":{"id":"n1821006712","loc":[-85.6351503,41.937307]},"n1821006716":{"id":"n1821006716","loc":[-85.6350366,41.9379774]},"n1821006725":{"id":"n1821006725","loc":[-85.6352147,41.9375903]},"n1821137607":{"id":"n1821137607","loc":[-85.5297057,41.9669915]},"n1821137608":{"id":"n1821137608","loc":[-85.5288598,41.9673094]},"n1821139530":{"id":"n1821139530","loc":[-85.4832228,41.9881686]},"n1821139531":{"id":"n1821139531","loc":[-85.4812101,41.9851258]},"n1821139532":{"id":"n1821139532","loc":[-85.4799127,41.9860244]},"n1821139533":{"id":"n1821139533","loc":[-85.4800313,41.9865555]},"n1841425201":{"id":"n1841425201","loc":[-85.4334577,42.0063713]},"n1841425222":{"id":"n1841425222","loc":[-85.4382449,42.0055785]},"n1914861007":{"id":"n1914861007","loc":[-85.394959,42.0057472]},"n1914861057":{"id":"n1914861057","loc":[-85.3967185,42.0049695]},"n1914861112":{"id":"n1914861112","loc":[-85.394179,42.0056906]},"n1914861306":{"id":"n1914861306","loc":[-85.3900226,42.0028488]},"n2114807565":{"id":"n2114807565","loc":[-85.6385979,41.9577824]},"n2114807568":{"id":"n2114807568","loc":[-85.6325097,41.9775713]},"n2114807572":{"id":"n2114807572","loc":[-85.6328996,41.9980965]},"n2114807578":{"id":"n2114807578","loc":[-85.6344818,41.9696956]},"n2114807583":{"id":"n2114807583","loc":[-85.6326289,41.9757853]},"n2114807593":{"id":"n2114807593","loc":[-85.6360828,41.9650674]},"n2130304159":{"id":"n2130304159","loc":[-85.6352537,41.9450015],"tags":{"railway":"level_crossing"}},"n2139795852":{"id":"n2139795852","loc":[-85.6374708,41.9311633]},"n2139858882":{"id":"n2139858882","loc":[-85.635178,41.9356158]},"n2139858883":{"id":"n2139858883","loc":[-85.63533,41.9355886]},"n2139858884":{"id":"n2139858884","loc":[-85.6353819,41.93556]},"n2139858885":{"id":"n2139858885","loc":[-85.6353665,41.9355157]},"n2139858886":{"id":"n2139858886","loc":[-85.6353165,41.9354971]},"n2139858887":{"id":"n2139858887","loc":[-85.6352454,41.9355328]},"n2139858888":{"id":"n2139858888","loc":[-85.6350184,41.9357846]},"n2139858889":{"id":"n2139858889","loc":[-85.634978,41.9359448]},"n2139858890":{"id":"n2139858890","loc":[-85.6347723,41.9361523]},"n2139858891":{"id":"n2139858891","loc":[-85.6347165,41.9362667]},"n2139858892":{"id":"n2139858892","loc":[-85.6346992,41.9364312]},"n2139858893":{"id":"n2139858893","loc":[-85.634603,41.9366329]},"n2139858894":{"id":"n2139858894","loc":[-85.6345973,41.9367488]},"n2139858895":{"id":"n2139858895","loc":[-85.6345127,41.9369734]},"n2139858896":{"id":"n2139858896","loc":[-85.634478,41.9371923]},"n2139858897":{"id":"n2139858897","loc":[-85.6344838,41.9373768]},"n2139858898":{"id":"n2139858898","loc":[-85.6346242,41.9375299]},"n2139858899":{"id":"n2139858899","loc":[-85.6347723,41.9376357]},"n2139858900":{"id":"n2139858900","loc":[-85.6347607,41.9377788]},"n2139858901":{"id":"n2139858901","loc":[-85.6346204,41.9379533]},"n2139858902":{"id":"n2139858902","loc":[-85.6344184,41.9380105]},"n2139858903":{"id":"n2139858903","loc":[-85.6341627,41.9380406]},"n2139858904":{"id":"n2139858904","loc":[-85.634005,41.9381679]},"n2139858905":{"id":"n2139858905","loc":[-85.63393,41.9383353]},"n2139858906":{"id":"n2139858906","loc":[-85.6338588,41.9384597]},"n2139858907":{"id":"n2139858907","loc":[-85.6336627,41.9387759]},"n2139858908":{"id":"n2139858908","loc":[-85.6335127,41.9389361]},"n2139858933":{"id":"n2139858933","loc":[-85.6353118,41.9432646]},"n2139858934":{"id":"n2139858934","loc":[-85.6353952,41.9433002]},"n2139858935":{"id":"n2139858935","loc":[-85.6356496,41.9433255]},"n2139858936":{"id":"n2139858936","loc":[-85.6363128,41.9433373]},"n2139858937":{"id":"n2139858937","loc":[-85.6365467,41.9433779]},"n2139858938":{"id":"n2139858938","loc":[-85.6368692,41.9435265]},"n2139858939":{"id":"n2139858939","loc":[-85.6370986,41.9437039]},"n2139858940":{"id":"n2139858940","loc":[-85.6372371,41.9437732]},"n2139858941":{"id":"n2139858941","loc":[-85.6374756,41.9438171]},"n2139858942":{"id":"n2139858942","loc":[-85.6376164,41.9439286]},"n2139858943":{"id":"n2139858943","loc":[-85.6377504,41.944138]},"n2139858944":{"id":"n2139858944","loc":[-85.6384204,41.9443137]},"n2139858945":{"id":"n2139858945","loc":[-85.6385726,41.9444506]},"n2139858946":{"id":"n2139858946","loc":[-85.638702,41.9445739]},"n2139858947":{"id":"n2139858947","loc":[-85.6387179,41.9446516]},"n2139858948":{"id":"n2139858948","loc":[-85.6387088,41.9447985]},"n2139858949":{"id":"n2139858949","loc":[-85.6387656,41.9449877]},"n2139858950":{"id":"n2139858950","loc":[-85.638777,41.9451448]},"n2139858951":{"id":"n2139858951","loc":[-85.6387088,41.9452631]},"n2139858964":{"id":"n2139858964","loc":[-85.6383346,41.9442912]},"n2139858966":{"id":"n2139858966","loc":[-85.6384724,41.9443605]},"n2139858967":{"id":"n2139858967","loc":[-85.6354078,41.9434285]},"n2139858968":{"id":"n2139858968","loc":[-85.635271,41.943654]},"n2139858969":{"id":"n2139858969","loc":[-85.6352657,41.9437437]},"n2139858970":{"id":"n2139858970","loc":[-85.635271,41.9438195]},"n2139858971":{"id":"n2139858971","loc":[-85.6351563,41.9438906]},"n2139858972":{"id":"n2139858972","loc":[-85.6351384,41.9438882]},"n2139858973":{"id":"n2139858973","loc":[-85.6351514,41.9438034]},"n2139858974":{"id":"n2139858974","loc":[-85.6351237,41.9436641]},"n2139858975":{"id":"n2139858975","loc":[-85.6351498,41.9436108]},"n2139858976":{"id":"n2139858976","loc":[-85.6351058,41.9435345]},"n2139858977":{"id":"n2139858977","loc":[-85.6349641,41.9432051]},"n2139858986":{"id":"n2139858986","loc":[-85.6341205,41.9380746]},"n2139858990":{"id":"n2139858990","loc":[-85.6345671,41.9381816]},"n2139858995":{"id":"n2139858995","loc":[-85.6339783,41.9382273]},"n2139859003":{"id":"n2139859003","loc":[-85.6340477,41.9373489]},"n2139859004":{"id":"n2139859004","loc":[-85.6339784,41.9374752]},"n2139870406":{"id":"n2139870406","loc":[-85.6342265,41.9432605]},"n2139877106":{"id":"n2139877106","loc":[-85.6346323,41.9438746]},"n2139982399":{"id":"n2139982399","loc":[-85.6324055,41.9408537]},"n2139982400":{"id":"n2139982400","loc":[-85.632488,41.941063],"tags":{"leisure":"slipway"}},"n2139982401":{"id":"n2139982401","loc":[-85.6327261,41.9415366]},"n2139982402":{"id":"n2139982402","loc":[-85.6326391,41.9413598]},"n2139982403":{"id":"n2139982403","loc":[-85.6327041,41.9414391]},"n2139982405":{"id":"n2139982405","loc":[-85.6322891,41.9406009]},"n2139982406":{"id":"n2139982406","loc":[-85.6325412,41.9425257]},"n2139989333":{"id":"n2139989333","loc":[-85.6340584,41.9431731]},"n2140006331":{"id":"n2140006331","loc":[-85.6361751,41.9459744]},"n2140006334":{"id":"n2140006334","loc":[-85.636528,41.9459751]},"n2140006336":{"id":"n2140006336","loc":[-85.6370918,41.9458926]},"n2140006338":{"id":"n2140006338","loc":[-85.6378806,41.9456474]},"n2140006340":{"id":"n2140006340","loc":[-85.6385831,41.9454343]},"n2140006342":{"id":"n2140006342","loc":[-85.639341,41.945157]},"n2140006344":{"id":"n2140006344","loc":[-85.6393497,41.9450232]},"n2140006346":{"id":"n2140006346","loc":[-85.6388245,41.9450145]},"n2140006348":{"id":"n2140006348","loc":[-85.6388167,41.9441739]},"n2140006351":{"id":"n2140006351","loc":[-85.6382915,41.9441797]},"n2140006353":{"id":"n2140006353","loc":[-85.63828,41.9438109]},"n2140006355":{"id":"n2140006355","loc":[-85.6381949,41.9436009]},"n2140006357":{"id":"n2140006357","loc":[-85.6371904,41.9435918]},"n2140006359":{"id":"n2140006359","loc":[-85.6366966,41.9432727]},"n2140006361":{"id":"n2140006361","loc":[-85.6353755,41.9432744]},"n2140006365":{"id":"n2140006365","loc":[-85.6350906,41.9435472]},"n2140006366":{"id":"n2140006366","loc":[-85.6343461,41.9441573]},"n2140006395":{"id":"n2140006395","loc":[-85.6351171,41.9437175]},"n2140006397":{"id":"n2140006397","loc":[-85.635352,41.9450206]},"n2140006399":{"id":"n2140006399","loc":[-85.6358194,41.9454937]},"n2140006401":{"id":"n2140006401","loc":[-85.6348693,41.9445739]},"n2140006431":{"id":"n2140006431","loc":[-85.6376737,41.9438023]},"n2140006437":{"id":"n2140006437","loc":[-85.6382631,41.9442724]},"n2189123379":{"id":"n2189123379","loc":[-85.6342671,41.9352665]},"w203974076":{"id":"w203974076","tags":{"highway":"footway"},"nodes":["n2139870442","n2139870457","n2139870458","n2139870459","n2139870460","n2139870452"]},"w170989131":{"id":"w170989131","tags":{"name":"St Joseph River","waterway":"river"},"nodes":["n1820938225","n1820938712","n1820937596","n1820937574","n1820938515","n1820938330","n1820938678","n1820938240","n1820938950","n1820939226","n1820939575","n1820937913","n1820938223","n1820937668","n1820938545","n1820937584","n1820939742","n1820939727","n1820937578","n1820938149","n1820938124","n1820938888","n1820938898","n1820937922","n1820939543","n1820939370","n1820939401","n1820939647","n1820938345","n1820939644","n1820938333","n1820938370","n1820938624","n1820938493","n1820939559","n1820939763","n1820939237","n1820939416","n1820937810","n1820938317","n1820938324","n1820937558","n1820939556","n1820938298","n1820939348","n1820939125","n1820939081","n1820938859","n1820939126","n1820938881","n1820939439","n1820939324","n1820939128","n1820938101","n1820937706","n1820938382","n1820938776","n1820937815","n1820939177","n1820939688","n1820938952","n1820938216","n1820938387","n1820939333","n1820938243","n1820938248","n1820937666","n1820939051","n1820938332","n1820939438","n1820939407","n1820939361","n1820937517","n1820938770","n1820939591","n1820937857","n1820938491","n1820937993","n1820938125","n1820938166","n1820937746","n1820939028","n1820937638","n1820938676","n1820938843","n1820938844","n1820937978","n1820938730","n1820939544","n1820938304","n1820939123","n1820939494","n1820939450","n1820939555","n1820938133","n1820938129","n1820938871","n1820939408","n1820938669","n1820938260","n1820939740","n1820937625","n1820938631","n1820939651","n1820939613","n1820937850","n1820938325","n1820937736","n1820938804","n1820938837","n1820938014","n1820938991","n1820938722","n1820938935","n1820937870","n1820938432","n1820937986","n1820938756","n1820938966","n1820939159","n1820937744","n1820938334","n1820937645","n1820939394","n1820937656","n1820938392","n1820939703","n1820938385","n1820938947","n1820938854","n1820938428","n1820938488","n1820938269","n1820938668","n1820938268","n1820938707","n1820937732","n1820939144","n1820938481","n1820938771","n1820938686","n1820938948","n1820937997","n1820937769","n1820939003","n1820938083","n1820939011","n1820938803","n1820938700","n1820939723","n1820938808","n1820938262","n1820938081","n1820938926","n1820938326","n1820938102","n1820938508","n1820939590","n1820939199","n1820938084","n1820938870","n1820938895","n1820937611","n1820938918","n1820938514","n1820939610","n1820938910","n1820937523","n1820938127","n1820939108","n1820937981","n1820938938","n1820938715","n1820939016","n1820938237","n1820939623","n1820939602","n1820937734","n1820938977","n1820939633","n1820939156","n1820939406","n1820938279","n1820938301","n1820937678","n1820937671","n1820939163","n1820938356","n1820939372","n1820937568","n1820937626","n1820939710","n1820939004","n1820938253","n1820938571","n1820937513","n1820939412","n1820939701","n1820939509","n1820938839","n1820939731","n1820937798","n1820939676","n1820939724","n1820939243","n1820939704","n1820937814","n1820937599","n1820938199","n1820938995","n1820938445","n1820938069","n1820938470","n1820939074","n1820938193","n1820938740","n1820938047","n1820939507","n1820939441","n1820939160","n1820937849","n1820937840","n1820938052","n1820938988","n1820938796","n1820937724","n1820937620","n1820939304","n1820938343","n1820939649","n1820938875","n1820939686","n1820938476","n1820937801","n1820937737","n1820938264","n1820939609","n1820939496","n1820939593","n1820939566","n1820939661","n1820937782","n1820938912","n1820939173","n1820937733","n1820938953","n1820939603","n1820937607","n1820938468","n1820939601","n1820939694","n1820939133","n1820938897","n1820938893","n1820937831","n1820937730","n1820938820","n1820938046","n1820938426","n1820938347","n1820937582","n1820938954","n1820938033","n1820938104","n1820938680","n1820939563","n1820939404","n1820939714","n1820939000","n1820937992","n1820938168","n1820939510","n1820939500","n1820937509","n1820938865","n1820939773","n1820938138","n1820938905","n1820937623","n1820939418","n1820937946","n1820939577","n1820937615","n1820939687","n1820939119","n1820937988","n1820938337","n1820937750","n1820938703","n1820938339","n1820939044","n1820939770","n1820938913","n1820937672","n1820939722","n1820937768","n1820939597","n1820939612","n1820937699","n1820937682","n1820937669","n1820937657","n1820939363","n1820937800","n1820938265","n1820937760","n1820938207","n1820938115","n1820939130","n1820939716","n1820938338","n1820938239","n1820939040","n1820938064","n1820938855","n1820939015","n1820938258","n1820939042","n1820939043","n1820938443","n1820939725","n1820937675","n1820938568","n1820938280","n1820937705","n1820938775","n1820938636","n1820938626","n1820937859","n1820938096","n1820937852","n1820939039","n1820938247","n1820938585","n1820937707","n1820938117","n1820938909","n1820939115","n1820939335","n1820938805","n1820937935","n1820937876","n1820938699","n1820937869","n1820938603","n1820938100","n1820938500","n1820938283","n1820938275","n1820938923","n1820938365","n1820938349","n1820937804","n1820937903","n1820937608","n1820938688","n1820939671","n1820938092","n1820937820","n1820938753","n1820938922","n1820937990","n1820939682","n1820939738","n1820939600","n1820938167","n1820937726","n1820939702","n1820938209","n1820939456","n1820937837","n1820938222","n1820938902","n1820939162","n1820938965","n1820938461","n1820937681","n1820937514","n1820937764","n1820939719","n1820939697","n1820938899","n1820939093","n1820938702","n1820939595","n1820938749","n1820938348","n1820937606","n1820938675","n1820938830","n1820938737","n1820938758","n1820938716","n1820939107","n1820937863","n1820939033","n1820938163","n1820937867","n1820938819","n1820938034","n1820938252","n1820937563","n1820937868","n1820939032","n1820938632","n1820937982","n1820937943","n1820939568","n1820939541","n1820938215","n1820939097","n1820938812","n1820937518","n1820937952","n1820938711","n1820938736","n1820939066","n1820937591","n1820938082","n1820938108","n1820938496","n1820939410","n1820938949","n1820938327","n1820937708","n1820939023","n1820937772","n1820938256","n1820939083","n1820938378","n1820938961","n1820937610","n1820939717","n1820938695","n1820938590","n1820939655","n1820938341","n1820939054","n1820939157","n1820939674","n1820939684","n1820939511","n1820937631","n1820939458","n1820937830","n1820937709","n1820937779","n1820939749","n1820938880","n1820938856","n1820938557","n1820939557","n1820938249","n1820938818","n1820937594","n1820939114","n1820938416","n1820937508","n1820938990","n1820938201","n1820937759","n1820937987","n1820939164","n1820939753","n1820938187","n1820939067","n1820937586","n1820937941","n1820938121","n1820937807","n1820938521","n1820939726","n1820938244","n1820939014","n1820938741","n1820937629","n1820938664","n1820938747","n1820939082","n1820938709","n1820938320","n1820938270","n1820937619","n1820937777","n1820937718","n1820939138","n1820938056","n1820938155","n1820938596","n1820937775","n1820938437","n1820938128","n1820939581","n1820939145","n1820938546","n1820938184","n1820937601","n1820937794","n1820938539","n1820939645","n1820938438","n1820938436","n1820939025","n1820938915","n1820938534","n1820937605","n1820939607","n1820939101","n1820939580","n1820939268","n1820939134","n1820938849","n1820938754","n1820938079","n1820937842","n1820938781","n1820938873","n1820938495","n1820938381","n1820938503","n1820939436","n1820938502","n1820939087","n1820938996","n1820938449","n1820938907","n1820937979","n1820937780","n1820937546","n1820939699","n1820937677","n1820938957","n1820938946","n1820937776","n1820937717","n1820938718","n1820937637","n1820938510","n1820937663","n1820938941","n1820939151","n1820937603","n1820938250","n1820937951","n1820938630","n1820938821","n1820938779","n1820938497","n1820938159","n1820939536","n1820938409","n1820938386","n1820939116","n1820938340","n1820939117","n1820938291","n1820938435","n1820937819","n1820938242","n1820939078","n1820938877","n1820939104","n1820939445","n1820938367","n1820938903","n1820939420","n1820938517","n1820939508","n1820939542","n1820939326","n1820938210","n1820939020","n1820938815","n1820937832","n1820939513","n1820937818","n1820939005","n1820938717","n1820939135","n1820938384","n1820937587","n1820939024","n1820939504","n1820939120","n1820939026","n1820938015","n1820938998","n1820937648","n1820939137","n1820937761","n1820938195","n1820938535","n1820939550","n1820938725","n1820938282","n1820937781","n1820937792","n1820939705","n1820937788","n1820939707","n1820937882","n1820939632","n1820938427","n1820938276","n1820939617","n1820939013","n1820939035","n1820937543","n1820939365","n1820937752","n1820937802","n1820939183","n1820939670","n1820938450","n1820939375","n1820937813","n1820937673","n1820937783","n1820939029","n1820939768","n1820939377","n1820937974","n1820939244","n1820939642","n1820937864","n1820938255","n1820938528","n1820939666","n1820938120","n1820937812","n1820938928","n1820939750","n1820939099","n1820938073","n1820938714","n1820939140","n1820938192","n1820937844","n1820938635","n1820938742","n1820939583","n1820937887","n1820938318","n1820938816","n1820939698","n1820938273","n1820939181","n1820937652","n1820938748","n1820937651","n1820938519","n1820938019","n1820938752","n1820938235","n1820939118","n1820938562","n1820939314","n1820939570","n1820938190","n1820938342","n1820938533","n1820937977","n1820939089","n1820939146","n1820938622","n1820938297","n1820938524","n1820939283","n1820938874","n1820938832","n1820937550","n1820937843","n1820938638","n1820938116","n1820938206","n1820938319","n1820939053","n1820937845","n1820938093","n1820939217","n1820938997","n1820939355","n1820938861","n1820938726","n1820938057","n1820939373","n1820937862","n1820938518","n1820939072","n1820939680","n1820938444","n1820938217","n1820938506","n1820938393","n1820938492","n1820938852","n1820938221","n1820938773","n1820937684","n1820939060","n1820938224","n1820938203","n1820938840","n1820937525","n1820938147","n1820938433","n1820938188","n1820939359","n1820938750","n1820938016","n1820938768","n1820937621","n1820937799","n1820938951","n1820938721","n1820939037","n1820937866","n1820939715","n1820938063","n1820938446","n1820937627","n1820939624","n1820938431","n1820939721","n1820939622","n1820939239","n1820939263","n1820939648","n1820939640","n1820938867","n1820938757","n1820938439","n1820939352","n1820937740","n1820939329","n1820938229","n1820937583","n1820938180","n1820938366","n1820937767","n1820937758","n1820939374","n1820938869","n1820938292","n1820938400","n1820938399","n1820939734","n1820939289","n1820938944","n1820937755","n1820938759","n1820938434","n1820937600","n1820937825","n1820937670","n1820937793","n1820938011","n1820938246","n1820938956","n1820937770","n1820937757","n1820938059","n1820937860","n1820937569","n1820939266","n1820939685","n1820939672","n1820938606","n1820938772","n1820939038","n1820938211","n1820938359","n1820939619","n1820938708","n1820939512","n1820938065","n1820939233","n1820939739","n1820938786","n1820938879","n1820939147","n1820938563","n1820939148","n1820937839","n1820937659","n1820937786","n1820938419","n1820939565","n1820939402","n1820937710","n1820938254","n1820938271","n1820938390","n1820937680","n1820938140","n1820937817","n1820938218","n1820937985","n1820939235","n1820938441","n1820938401","n1820938719","n1820937795","n1820938971","n1820938460","n1820939759","n1820937972","n1820937841","n1820938462","n1820939320","n1820938978","n1820938360","n1820939713","n1820937676","n1820939712","n1820937939","n1820938080","n1820937754","n1820937753","n1820938530","n1820937886","n1820939689","n1820939124","n1820938697","n1820938789","n1820939105","n1820938860","n1820938853","n1820939400","n1820937561","n1820938404","n1820938774","n1820939316","n1820937696","n1820938782","n1820938975","n1820937564","n1820939730","n1820938257","n1820937853","n1820938487","n1820938848","n1820938906","n1820939230","n1820938424","n1820938051","n1820937771","n1820939587","n1820939149","n1820938792","n1820939041","n1820938934","n1820939777","n1820937515","n1820939058","n1820938312","n1820939264","n1820939631","n1820939109","n1820939403","n1820939664","n1820938724","n1820938929","n1820939399","n1820939776","n1820939369","n1820939185","n1820937701","n1820938126","n1820938336","n1820938219","n1820939080","n1820938642","n1820938043","n1820937725","n1820938548","n1820938552","n1820938035","n1820938684","n1820937778","n1820938764","n1820939021","n1820939346","n1820937712","n1820939761","n1820938397","n1820937747","n1820938566","n1820939161","n1820939090","n1820939752","n1820939271","n1820938878","n1820938110","n1820938346","n1820938499","n1820938151","n1820939538","n1820938281","n1820939153","n1820938551","n1820939285","n1820938197","n1820938408","n1820938482","n1820939036","n1820939579","n1820938489","n1820938483","n1820938189","n1820938123","n1820938087","n1820937741","n1820938485","n1820937590","n1820938972","n1820937773","n1820937520","n1820938872","n1820938131","n1820938452","n1820938328","n1820939620","n1820937641","n1820938353","n1820939693","n1820938705","n1820937640","n1820939189","n1820938144","n1820939774","n1820938694","n1820938238","n1820939397","n1820937917","n1820938454","n1820938567","n1820938979","n1820938060","n1820938204","n1820937828","n1820939232","n1820938806","n1820938857","n1820938078","n1820938105","n1820939228","n1820938604","n1820937763","n1820937854","n1820938289","n1820939736","n1820937937","n1820937714","n1820938278","n1820938058","n1820938706","n1820938989","n1820938313","n1820938520","n1820938288","n1820937689","n1820939537","n1820939531","n1820939019","n1820937527","n1820938455","n1820938814","n1820938045","n1820939627","n1820938213","n1820938161","n1820938331","n1820938024","n1820938220","n1820938062","n1820938178","n1820937796","n1820937644","n1820938490","n1820937589","n1820937879","n1820939614","n1820938882","n1820938039","n1820938538","n1820937667","n1820937719","n1820938561","n1820939658","n1820938783","n1820938601","n1820938198","n1820938388","n1820938969","n1820937687","n1820939086","n1820939665","n1820939187","n1820938498","n1820938261","n1820937983","n1820938068","n1820938136","n1820939061","n1820938137","n1820938186","n1820939071","n1820937592","n1820939669","n1820937553","n1820939357","n1820938727","n1820939371","n1820939112","n1820939079","n1820938743","n1820938467","n1820938834","n1820938022","n1820938537","n1820938122","n1820938516","n1820937614","n1820937612","n1820939469","n1820939636","n1820939050","n1820939552","n1820938157","n1820938663","n1820938955","n1820939091","n1820938430","n1820938471","n1820937809","n1820938074","n1820938208","n1820938914","n1820938858","n1820938417","n1820937531","n1820938107","n1820939100","n1820938751","n1820937711","n1820938824","n1820939745","n1820937572","n1820938602","n1820938212","n1820938097","n1820937921","n1820938090","n1820938511","n1820938876","n1820939762","n1820938234","n1820938048","n1820937774","n1820937856","n1820937749","n1820937765","n1820938286","n1820939095","n1820938480","n1820939229","n1820938277","n1820937617","n1820938311","n1820937622","n1820939196","n1820937690","n1820939006","n1820939287","n1820939131","n1820938106","n1820938784","n1820938335","n1820938095","n1820938182","n1820937715","n1820937683","n1820938070","n1820939605","n1820938527","n1820938763","n1820938398","n1820937686","n1820939621","n1820937664","n1820939277","n1820938565","n1820939539","n1820938099","n1820939646","n1820938556","n1820937548","n1820938729","n1820939336","n1820938259","n1820938728","n1820938361","n1820937643","n1820938644","n1820939007","n1820939690","n1820939227","n1820937635","n1820937950","n1820938682","n1820939150","n1820939012","n1820939261","n1820939111","n1820937805","n1820939691","n1820939677","n1820937628","n1820937811","n1820938790","n1820938251","n1820938226","n1820938942","n1820937633","n1820937984","n1820937751","n1820939673","n1820938970","n1820938415","n1820938597","n1820938309","n1820938111","n1820938472","n1820938894","n1820938402","n1820937593","n1820938570","n1820939102","n1820939775","n1820937948","n1820939121","n1820937511","n1820938787","n1820939720","n1820939075","n1820937880","n1820937742","n1820937721","n1820939535","n1820938486","n1820938354","n1820937632","n1820939010","n1820938885","n1820938089","n1820937613","n1820938442","n1820938245","n1820938272","n1820937566","n1820938295","n1820938532","n1820938883","n1820937713","n1820937674","n1820939635","n1820938448","n1820938355","n1820938587","n1820938559","n1820937787","n1820939301","n1820937723","n1820939056","n1820937560","n1820938323","n1820938230","n1820938453","n1820938377","n1820938357","n1820939637","n1820938017","n1820939540","n1820939376","n1820937639","n1820937642","n1820938075","n1820938351","n1820938766","n1820937897","n1820938973","n1820938066","n1820939547","n1820939652","n1820937944","n1820937748","n1820939234","n1820939193","n1820937891","n1820938785","n1820939132","n1820938523","n1820938884","n1820938411","n1820939554","n1820938791","n1820937655","n1820938368","n1820939152","n1820938030","n1820938447","n1820937580","n1820939628","n1820937588","n1820937894","n1820939201","n1820938086","n1820937650","n1820938379","n1820939008","n1820938999","n1820937524","n1820937872","n1820938389","n1820939197","n1820938422","n1820938936","n1820939262","n1820937634","n1820938583","n1820939589","n1820937901","n1820939034","n1820939065","n1820938290","n1820939195","n1820938228","n1820937884","n1820938797","n1820938191","n1820939191","n1820939198","n1820937892","n1820939679","n1820938507","n1820937647","n1820937909","n1820938542","n1820939598","n1820937851","n1820939084","n1820939728","n1820937688","n1820938263","n1820938670","n1820937762","n1820939310","n1820938925","n1820938862","n1820938822","n1820938547","n1820937731","n1820938594","n1820938592","n1820938214","n1820938284","n1820937835","n1820938599","n1820939437","n1820937834","n1820937576","n1820937692","n1820939586","n1820939546","n1820938403","n1820937970","n1820939561","n1820938098","n1820938851","n1820938477","n1820938892","n1820939045","n1820939758","n1820939350","n1820938321","n1820938440","n1820938595","n1820938364","n1820938962","n1820938118","n1820939678","n1820938406","n1820938549","n1820937555","n1820938823","n1820937521","n1820939471","n1820939487","n1820938799","n1820938605","n1820937928","n1820938373","n1820939747","n1820939629","n1820937557","n1820937526","n1820938958","n1820938833","n1820937636","n1820938967","n1820938760","n1820938842","n1820938067","n1820939077","n1820939224","n1820938185","n1820939110","n1820938372","n1820939757","n1820939063","n1820939660","n1820938813","n1820937528","n1820938369","n1820938896","n1820939551","n1820939683","n1820937660","n1820937873","n1820938810","n1820938478","n1820939662","n1820937595","n1820939052","n1820938113","n1820939070","n1820938733","n1820937878","n1820938300","n1820939760","n1820939718","n1820937646","n1820939057","n1820939443","n1914861306","n1820938013","n1820937529","n1820939764","n1820938826","n1820937885","n1820939588","n1820937865","n1820937833","n1914861112","n1820938761","n1914861007","n1820937905","n1820938541","n1820939092","n1914861057","n1820938153","n1820938267","n1820939265","n1820938085","n1820939018","n1820939755","n1820938474","n1820939027","n1820938593","n1820938202","n1820939599","n1820939695","n1820938077","n1820938012","n1820939545","n1820939596","n1820939337","n1820938227","n1820937698","n1820938475","n1820939465","n1820938165","n1820938698","n1820938525","n1820938529","n1820938553","n1820938940","n1820939498","n1820938501","n1820939533","n1820938924","n1820939634","n1820939220","n1820939657","n1820938887","n1820938838","n1820938114","n1820937823","n1820938778","n1820938801","n1820939096","n1820938981","n1820937953","n1820938732","n1820938980","n1820938960","n1820937949","n1820938026","n1820939273","n1841425201","n1820938629","n1820938864","n1820938554","n1820938088","n1820937685","n1841425222","n1820939729","n1820937665","n1820937838","n1820937739","n1820938780","n1820937821","n1820938825","n1820939055","n1820939485","n1820938041","n1820938746","n1820939562","n1820938459","n1820939489","n1820938050","n1820937980","n1820937695","n1820938413","n1820938555","n1820937703","n1820938536","n1820938196","n1820938287","n1820938169","n1820939279","n1820938531","n1820938959","n1820939741","n1820938665","n1820938963","n1820939611","n1820937653","n1820939618","n1820939492","n1820938600","n1820938628","n1820939312","n1820939616","n1820937738","n1820939001","n1820939062","n1820938794","n1820938558","n1820937822","n1820937532","n1820939073","n1820938200","n1820938241","n1820938968","n1820938927","n1820938306","n1820937630","n1820938456","n1820937694","n1820938908","n1820939076","n1820937522","n1820939659","n1820938522","n1820939318","n1820938932","n1820938841","n1820937579","n1820937540","n1820938560","n1821139530","n1820938964","n1820937662","n1820939281","n1821139533","n1820937797","n1821139532","n1820939751","n1821139531","n1820939291","n1820938420","n1820939696","n1820938904","n1820938484","n1820939448","n1820939009","n1820938735","n1820938986","n1820938937","n1820939030","n1820938734","n1820938745","n1820939106","n1820938987","n1820937858","n1820938673","n1820938620","n1820937808","n1820937700","n1820939573","n1820938540","n1820937661","n1820937570","n1820938396","n1820937875","n1820939048","n1820938233","n1820938793","n1820939584","n1820938412","n1820938394","n1820937846","n1820938800","n1820938690","n1820939331","n1820939630","n1820938762","n1820938710","n1820939322","n1820938992","n1821137608","n1821137607","n1820937924","n1820939139","n1820939463","n1820939574","n1820938294","n1820938071","n1820938307","n1820938061","n1820939260","n1820937899","n1820938310","n1820938983","n1820937530","n1820938993","n1820938890","n1820937915","n1820938231","n1820938040","n1820938920","n1820939585","n1820938135","n1820939700","n1820937824","n1820939667","n1820937930","n1820938134","n1820937551","n1820939405","n1820938232","n1820937716","n1820937848","n1820939765","n1820939068","n1820939766","n1820937933","n1820937720","n1820939222","n1820939772","n1820939022","n1820939732","n1820937702","n1820937691","n1820938945","n1820937756","n1820938451","n1820938410","n1820938798","n1820937945","n1820937654","n1820938598","n1820938836","n1820937571","n1820937556","n1820938994","n1820938919","n1820938863","n1820939064","n1820938018","n1820937658","n1820937537","n1820938142","n1820938666","n1820937535","n1820939571","n1820938465","n1820939638","n1820937533","n1820939656","n1820939422","n1820938109","n1820938405","n1820938028","n1820937649","n1820938829","n1820939031","n1820939155","n1820938350","n1820938463","n1820938425","n1820939047","n1820938831","n1820938494","n1820937697","n1820938504","n1820938900","n1820937784","n1820938414","n1820938076","n1820938723","n1820937722","n1820938739","n1820937791","n1820938985","n1820938352","n1820938293","n1820938274","n1820939692","n1820937871","n1820939059","n1820938868","n1820937877","n1820937743","n1820938429","n1820937545","n1820937575","n1820938302","n1820938505","n1820938916","n1820938374","n1820938329","n1820937790","n1820939735","n1820938930","n1820937995","n1820938512","n1820938130","n1820938194","n1820938671","n1820938802","n1820937542","n1820937602","n1820939069","n1820938901","n1820939654","n1820937727","n1820939569","n1820938375","n1820939306","n1820938479","n1820938376","n1820938667","n1820937766","n1820939467","n1820939567","n1820937806","n1820938943","n1820938931","n1820937745","n1820939452","n1820938738","n1820938053","n1820939653","n1820938640","n1820937604","n1820937536","n1820938701","n1820939625","n1820939744","n1820939572","n1820937577","n1820937541","n1820938891","n1820937597","n1820938469","n1820939194","n1820937539","n1820938911","n1820939017","n1820939650","n1820939103","n1820939578","n1820938132","n1820937549","n1820938634","n1820939743","n1820937544","n1820937826","n1820937598","n1820937547","n1820938032","n1820939142"]},"w17963021":{"id":"w17963021","tags":{"highway":"residential"},"nodes":["n185948706","n185948708","n185948710"]},"w203974069":{"id":"w203974069","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139870431","n2139870432","n2139870433","n2139870434","n2139870431"]},"w209816575":{"id":"w209816575","tags":{"area":"yes","building":"yes"},"nodes":["n2199856288","n2199856289","n2199856290","n2199856291","n2199856292","n2199856293","n2199856294","n2199856295","n2199856296","n2199856297","n2199856298","n2199856299","n2199856300","n2199856301","n2199856302","n2199856303","n2199856288"]},"w203841838":{"id":"w203841838","tags":{"area":"yes","natural":"water"},"nodes":["n2138493826","n2138493827","n2138493828","n2138493829","n2138493830","n2138493831","n2138493833","n2138493832","n2138493826"]},"w203972937":{"id":"w203972937","tags":{"highway":"path","name":"Riverwalk Trail","surface":"asphalt","width":"3"},"nodes":["n2139858882","n2139858883","n2139858884","n2139858885","n2139858886","n2139858887","n2139858882","n2139858888","n2139858889","n2139858890","n2139858891","n2139858892","n2139858893","n2139858894","n2139858895","n2139858896","n2139858897","n2139858898","n2139858899","n2139858900","n2139858901","n2139858902","n2139858903","n2139858986","n2139858904","n2139858995","n2139858905","n2139858906","n2139858907","n2139858908","n2139858909","n2139858910","n2139858911","n2139858912","n2139858913","n2139858914","n2139858915","n2139858916","n2139858917","n2139858918","n2139858919","n2139858920","n2139858921","n2139858922","n2139858923","n2139858924","n2139858925","n2139858926","n2139858927","n2139858982","n2139858928","n2139858929","n2139858930","n2139858931","n2139858932","n2139858981","n2139858933","n2139858934","n2139858935","n2139858936","n2139858937","n2139858938","n2139858939","n2139858940","n2139858941","n2139858942","n2139858943","n2140006437","n2139858964","n2139858944","n2139858966","n2139858945","n2139858946","n2139858947","n2139858948","n2139858949","n2139858950","n2139858951"]},"w17964015":{"id":"w17964015","tags":{"highway":"residential"},"nodes":["n185954680","n185954683","n185954685","n185954687","n185954689","n185954690","n185954691","n2139870379","n2139870456","n185954692","n185954693","n185954695"]},"w17967315":{"id":"w17967315","tags":{"highway":"residential","name":"South Andrews Street"},"nodes":["n185981999","n185974477","n185964963"]},"w203974071":{"id":"w203974071","tags":{"highway":"footway"},"nodes":["n2139870439","n2139870440","n2139870441","n2139870442","n2139870443","n2139870444","n2139870445","n2139870446","n2139870447","n2139870448","n2139870449"]},"w170848824":{"id":"w170848824","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819858503","n1819858531","n1819858526","n1819858518","n1819858505","n1819858508","n1819858512","n1819858514","n1819858528","n1819858509","n1819858511","n1819858507","n1819858521"]},"w203986458":{"id":"w203986458","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139989357","n2139989359","n2139989360","n2139989362","n2139989357"]},"w170844917":{"id":"w170844917","tags":{"waterway":"riverbank"},"nodes":["n1819805911","n1819805690","n1819805812","n1819805766","n1819805802","n1819805885","n1819805626","n1819805842","n1819805715","n1819805694","n1819805618","n1819805629","n1819805731","n1819805636","n1819805878","n1819805718","n1819805798","n1819849057","n1819805666","n1819805852","n1819805805","n1819805789","n1819805868","n1819805680","n1819805918","n1819848888","n1819805762","n2139989328","n1819805907","n2139989330","n1819805915","n1819858521","n1819805854","n1819805876","n1819805864","n1819805922","n2139859004","n1819805702","n2139859003","n1819805614","n1819805792","n1819805786","n1819805777","n1819805645","n1819805838","n1819805889","n1819805795","n1819805707","n1819805774","n1819805808","n1819805810","n1819805724","n1819805676","n1819805728","n1819805783","n1819805687","n1819805727","n2189123379","n1819805632","n1819805641","n1819805760","n1819805887","n1819805861","n1819805722","n1819805880","n2139982405","n2139982399","n2139982400","n1819805770","n2139982402","n2139982403","n2139982401","n1819805780","n1819805834","n2139982406","n1819805698","n1819805647","n1819805870","n1819805683","n1819805622","n1819805639","n1819805858","n1819805643","n1819805673","n1819805925","n1819805849","n1819805711","n1819805846","n1819805669","n1819805883","n1819805814","n1819805873","n1819805911"]},"w17967326":{"id":"w17967326","tags":{"highway":"residential","name":"North Constantine Street"},"nodes":["n185985217","n185985219","n185985221","n185985222","n185985223","n185985225","n2140006431","n185985227","n185985229","n185985231","n185985233","n185985235","n185985238","n185985240","n2140018998","n185964965"]},"w134150789":{"id":"w134150789","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964971","n2139870406","n185964972"]},"w17966400":{"id":"w17966400","tags":{"highway":"tertiary","name":"South Constantine Street"},"nodes":["n185958672","n185964965"]},"w203974066":{"id":"w203974066","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139870417","n2139870418","n2139870420","n2139870419"]},"w17965998":{"id":"w17965998","tags":{"name":"Conrail Railroad","railway":"rail"},"nodes":["n185972775","n185972777","n185972779","n185972781","n185972783","n185972785","n185972787","n185972788","n185972789","n185972790","n185972791","n185972793","n185972795","n185972797","n185972798","n185972800","n185972802","n185972805","n185972807","n185972809","n185972811","n185972813","n185972814","n185972815","n185972816","n185972817","n185972819","n185972821","n185972824","n185972826","n185972830","n185972832","n185972834","n185972835","n185972836","n185972839","n185990434","n2114807572","n2114807568","n185972845","n2114807583","n185972847","n185972849","n185972851","n2114807578","n1475293254","n2114807593","n1475293226","n185972862","n2114807565","n185951869","n1475293234","n1475293252","n185972868","n1475293264","n1475293222","n185972878","n1475293261","n185972882","n185972885","n1475293260","n1475293240","n185972891","n185972895","n185972897","n185972899","n2130304159","n1475284023","n185972903"]},"w134150795":{"id":"w134150795","tags":{"bridge":"yes","highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964970","n185964971"]},"w203974067":{"id":"w203974067","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139870420","n2139870421"]},"w170995908":{"id":"w170995908","tags":{"highway":"residential","name":"Thomas Street"},"nodes":["n1821006702","n1821006700","n1821006698","n2139858990","n1821006716","n1821006725","n1821006712","n1821006704","n1821006708","n1821006710","n1821006706"]},"w17965834":{"id":"w17965834","tags":{"highway":"residential","name":"Spring Street"},"nodes":["n185971361","n185971364","n185971366","n185971368","n185954695","n185964968"]},"w203974070":{"id":"w203974070","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139870435","n2139870436","n2139870437","n2139870438","n2139870435"]},"w203989879":{"id":"w203989879","tags":{"highway":"service"},"nodes":["n2140018998","n2140018999","n2140019000"]},"w203974062":{"id":"w203974062","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870387","n2139870388","n2139870389","n2139870390","n2139870391","n2139870392","n2139870397","n2139870393","n2139870396","n2139870395","n2139870394","n2139870387"]},"w203974061":{"id":"w203974061","tags":{"bridge":"yes","highway":"footway"},"nodes":["n2139870382","n2139870383"]},"w203049587":{"id":"w203049587","tags":{"area":"yes","name":"Scidmore Park Petting Zoo","tourism":"zoo","zoo":"petting_zoo"},"nodes":["n2130304133","n2130304136","n2130304138","n2130304140","n2130304142","n2130304144","n2130304146","n2130304147","n2130304148","n2130304149","n2130304150","n2130304151","n2130304133"]},"w203972941":{"id":"w203972941","tags":{"highway":"path"},"nodes":["n2139858982","n2139858983","n2139858984","n2139858985","n2139858927"]},"w203974065":{"id":"w203974065","tags":{"highway":"service"},"nodes":["n2139870406","n2139870407","n2139870408","n2139870417","n2139870409","n2139870410","n2139870411","n2139870412","n2139870426","n2139870413","n2139870414","n2139870415","n2139870419","n2139870416","n2139870421","n2139870408"]},"w203972940":{"id":"w203972940","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858934","n2139858967","n2139858968","n2139858969","n2139858970","n2139858971","n2139858972","n2139858973","n2139858974","n2139858975","n2139858976","n2139858977","n2139858978","n2139858979","n2139858980","n2139858981"]},"w203974072":{"id":"w203974072","tags":{"highway":"footway"},"nodes":["n2139858925","n2139870450","n2139870453","n2139870451","n2139870452","n2139870441"]},"w203974074":{"id":"w203974074","tags":{"highway":"footway"},"nodes":["n2139870454","n2139870456","n2139870429"]},"w203974060":{"id":"w203974060","tags":{"highway":"footway"},"nodes":["n2139870383","n2139870384","n2139870422","n2139870385","n2139870386","n2139870388"]},"w203841837":{"id":"w203841837","tags":{"area":"yes","natural":"water"},"nodes":["n2138493807","n2138493808","n2138493809","n2138493810","n2138493811","n2138493812","n2138493813","n2138493814","n2138493815","n2138493816","n2138493825","n2138493817","n2138493824","n2138493818","n2138493819","n2138493820","n2138493821","n2138493822","n2138493823","n2138493807"]},"w134150845":{"id":"w134150845","tags":{"bridge":"yes","name":"Conrail Railroad","railway":"rail"},"nodes":["n185972903","n185972905"]},"w203974059":{"id":"w203974059","tags":{"highway":"footway"},"nodes":["n2139870430","n2139870439","n2139870429","n2139870428","n2139870379","n2139870455","n2139870380","n2139870381","n2139858925","n2139870382"]},"w203986457":{"id":"w203986457","tags":{"area":"yes","leisure":"park","name":"Scidmore Park"},"nodes":["n2139989333","n2139989335","n2139989337","n2139989339","n1819805762","n2139989328","n1819805907","n2139989330","n1819805915","n2139989341","n2139989344","n2139989346","n2139989348","n2139989350","n2139989351","n2139989353","n2139989355","n2139989333"]},"w170848331":{"id":"w170848331","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819848937","n1819849104","n1819849076","n1819849183","n1819848928","n1819848972","n1819848948","n1819848971","n1819848859","n1819849008","n1819848889","n1819849026","n1819849094","n1819849083","n1819849079","n1819849187","n1819848992","n1819849060","n1819849056","n1819849071","n1819849067","n1819849048","n1819849036","n1819849150","n1819849075","n1819849051","n1819849062","n1819848926","n1819849035","n1819848987","n1819849012","n1819848933","n1819848996","n1819848990","n1819849005","n1819849021","n1819848892","n1819849092","n1819848863","n1819848922","n1819848858","n1819848855","n1819848974","n1819848953","n1819849019","n1819849049","n1819848979","n1819849140","n1819849193","n1819849147","n1819849151","n1819849163","n1819849023","n1819848878","n1819849004","n1819848857","n1819848879","n1819849041","n1819849165","n1819849107","n1819849156","n1819848934","n1819848914","n1819848955","n1819848931","n1819848927","n1819849084","n1819849169","n1819849045","n1819848945","n1819849095","n1819848924","n1819849171","n1819849141","n1819849046","n1819849197","n1819849011","n1819849108","n1819849158","n1819849160","n1819848870","n1819849006","n1819849157","n1819848993","n1819848970","n1819849202","n1819848903","n1819848975","n1819848849","n1819849025","n1819849105","n1819849033","n1819849176","n1819849099","n1819849086","n1819848960","n1819848961","n1819849001","n1819848980","n1819849038","n1819848854","n1819849127","n1819849170","n1819849139","n1819848873","n1819848929","n1819849201","n1819849121","n1819849031","n1819849131","n1819848875","n1819849080","n1819849066","n1819849081","n1819849096","n1819849172","n1819849114","n1819849182","n1819848905","n1819849054","n1819848920","n1819848851","n1819848968","n1819848917","n1819849111","n1819849119","n1819849074","n1819848893","n1819849129","n1819848850","n1819848956","n1819849154","n1819848877","n1819848986","n1819849191","n1819848952","n1819848954","n1819848942","n1819849028","n1819849195","n1819848938","n1819848962","n1819849070","n1819849034","n1819849052","n1819849059","n1819848916","n1819849162","n1819849167","n1819849093","n1819849030","n1819849002","n1819849161","n1819848886","n1819848958","n1819849064","n1819849112","n1819849148","n1819848856","n1819848976","n1819848977","n1819849144","n1819848918","n1819849200","n1819848919","n1819849042","n1819849166","n1819849186","n1819849152","n1819849058","n1819849185","n1819849199","n1819849053","n1819849194","n1819849068","n1819849146","n1819849174","n1819848967","n1819848932","n1819849155","n1819849198","n1819848964","n1819848894","n1819848969","n1819849184","n1819849055","n1819849179","n1819848865","n1819848860","n1819849082","n1819848966","n1819849040","n1819849069","n1819849078","n1819849077","n1819848904","n1819848959","n1819849133","n1819849089","n1819849000","n1819849124","n1819849032","n1819849097","n1819848939","n1819849072","n1819848915","n1819849196","n1819848946","n1819849047","n1819849029","n1819849164","n1819848994","n1819849022","n1819858513","n1819849126","n1819849063","n1819848941","n1819849085","n1819848871","n1819848943","n1819849192","n1819858501","n1819849159","n1819858523","n1819848901","n1819849189","n1819858503","n1819849065","n2139877106","n1819848909","n1819848930","n1819848888"]},"w17967397":{"id":"w17967397","tags":{"highway":"residential","name":"North Andrews Street"},"nodes":["n185964963","n185985217"]},"w17964497":{"id":"w17964497","tags":{"highway":"tertiary","name":"Constantine St"},"nodes":["n185958643","n185958645","n2139795852","n185958647","n185958649","n185958651","n185958653","n185958656","n185958658","n185958660","n185958662","n185958664","n185958666","n185958668","n185958670","n185948710","n185958672"]},"w203974068":{"id":"w203974068","tags":{"highway":"footway"},"nodes":["n2139870422","n2139870423","n2139870424","n2139870425","n2139870426","n2139870427"]},"w203974063":{"id":"w203974063","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870398","n2139870399","n2139870400","n2139870401","n2139870398"]},"w203986459":{"id":"w203986459","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139989364","n2139989366","n2139989368","n2139989370","n2139989364"]},"w203988286":{"id":"w203988286","tags":{"area":"yes","leisure":"park","name":"Memory Isle Park"},"nodes":["n2140006331","n2140006334","n2140006336","n2140006338","n2140006340","n2140006342","n2140006344","n2140006346","n2140006348","n2140006351","n2140006353","n2140006355","n2140006357","n2140006359","n2140006361","n2140006363","n2140006364","n2140006365","n2140006395","n2140006366","n2140006401","n2140006397","n2140006399","n2140006331"]},"w203974073":{"id":"w203974073","tags":{"highway":"footway"},"nodes":["n2139870453","n2139870454","n2139870455"]},"w203974064":{"id":"w203974064","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870402","n2139870403","n2139870404","n2139870405","n2139870402"]},"n185966959":{"id":"n185966959","loc":[-85.642185,41.946411]},"n1475283980":{"id":"n1475283980","loc":[-85.6398249,41.9451425]},"n1475284013":{"id":"n1475284013","loc":[-85.6396448,41.9451666]},"n1475284042":{"id":"n1475284042","loc":[-85.6386382,41.9454789]},"n185975925":{"id":"n185975925","loc":[-85.6393332,41.9452388]},"n185975919":{"id":"n185975919","loc":[-85.6391279,41.9453044]},"n185975917":{"id":"n185975917","loc":[-85.6389034,41.9453872]},"n2140006369":{"id":"n2140006369","loc":[-85.6386163,41.9451631]},"n2140006370":{"id":"n2140006370","loc":[-85.6385144,41.9449357]},"n2140006417":{"id":"n2140006417","loc":[-85.6385785,41.9450299]},"n2140006419":{"id":"n2140006419","loc":[-85.6385781,41.9452152]},"n2189123361":{"id":"n2189123361","loc":[-85.6404948,41.947015]},"n2189123363":{"id":"n2189123363","loc":[-85.6395765,41.946495]},"n2189123365":{"id":"n2189123365","loc":[-85.6389347,41.9460875]},"n185966962":{"id":"n185966962","loc":[-85.644417,41.946364]},"n185975911":{"id":"n185975911","loc":[-85.637532,41.9458276]},"n185975913":{"id":"n185975913","loc":[-85.6376323,41.9457936]},"n185975915":{"id":"n185975915","loc":[-85.6383596,41.9455425]},"n185975932":{"id":"n185975932","loc":[-85.644403,41.945088]},"n185975934":{"id":"n185975934","loc":[-85.645486,41.945084]},"n185979974":{"id":"n185979974","loc":[-85.644381,41.943831]},"n2139795809":{"id":"n2139795809","loc":[-85.6464756,41.9450813]},"n2139795810":{"id":"n2139795810","loc":[-85.6466646,41.945174]},"n2139858952":{"id":"n2139858952","loc":[-85.6383567,41.9454039]},"n2139858953":{"id":"n2139858953","loc":[-85.6380506,41.9455301]},"n2139858954":{"id":"n2139858954","loc":[-85.6377321,41.9455546]},"n2139858955":{"id":"n2139858955","loc":[-85.6376571,41.9455245]},"n2139858956":{"id":"n2139858956","loc":[-85.6375859,41.9454544]},"n2139858957":{"id":"n2139858957","loc":[-85.6376686,41.9453185]},"n2139858958":{"id":"n2139858958","loc":[-85.6378936,41.9451712]},"n2139858959":{"id":"n2139858959","loc":[-85.6379225,41.9450825]},"n2139858960":{"id":"n2139858960","loc":[-85.6379302,41.9447564]},"n2139858961":{"id":"n2139858961","loc":[-85.6379763,41.9446963]},"n2139858962":{"id":"n2139858962","loc":[-85.6380436,41.9446706]},"n2139858963":{"id":"n2139858963","loc":[-85.6381286,41.9445969]},"n2139858965":{"id":"n2139858965","loc":[-85.6382523,41.9444134]},"n2140006367":{"id":"n2140006367","loc":[-85.6380923,41.9454418]},"n2140006368":{"id":"n2140006368","loc":[-85.6384089,41.9453146]},"n2140006372":{"id":"n2140006372","loc":[-85.6383252,41.9447706]},"n2140006374":{"id":"n2140006374","loc":[-85.6381033,41.9447436]},"n2140006376":{"id":"n2140006376","loc":[-85.6379759,41.9447815]},"n2140006378":{"id":"n2140006378","loc":[-85.6379832,41.9448654]},"n2140006380":{"id":"n2140006380","loc":[-85.6380632,41.9450738]},"n2140006382":{"id":"n2140006382","loc":[-85.6380414,41.9452064]},"n2140006389":{"id":"n2140006389","loc":[-85.6379068,41.9453092]},"n2140006391":{"id":"n2140006391","loc":[-85.637925,41.9453904]},"n2140006393":{"id":"n2140006393","loc":[-85.6379977,41.94545]},"n2189123275":{"id":"n2189123275","loc":[-85.6371346,41.9462544]},"n2189123278":{"id":"n2189123278","loc":[-85.6368371,41.9466153]},"n2189123280":{"id":"n2189123280","loc":[-85.6379537,41.9489088]},"n2189123282":{"id":"n2189123282","loc":[-85.6383816,41.9497858]},"n2189123285":{"id":"n2189123285","loc":[-85.6393673,41.9512417]},"n2189123287":{"id":"n2189123287","loc":[-85.640554,41.9517766]},"n2189123289":{"id":"n2189123289","loc":[-85.6411,41.9522344]},"n2189123291":{"id":"n2189123291","loc":[-85.6417418,41.9526574]},"n2189123293":{"id":"n2189123293","loc":[-85.642321,41.9529407]},"n2189123295":{"id":"n2189123295","loc":[-85.6427697,41.9532278]},"n2189123297":{"id":"n2189123297","loc":[-85.6433332,41.9538254]},"n2189123300":{"id":"n2189123300","loc":[-85.6435785,41.9543648]},"n2189123301":{"id":"n2189123301","loc":[-85.6444394,41.9541048]},"n2189123303":{"id":"n2189123303","loc":[-85.6450603,41.954]},"n2189123312":{"id":"n2189123312","loc":[-85.6454829,41.9539108]},"n2189123314":{"id":"n2189123314","loc":[-85.6460464,41.9538526]},"n2189123315":{"id":"n2189123315","loc":[-85.6463178,41.9537167]},"n2189123316":{"id":"n2189123316","loc":[-85.646276,41.9534141]},"n2189123317":{"id":"n2189123317","loc":[-85.6459995,41.9531541]},"n2189123318":{"id":"n2189123318","loc":[-85.645222,41.9531929]},"n2189123319":{"id":"n2189123319","loc":[-85.6447316,41.9531813]},"n2189123320":{"id":"n2189123320","loc":[-85.6440637,41.9532977]},"n2189123321":{"id":"n2189123321","loc":[-85.6438185,41.9531774]},"n2189123322":{"id":"n2189123322","loc":[-85.6440011,41.9528398]},"n2189123323":{"id":"n2189123323","loc":[-85.6442672,41.9525914]},"n2189123324":{"id":"n2189123324","loc":[-85.6442881,41.9523276]},"n2189123326":{"id":"n2189123326","loc":[-85.644262,41.952153]},"n2189123328":{"id":"n2189123328","loc":[-85.6441681,41.9520404]},"n2189123330":{"id":"n2189123330","loc":[-85.6442098,41.9517494]},"n2189123333":{"id":"n2189123333","loc":[-85.6438498,41.9515864]},"n2189123336":{"id":"n2189123336","loc":[-85.6435889,41.9513225]},"n2189123339":{"id":"n2189123339","loc":[-85.6425349,41.9510315]},"n2189123342":{"id":"n2189123342","loc":[-85.6422688,41.9508802]},"n2189123345":{"id":"n2189123345","loc":[-85.6418775,41.9508142]},"n2189123348":{"id":"n2189123348","loc":[-85.6415488,41.9508064]},"n2189123351":{"id":"n2189123351","loc":[-85.6411027,41.9505488]},"n2189123353":{"id":"n2189123353","loc":[-85.6410374,41.9498208]},"n2189123355":{"id":"n2189123355","loc":[-85.6410061,41.9494327]},"n2189123357":{"id":"n2189123357","loc":[-85.6411522,41.9482569]},"n2189123359":{"id":"n2189123359","loc":[-85.6410548,41.9473036]},"n2189123368":{"id":"n2189123368","loc":[-85.6380216,41.9458974]},"n2189123370":{"id":"n2189123370","loc":[-85.6386721,41.9507782]},"w17968193":{"id":"w17968193","tags":{"highway":"residential","name":"French St"},"nodes":["n185970906","n185982877","n185967774","n185985823","n185979974"]},"w203972939":{"id":"w203972939","tags":{"highway":"path"},"nodes":["n2139858965","n2139858966"]},"w203988289":{"id":"w203988289","tags":{"area":"yes","natural":"water"},"nodes":["n2140006367","n2140006368","n2140006419","n2140006369","n2140006417","n2140006370","n2140006372","n2140006374","n2140006376","n2140006378","n2140006380","n2140006382","n2140006389","n2140006391","n2140006393","n2140006367"]},"w208640157":{"id":"w208640157","tags":{"area":"yes","natural":"wetland"},"nodes":["n1819849029","n2189123275","n2189123278","n2189123280","n2189123282","n2189123370","n2189123285","n2189123287","n2189123289","n2189123291","n2189123293","n2189123295","n2189123297","n2189123300","n2189123301","n2189123303","n2189123312","n2189123314","n2189123315","n2189123316","n2189123317","n2189123318","n2189123319","n2189123320","n2189123321","n2189123322","n2189123323","n2189123324","n2189123326","n2189123328","n2189123330","n2189123333","n2189123336","n2189123339","n2189123342","n2189123345","n2189123348","n2189123351","n2189123353","n2189123355","n2189123357","n2189123359","n2189123361","n2189123363","n2189123365","n2189123368","n1819849029"]},"w17966281":{"id":"w17966281","tags":{"highway":"residential","name":"Pealer St"},"nodes":["n185975911","n185975913","n185975915","n1475284042","n185975917","n185975919","n185975925","n185970909","n1475284013","n1475283980","n185975928","n185967775","n185975930","n185975932","n185975934","n2139795809","n2139795810"]},"w17965353":{"id":"w17965353","tags":{"highway":"residential","name":"Yauney St"},"nodes":["n185966958","n185966959","n185966960","n185966962"]},"w203972938":{"id":"w203972938","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858964","n2139858965","n2139858963","n2139858962","n2139858961","n2139858960","n2139858959","n2139858958","n2139858957","n2139858956","n2139858955","n2139858954","n2139858953","n2139858952","n2139858951"]},"n354002665":{"id":"n354002665","loc":[-85.6366599,41.9444923],"tags":{"name":"Memory Isle","place":"island"}},"n354031301":{"id":"n354031301","loc":[-85.635,41.9463889],"tags":{"amenity":"post_office","name":"Three Rivers Post Office"}},"n185963454":{"id":"n185963454","loc":[-85.633686,41.946072]},"n185963455":{"id":"n185963455","loc":[-85.633815,41.946131]},"n185963456":{"id":"n185963456","loc":[-85.633951,41.946174]},"n185978375":{"id":"n185978375","loc":[-85.634385,41.94559]},"n185978377":{"id":"n185978377","loc":[-85.634544,41.945725]},"n185978379":{"id":"n185978379","loc":[-85.634573,41.945764]},"n185978381":{"id":"n185978381","loc":[-85.634616,41.945849]},"n185978383":{"id":"n185978383","loc":[-85.634629,41.945893]},"n185984011":{"id":"n185984011","loc":[-85.636058,41.946201]},"n185984013":{"id":"n185984013","loc":[-85.636112,41.946366]},"n185984015":{"id":"n185984015","loc":[-85.636143,41.946551]},"n185988237":{"id":"n185988237","loc":[-85.6354162,41.946044]},"n185988969":{"id":"n185988969","loc":[-85.635374,41.945325]},"n185988971":{"id":"n185988971","loc":[-85.635643,41.945585]},"n185988972":{"id":"n185988972","loc":[-85.635853,41.94586]},"n1475283992":{"id":"n1475283992","loc":[-85.6372968,41.9459007]},"n1475284011":{"id":"n1475284011","loc":[-85.6359415,41.9459797]},"n1475284019":{"id":"n1475284019","loc":[-85.6364433,41.9460423]},"n185984009":{"id":"n185984009","loc":[-85.6360524,41.9460485]},"n185988239":{"id":"n185988239","loc":[-85.6358187,41.9460423]},"n185988243":{"id":"n185988243","loc":[-85.6366156,41.9460282]},"n185988244":{"id":"n185988244","loc":[-85.6368316,41.9460046]},"n185988245":{"id":"n185988245","loc":[-85.6370133,41.9459704]},"n185988241":{"id":"n185988241","loc":[-85.636291,41.9460461]},"n185964976":{"id":"n185964976","loc":[-85.633923,41.9434157]},"n185964980":{"id":"n185964980","loc":[-85.6333656,41.9437293]},"n185978388":{"id":"n185978388","loc":[-85.6346449,41.9460571]},"n1819858504":{"id":"n1819858504","loc":[-85.6365343,41.9447926]},"n1819858506":{"id":"n1819858506","loc":[-85.6370546,41.9451882]},"n1819858516":{"id":"n1819858516","loc":[-85.6358369,41.9444654]},"n1819858519":{"id":"n1819858519","loc":[-85.6361534,41.9446176]},"n1819858525":{"id":"n1819858525","loc":[-85.6368025,41.9449442]},"n1819858527":{"id":"n1819858527","loc":[-85.6334199,41.9457495]},"n185963452":{"id":"n185963452","loc":[-85.633564,41.9458519]},"n185963453":{"id":"n185963453","loc":[-85.6336152,41.9459804]},"n185963451":{"id":"n185963451","loc":[-85.6332888,41.9456871]},"n2130304152":{"id":"n2130304152","loc":[-85.6359466,41.9454599]},"n2130304153":{"id":"n2130304153","loc":[-85.6362773,41.9452683]},"n2130304154":{"id":"n2130304154","loc":[-85.6352028,41.9442868]},"n2130304155":{"id":"n2130304155","loc":[-85.6348756,41.9444769]},"n2130304156":{"id":"n2130304156","loc":[-85.6349723,41.9444207]},"n2130304157":{"id":"n2130304157","loc":[-85.6338698,41.9434443]},"n2130304158":{"id":"n2130304158","loc":[-85.635094,41.9451026]},"n2130304160":{"id":"n2130304160","loc":[-85.6353716,41.9449322]},"n2130304162":{"id":"n2130304162","loc":[-85.6365942,41.9459352]},"n2130304163":{"id":"n2130304163","loc":[-85.6369006,41.9457469]},"n2130304164":{"id":"n2130304164","loc":[-85.6363292,41.9452278]},"n2130304165":{"id":"n2130304165","loc":[-85.6360248,41.9454175]},"n2139824683":{"id":"n2139824683","loc":[-85.6339825,41.9446441]},"n2139824689":{"id":"n2139824689","loc":[-85.6340437,41.9446925]},"n2139824702":{"id":"n2139824702","loc":[-85.6340961,41.9447551]},"n2139824705":{"id":"n2139824705","loc":[-85.6337467,41.944809]},"n2139824707":{"id":"n2139824707","loc":[-85.6341598,41.9448129]},"n2139824710":{"id":"n2139824710","loc":[-85.6342771,41.9448223]},"n2139824712":{"id":"n2139824712","loc":[-85.6346058,41.944841]},"n2139824713":{"id":"n2139824713","loc":[-85.633808,41.9448574]},"n2139824714":{"id":"n2139824714","loc":[-85.6340889,41.9448589]},"n2139824716":{"id":"n2139824716","loc":[-85.6343335,41.944871]},"n2139824717":{"id":"n2139824717","loc":[-85.6343341,41.9448717]},"n2139824720":{"id":"n2139824720","loc":[-85.6338757,41.9449069]},"n2139824721":{"id":"n2139824721","loc":[-85.6341445,41.9449071]},"n2139824724":{"id":"n2139824724","loc":[-85.6334787,41.9449262]},"n2139824726":{"id":"n2139824726","loc":[-85.6347119,41.9449332]},"n2139824727":{"id":"n2139824727","loc":[-85.6347175,41.9449418]},"n2139824728":{"id":"n2139824728","loc":[-85.6344284,41.9449538]},"n2139824729":{"id":"n2139824729","loc":[-85.6339339,41.9449573]},"n2139824730":{"id":"n2139824730","loc":[-85.6339179,41.9449682]},"n2139824732":{"id":"n2139824732","loc":[-85.6335472,41.9449895]},"n2139824733":{"id":"n2139824733","loc":[-85.6339736,41.9450164]},"n2139824735":{"id":"n2139824735","loc":[-85.6336034,41.9450415]},"n2139824736":{"id":"n2139824736","loc":[-85.6348317,41.945043]},"n2139824737":{"id":"n2139824737","loc":[-85.63403,41.9450651]},"n2139824738":{"id":"n2139824738","loc":[-85.6336611,41.9450949]},"n2139824740":{"id":"n2139824740","loc":[-85.6336582,41.9450966]},"n2139824744":{"id":"n2139824744","loc":[-85.6331702,41.9451107]},"n2139824745":{"id":"n2139824745","loc":[-85.6333388,41.9451142]},"n2139824746":{"id":"n2139824746","loc":[-85.6337131,41.9451341]},"n2139824747":{"id":"n2139824747","loc":[-85.6337021,41.9451372]},"n2139824748":{"id":"n2139824748","loc":[-85.6341244,41.9451472]},"n2139824749":{"id":"n2139824749","loc":[-85.6333952,41.945166]},"n2139824750":{"id":"n2139824750","loc":[-85.633395,41.9451661]},"n2139824751":{"id":"n2139824751","loc":[-85.6346258,41.9451725]},"n2139824752":{"id":"n2139824752","loc":[-85.6332387,41.9451741]},"n2139824753":{"id":"n2139824753","loc":[-85.6346901,41.9451853]},"n2139824754":{"id":"n2139824754","loc":[-85.6346611,41.9452035]},"n2139824755":{"id":"n2139824755","loc":[-85.6346574,41.9452059]},"n2139824756":{"id":"n2139824756","loc":[-85.6345611,41.9452133]},"n2139824757":{"id":"n2139824757","loc":[-85.633453,41.9452194]},"n2139824758":{"id":"n2139824758","loc":[-85.6335508,41.9452283]},"n2139824759":{"id":"n2139824759","loc":[-85.6347424,41.9452312]},"n2139824760":{"id":"n2139824760","loc":[-85.6342305,41.9452395]},"n2139824761":{"id":"n2139824761","loc":[-85.6342319,41.9452449]},"n2139824762":{"id":"n2139824762","loc":[-85.6334969,41.94526]},"n2139824763":{"id":"n2139824763","loc":[-85.63468,41.9452706]},"n2139824764":{"id":"n2139824764","loc":[-85.6346772,41.9452724]},"n2139824765":{"id":"n2139824765","loc":[-85.6338611,41.9452763]},"n2139824766":{"id":"n2139824766","loc":[-85.6347811,41.9452939]},"n2139824767":{"id":"n2139824767","loc":[-85.6347375,41.9453211]},"n2139824768":{"id":"n2139824768","loc":[-85.6339171,41.9453301]},"n2139824769":{"id":"n2139824769","loc":[-85.6348307,41.9453377]},"n2139824770":{"id":"n2139824770","loc":[-85.6347067,41.9453405]},"n2139824771":{"id":"n2139824771","loc":[-85.6343461,41.9453461]},"n2139824772":{"id":"n2139824772","loc":[-85.6343481,41.9453475]},"n2139824773":{"id":"n2139824773","loc":[-85.634805,41.9453538]},"n2139824774":{"id":"n2139824774","loc":[-85.6336997,41.9453692]},"n2139824775":{"id":"n2139824775","loc":[-85.6339709,41.9453818]},"n2139824776":{"id":"n2139824776","loc":[-85.6336229,41.9454134]},"n2139824777":{"id":"n2139824777","loc":[-85.6349022,41.9454141]},"n2139824778":{"id":"n2139824778","loc":[-85.6348854,41.9454246]},"n2139824779":{"id":"n2139824779","loc":[-85.6340286,41.9454373]},"n2139824780":{"id":"n2139824780","loc":[-85.6336963,41.9454572]},"n2139824781":{"id":"n2139824781","loc":[-85.6336789,41.9454672]},"n2139824782":{"id":"n2139824782","loc":[-85.6344933,41.945475]},"n2139824783":{"id":"n2139824783","loc":[-85.6340854,41.9454918]},"n2139824784":{"id":"n2139824784","loc":[-85.6350036,41.9455034]},"n2139824785":{"id":"n2139824785","loc":[-85.6337501,41.9455089]},"n2139824786":{"id":"n2139824786","loc":[-85.6337497,41.9455091]},"n2139824787":{"id":"n2139824787","loc":[-85.6345425,41.9455186]},"n2139824788":{"id":"n2139824788","loc":[-85.6341459,41.9455372]},"n2139824789":{"id":"n2139824789","loc":[-85.6341376,41.945542]},"n2139824790":{"id":"n2139824790","loc":[-85.6338394,41.9455462]},"n2139824791":{"id":"n2139824791","loc":[-85.6349171,41.9455588]},"n2139824792":{"id":"n2139824792","loc":[-85.6338074,41.9455646]},"n2139824793":{"id":"n2139824793","loc":[-85.6346229,41.9455894]},"n2139824794":{"id":"n2139824794","loc":[-85.6338983,41.9455995]},"n2139824795":{"id":"n2139824795","loc":[-85.6338962,41.9456007]},"n2139824796":{"id":"n2139824796","loc":[-85.6342475,41.9456348]},"n2139824797":{"id":"n2139824797","loc":[-85.6339505,41.9456497]},"n2139824798":{"id":"n2139824798","loc":[-85.6347243,41.9456788]},"n2139824799":{"id":"n2139824799","loc":[-85.635057,41.9456831]},"n2139824800":{"id":"n2139824800","loc":[-85.635287,41.9457056]},"n2139824801":{"id":"n2139824801","loc":[-85.6350753,41.9457068]},"n2139824802":{"id":"n2139824802","loc":[-85.6347753,41.9457252]},"n2139824803":{"id":"n2139824803","loc":[-85.6340521,41.9457473]},"n2139824804":{"id":"n2139824804","loc":[-85.6352875,41.9457611]},"n2139824805":{"id":"n2139824805","loc":[-85.6352941,41.9457611]},"n2139824806":{"id":"n2139824806","loc":[-85.6350758,41.9457623]},"n2139824807":{"id":"n2139824807","loc":[-85.6348194,41.9457638]},"n2139824808":{"id":"n2139824808","loc":[-85.635296,41.9459428]},"n2139824809":{"id":"n2139824809","loc":[-85.6348212,41.9459455]},"n2139832635":{"id":"n2139832635","loc":[-85.6354612,41.9448791]},"n2139832636":{"id":"n2139832636","loc":[-85.6360241,41.9453844]},"n2139832637":{"id":"n2139832637","loc":[-85.6361452,41.9453121]},"n2139832639":{"id":"n2139832639","loc":[-85.6355997,41.944797]},"n2139832641":{"id":"n2139832641","loc":[-85.6351346,41.9443541]},"n2139832647":{"id":"n2139832647","loc":[-85.6329883,41.9453692]},"n2139832653":{"id":"n2139832653","loc":[-85.6333643,41.9456293]},"n2139832663":{"id":"n2139832663","loc":[-85.6335394,41.9455339]},"n2139832665":{"id":"n2139832665","loc":[-85.6332375,41.9452476]},"n2139832667":{"id":"n2139832667","loc":[-85.6331664,41.9452161]},"n2139832669":{"id":"n2139832669","loc":[-85.6331144,41.9451875]},"n2139832671":{"id":"n2139832671","loc":[-85.6330779,41.9451274]},"n2139832673":{"id":"n2139832673","loc":[-85.6330664,41.9450802]},"n2139832678":{"id":"n2139832678","loc":[-85.6332218,41.9453585]},"n2139832686":{"id":"n2139832686","loc":[-85.6334246,41.945541]},"n2139832691":{"id":"n2139832691","loc":[-85.6329898,41.9454997]},"n2139832693":{"id":"n2139832693","loc":[-85.6343554,41.9443274]},"n2139832694":{"id":"n2139832694","loc":[-85.6336339,41.9437089]},"n2139832696":{"id":"n2139832696","loc":[-85.633532,41.9437708]},"n2139832697":{"id":"n2139832697","loc":[-85.6338316,41.9440868]},"n2139832698":{"id":"n2139832698","loc":[-85.6342258,41.9444141]},"n2139832699":{"id":"n2139832699","loc":[-85.6339164,41.9442166]},"n2139832700":{"id":"n2139832700","loc":[-85.6341389,41.944384]},"n2139832701":{"id":"n2139832701","loc":[-85.634235,41.9443259]},"n2139832702":{"id":"n2139832702","loc":[-85.633613,41.9437875]},"n2139832703":{"id":"n2139832703","loc":[-85.633915,41.9436132]},"n2139832704":{"id":"n2139832704","loc":[-85.6340019,41.9435613]},"n2139832706":{"id":"n2139832706","loc":[-85.6343197,41.9438427]},"n2139832708":{"id":"n2139832708","loc":[-85.6342361,41.9438936]},"n2139832709":{"id":"n2139832709","loc":[-85.6353839,41.9460401]},"n2139832710":{"id":"n2139832710","loc":[-85.6354032,41.9456763]},"n2139832711":{"id":"n2139832711","loc":[-85.6356839,41.9459252]},"n2139832712":{"id":"n2139832712","loc":[-85.6356109,41.945735]},"n2139832713":{"id":"n2139832713","loc":[-85.6353997,41.9457421]},"n2139832714":{"id":"n2139832714","loc":[-85.6353895,41.9459347]},"n2139832715":{"id":"n2139832715","loc":[-85.6334777,41.9436628]},"n2139832716":{"id":"n2139832716","loc":[-85.6333137,41.9435382]},"n2139832717":{"id":"n2139832717","loc":[-85.6330938,41.9435406]},"n2139832721":{"id":"n2139832721","loc":[-85.6333023,41.9434922]},"n2139832722":{"id":"n2139832722","loc":[-85.6330466,41.943623]},"n2139832723":{"id":"n2139832723","loc":[-85.6332746,41.9435624]},"n2139832724":{"id":"n2139832724","loc":[-85.6333511,41.9435176]},"n2139832725":{"id":"n2139832725","loc":[-85.6332241,41.9434001]},"n2139832726":{"id":"n2139832726","loc":[-85.6332355,41.9433686]},"n2139870373":{"id":"n2139870373","loc":[-85.6351783,41.9439117]},"n2139870374":{"id":"n2139870374","loc":[-85.6351431,41.9439217]},"n2139870375":{"id":"n2139870375","loc":[-85.6348853,41.9439117]},"n2139870376":{"id":"n2139870376","loc":[-85.6348317,41.9439105]},"n2139870377":{"id":"n2139870377","loc":[-85.6346384,41.944007]},"n2139870378":{"id":"n2139870378","loc":[-85.6345563,41.9440523]},"n2140006403":{"id":"n2140006403","loc":[-85.6359942,41.9450097]},"n2140006405":{"id":"n2140006405","loc":[-85.6363884,41.9446079]},"n2140006407":{"id":"n2140006407","loc":[-85.6362148,41.9447874]},"n2140006409":{"id":"n2140006409","loc":[-85.6379476,41.9445869]},"n2140006411":{"id":"n2140006411","loc":[-85.6378485,41.9445674]},"n2140006413":{"id":"n2140006413","loc":[-85.6378952,41.9444547]},"n2140006415":{"id":"n2140006415","loc":[-85.6379962,41.944477]},"n2140006421":{"id":"n2140006421","loc":[-85.6355248,41.9433702]},"n2140006423":{"id":"n2140006423","loc":[-85.6378471,41.9439233]},"n2140006425":{"id":"n2140006425","loc":[-85.6378913,41.9441238]},"n2140006426":{"id":"n2140006426","loc":[-85.6381674,41.9442289]},"n2140006427":{"id":"n2140006427","loc":[-85.6382359,41.9440975]},"n2140006428":{"id":"n2140006428","loc":[-85.6382071,41.9440252]},"n2140006429":{"id":"n2140006429","loc":[-85.6381409,41.9439973]},"n2140006430":{"id":"n2140006430","loc":[-85.6380569,41.9440153]},"n2140006433":{"id":"n2140006433","loc":[-85.6379071,41.9442467]},"n2140006435":{"id":"n2140006435","loc":[-85.6381634,41.9443125]},"n2140006436":{"id":"n2140006436","loc":[-85.6382407,41.944301]},"n2140006438":{"id":"n2140006438","loc":[-85.6382761,41.9442188]},"n2140006439":{"id":"n2140006439","loc":[-85.6382429,41.9441761]},"n2140006440":{"id":"n2140006440","loc":[-85.6382016,41.9441632]},"n2140006441":{"id":"n2140006441","loc":[-85.6378185,41.9439835]},"n2166205688":{"id":"n2166205688","loc":[-85.6349963,41.9444392]},"n2168544780":{"id":"n2168544780","loc":[-85.633944,41.945807]},"n2168544781":{"id":"n2168544781","loc":[-85.6340783,41.9458621]},"n2168544782":{"id":"n2168544782","loc":[-85.6338184,41.9457548]},"n2168544783":{"id":"n2168544783","loc":[-85.6339925,41.9459777]},"n2168544784":{"id":"n2168544784","loc":[-85.6337317,41.9458698]},"n2168544785":{"id":"n2168544785","loc":[-85.6337297,41.9460042]},"n2168544786":{"id":"n2168544786","loc":[-85.633919,41.9460797]},"n2168544787":{"id":"n2168544787","loc":[-85.6338672,41.9459263]},"n2168544788":{"id":"n2168544788","loc":[-85.6338246,41.9459853]},"n2168544789":{"id":"n2168544789","loc":[-85.6337615,41.9459601]},"n2168544790":{"id":"n2168544790","loc":[-85.6342079,41.9460399]},"n2168544791":{"id":"n2168544791","loc":[-85.6343346,41.9458503]},"n2168544792":{"id":"n2168544792","loc":[-85.6343759,41.9458116]},"n2168544793":{"id":"n2168544793","loc":[-85.6344394,41.9458109]},"n2168544795":{"id":"n2168544795","loc":[-85.6344827,41.945851]},"n2168544797":{"id":"n2168544797","loc":[-85.6344807,41.945969]},"n2168544798":{"id":"n2168544798","loc":[-85.6344404,41.9459697]},"n2168544799":{"id":"n2168544799","loc":[-85.6344413,41.9460333]},"n2168544800":{"id":"n2168544800","loc":[-85.6342173,41.9460705]},"n2168544801":{"id":"n2168544801","loc":[-85.6342162,41.9460392]},"n2168544802":{"id":"n2168544802","loc":[-85.6344251,41.9460351]},"n2168544805":{"id":"n2168544805","loc":[-85.6344257,41.9460507]},"n2168544807":{"id":"n2168544807","loc":[-85.6344721,41.9460498]},"n2168544809":{"id":"n2168544809","loc":[-85.6344754,41.9461427]},"n2168544811":{"id":"n2168544811","loc":[-85.6344311,41.9461435]},"n2168544813":{"id":"n2168544813","loc":[-85.6344317,41.9461592]},"n2168544815":{"id":"n2168544815","loc":[-85.6343708,41.9461604]},"n2168544817":{"id":"n2168544817","loc":[-85.6343715,41.9461786]},"n2168544819":{"id":"n2168544819","loc":[-85.6343229,41.9461795]},"n2168544821":{"id":"n2168544821","loc":[-85.6343222,41.9461606]},"n2168544823":{"id":"n2168544823","loc":[-85.6342476,41.9461621]},"n2168544825":{"id":"n2168544825","loc":[-85.6342444,41.94607]},"n2168544827":{"id":"n2168544827","loc":[-85.634138,41.9461632]},"n2168544829":{"id":"n2168544829","loc":[-85.6342016,41.9460703]},"n2168544830":{"id":"n2168544830","loc":[-85.6332929,41.9463092]},"n2168544831":{"id":"n2168544831","loc":[-85.633122,41.946239]},"n2168544832":{"id":"n2168544832","loc":[-85.6332954,41.9460055]},"n2168544833":{"id":"n2168544833","loc":[-85.6333954,41.9460466]},"n2168544834":{"id":"n2168544834","loc":[-85.6334044,41.9460345]},"n2168544835":{"id":"n2168544835","loc":[-85.6334594,41.9460571]},"n2168544836":{"id":"n2168544836","loc":[-85.6333871,41.9461544]},"n2168544837":{"id":"n2168544837","loc":[-85.633403,41.9461609]},"n2168544838":{"id":"n2168544838","loc":[-85.6341683,41.9464167]},"n2168544839":{"id":"n2168544839","loc":[-85.6341711,41.9463411]},"n2168544840":{"id":"n2168544840","loc":[-85.6344471,41.9463469]},"n2168544841":{"id":"n2168544841","loc":[-85.6344441,41.9464243]},"n2168544842":{"id":"n2168544842","loc":[-85.6343622,41.9464226]},"n2168544843":{"id":"n2168544843","loc":[-85.6343593,41.9464989]},"n2168544844":{"id":"n2168544844","loc":[-85.6342812,41.9464973]},"n2168544845":{"id":"n2168544845","loc":[-85.634283,41.9464504]},"n2168544846":{"id":"n2168544846","loc":[-85.6342609,41.9464499]},"n2168544847":{"id":"n2168544847","loc":[-85.6342621,41.9464187]},"n2168544848":{"id":"n2168544848","loc":[-85.6348414,41.9463396]},"n2168544849":{"id":"n2168544849","loc":[-85.6348387,41.9461872]},"n2168544850":{"id":"n2168544850","loc":[-85.6351186,41.9461844]},"n2168544851":{"id":"n2168544851","loc":[-85.635119,41.9462112]},"n2168544852":{"id":"n2168544852","loc":[-85.6351918,41.9462104]},"n2168544853":{"id":"n2168544853","loc":[-85.6351944,41.9463515]},"n2168544854":{"id":"n2168544854","loc":[-85.6351049,41.9463524]},"n2168544855":{"id":"n2168544855","loc":[-85.6351046,41.946337]},"n2189153180":{"id":"n2189153180","loc":[-85.6340369,41.9469572]},"n2189153181":{"id":"n2189153181","loc":[-85.6342531,41.946953]},"n2189153183":{"id":"n2189153183","loc":[-85.6348115,41.9465468]},"n2189153184":{"id":"n2189153184","loc":[-85.6348105,41.9464569]},"n2189153185":{"id":"n2189153185","loc":[-85.6351431,41.9464549]},"n2189153186":{"id":"n2189153186","loc":[-85.6351441,41.9465448]},"n2189153187":{"id":"n2189153187","loc":[-85.6350077,41.9465456]},"n2189153188":{"id":"n2189153188","loc":[-85.635008,41.9465721]},"n2189153189":{"id":"n2189153189","loc":[-85.6348965,41.9465727]},"n2189153190":{"id":"n2189153190","loc":[-85.6348962,41.9465463]},"n2189153191":{"id":"n2189153191","loc":[-85.6348963,41.9471586]},"n2189153192":{"id":"n2189153192","loc":[-85.6348944,41.947032]},"n2189153193":{"id":"n2189153193","loc":[-85.6350241,41.947031]},"n2189153194":{"id":"n2189153194","loc":[-85.635026,41.9471575]},"n2189153195":{"id":"n2189153195","loc":[-85.6352328,41.9471053]},"n2189153196":{"id":"n2189153196","loc":[-85.6352359,41.9469906]},"n2189153197":{"id":"n2189153197","loc":[-85.6353694,41.9469925]},"n2189153198":{"id":"n2189153198","loc":[-85.6353664,41.9471072]},"n2189153199":{"id":"n2189153199","loc":[-85.6348241,41.9469287]},"n2189153200":{"id":"n2189153200","loc":[-85.6348248,41.9468185]},"n2189153201":{"id":"n2189153201","loc":[-85.6351199,41.9468195]},"n2189153202":{"id":"n2189153202","loc":[-85.6351192,41.9469298]},"n2189153203":{"id":"n2189153203","loc":[-85.6347965,41.9468057]},"n2189153204":{"id":"n2189153204","loc":[-85.634792,41.9466044]},"n2189153205":{"id":"n2189153205","loc":[-85.6349483,41.9466025]},"n2189153206":{"id":"n2189153206","loc":[-85.6349493,41.9466448]},"n2189153207":{"id":"n2189153207","loc":[-85.6349753,41.9466445]},"n2189153208":{"id":"n2189153208","loc":[-85.6349743,41.9465995]},"n2189153209":{"id":"n2189153209","loc":[-85.6351173,41.9465977]},"n2189153210":{"id":"n2189153210","loc":[-85.6351219,41.9468015]},"n2189153211":{"id":"n2189153211","loc":[-85.6349806,41.9468032]},"n2189153212":{"id":"n2189153212","loc":[-85.6349794,41.9467519]},"n2189153213":{"id":"n2189153213","loc":[-85.6349521,41.9467523]},"n2189153214":{"id":"n2189153214","loc":[-85.6349532,41.9468037]},"n2189153215":{"id":"n2189153215","loc":[-85.6346302,41.9468381]},"n2189153216":{"id":"n2189153216","loc":[-85.6343028,41.9468449]},"n2189153217":{"id":"n2189153217","loc":[-85.6342006,41.9468297]},"n2189153218":{"id":"n2189153218","loc":[-85.6336698,41.9465918]},"n2189153219":{"id":"n2189153219","loc":[-85.6344663,41.9466639]},"n2189153220":{"id":"n2189153220","loc":[-85.6344639,41.9466015]},"n2189153221":{"id":"n2189153221","loc":[-85.6342283,41.9466065]},"n2189153222":{"id":"n2189153222","loc":[-85.6342303,41.9466587]},"n2189153223":{"id":"n2189153223","loc":[-85.6342843,41.9466575]},"n2189153224":{"id":"n2189153224","loc":[-85.6342851,41.9466794]},"n2189153225":{"id":"n2189153225","loc":[-85.6343475,41.9466781]},"n2189153226":{"id":"n2189153226","loc":[-85.634347,41.9466664]},"n2189153227":{"id":"n2189153227","loc":[-85.6354428,41.9470148]},"n2189153228":{"id":"n2189153228","loc":[-85.6354432,41.9468005]},"n2189153229":{"id":"n2189153229","loc":[-85.6360277,41.9468011]},"n2189153230":{"id":"n2189153230","loc":[-85.6360273,41.9470154]},"n2189153231":{"id":"n2189153231","loc":[-85.6354565,41.9465823]},"n2189153232":{"id":"n2189153232","loc":[-85.6354496,41.946218]},"n2189153233":{"id":"n2189153233","loc":[-85.6356355,41.9465788]},"n2189153234":{"id":"n2189153234","loc":[-85.6357155,41.9468008]},"n2189153235":{"id":"n2189153235","loc":[-85.6359539,41.9467969]},"n2189153236":{"id":"n2189153236","loc":[-85.6359561,41.9463036]},"n2189153237":{"id":"n2189153237","loc":[-85.6360129,41.9464793]},"n2189153238":{"id":"n2189153238","loc":[-85.6360152,41.9463898]},"n2189153239":{"id":"n2189153239","loc":[-85.6359607,41.9464928]},"n2189153240":{"id":"n2189153240","loc":[-85.6356903,41.9462227]},"n2189153242":{"id":"n2189153242","loc":[-85.6354163,41.946142]},"n2189153243":{"id":"n2189153243","loc":[-85.6357546,41.9462214]},"n2189153244":{"id":"n2189153244","loc":[-85.6357937,41.9462542]},"n2189153245":{"id":"n2189153245","loc":[-85.6358723,41.9467048]},"n2189153246":{"id":"n2189153246","loc":[-85.6361494,41.946757]},"n2189153247":{"id":"n2189153247","loc":[-85.6354173,41.9469082]},"n2189153248":{"id":"n2189153248","loc":[-85.635443,41.9469079]},"n2189153249":{"id":"n2189153249","loc":[-85.6360275,41.9469093]},"n2189153250":{"id":"n2189153250","loc":[-85.6361542,41.946915]},"n2189153251":{"id":"n2189153251","loc":[-85.6358654,41.9464843]},"n2189153252":{"id":"n2189153252","loc":[-85.6359549,41.9467499]},"n2189153253":{"id":"n2189153253","loc":[-85.6357172,41.9466335]},"n2189153254":{"id":"n2189153254","loc":[-85.6355644,41.9461768]},"n2189153255":{"id":"n2189153255","loc":[-85.6355655,41.946528]},"n2189153256":{"id":"n2189153256","loc":[-85.6357055,41.9465971]},"n2189153257":{"id":"n2189153257","loc":[-85.635869,41.9465971]},"n2189153259":{"id":"n2189153259","loc":[-85.6354561,41.9470278]},"n2189153260":{"id":"n2189153260","loc":[-85.6357961,41.9470233]},"n2189153261":{"id":"n2189153261","loc":[-85.6357977,41.9470907]},"n2189153262":{"id":"n2189153262","loc":[-85.6357297,41.9470916]},"n2189153263":{"id":"n2189153263","loc":[-85.635733,41.947233]},"n2189153264":{"id":"n2189153264","loc":[-85.6362674,41.9468637]},"n2189153265":{"id":"n2189153265","loc":[-85.6362646,41.9467047]},"n2189153266":{"id":"n2189153266","loc":[-85.6363267,41.9467047]},"n2189153267":{"id":"n2189153267","loc":[-85.6362633,41.9465848]},"n2189153268":{"id":"n2189153268","loc":[-85.6363805,41.9465468]},"n2189153269":{"id":"n2189153269","loc":[-85.6364604,41.9466842]},"n2189153270":{"id":"n2189153270","loc":[-85.6364604,41.9468647]},"n2199109756":{"id":"n2199109756","loc":[-85.6337134,41.9471841]},"n2199109757":{"id":"n2199109757","loc":[-85.6336514,41.94716]},"n2199109758":{"id":"n2199109758","loc":[-85.6337043,41.9470847]},"n2199109759":{"id":"n2199109759","loc":[-85.6335997,41.9470441]},"n2199109760":{"id":"n2199109760","loc":[-85.6335064,41.9471771]},"n185960195":{"id":"n185960195","loc":[-85.6295992,41.9524346]},"n185960796":{"id":"n185960796","loc":[-85.634723,41.953681]},"n185961396":{"id":"n185961396","loc":[-85.634767,41.959009]},"n185962625":{"id":"n185962625","loc":[-85.635175,41.97201]},"n185964982":{"id":"n185964982","loc":[-85.632799,41.9440543]},"n185965289":{"id":"n185965289","loc":[-85.634621,41.947323]},"n185965291":{"id":"n185965291","loc":[-85.636166,41.947296]},"n185965399":{"id":"n185965399","loc":[-85.634776,41.959834]},"n185966937":{"id":"n185966937","loc":[-85.633183,41.947315]},"n185966948":{"id":"n185966948","loc":[-85.626406,41.957188]},"n185967422":{"id":"n185967422","loc":[-85.6320229,41.9490123]},"n185967917":{"id":"n185967917","loc":[-85.634763,41.958292]},"n185967918":{"id":"n185967918","loc":[-85.636271,41.958311]},"n185968100":{"id":"n185968100","loc":[-85.630835,41.950656]},"n185970515":{"id":"n185970515","loc":[-85.634832,41.963866]},"n185971578":{"id":"n185971578","loc":[-85.634641,41.948627]},"n185971580":{"id":"n185971580","loc":[-85.6361818,41.9486135]},"n185971631":{"id":"n185971631","loc":[-85.634729,41.954667]},"n185971632":{"id":"n185971632","loc":[-85.636236,41.954656]},"n185972155":{"id":"n185972155","loc":[-85.623333,41.961987]},"n185974583":{"id":"n185974583","loc":[-85.634686,41.951158]},"n185974585":{"id":"n185974585","loc":[-85.6362059,41.9511457]},"n185975064":{"id":"n185975064","loc":[-85.636218,41.953667]},"n185975735":{"id":"n185975735","loc":[-85.634923,41.969269]},"n185978390":{"id":"n185978390","loc":[-85.634668,41.949875]},"n185978392":{"id":"n185978392","loc":[-85.634686,41.952415]},"n185978394":{"id":"n185978394","loc":[-85.634726,41.955921]},"n185978399":{"id":"n185978399","loc":[-85.6347861,41.9606613]},"n185978402":{"id":"n185978402","loc":[-85.634806,41.961485]},"n185978406":{"id":"n185978406","loc":[-85.6348298,41.964783]},"n185978410":{"id":"n185978410","loc":[-85.6348766,41.9677088]},"n185978414":{"id":"n185978414","loc":[-85.634938,41.971566]},"n185978415":{"id":"n185978415","loc":[-85.634942,41.971611]},"n185978417":{"id":"n185978417","loc":[-85.634952,41.971655]},"n185978419":{"id":"n185978419","loc":[-85.634989,41.971741]},"n185978420":{"id":"n185978420","loc":[-85.635063,41.971864]},"n185978787":{"id":"n185978787","loc":[-85.627936,41.954693]},"n185978790":{"id":"n185978790","loc":[-85.626832,41.954677]},"n185978967":{"id":"n185978967","loc":[-85.632278,41.948613]},"n185980735":{"id":"n185980735","loc":[-85.628639,41.953725]},"n185982163":{"id":"n185982163","loc":[-85.636233,41.952398]},"n185982193":{"id":"n185982193","loc":[-85.6313855,41.9499125]},"n185982195":{"id":"n185982195","loc":[-85.6304857,41.9511945]},"n185982196":{"id":"n185982196","loc":[-85.626336,41.957291]},"n185982197":{"id":"n185982197","loc":[-85.625578,41.958664]},"n185982198":{"id":"n185982198","loc":[-85.624619,41.960145]},"n185982200":{"id":"n185982200","loc":[-85.624494,41.960338]},"n185984017":{"id":"n185984017","loc":[-85.636163,41.947382]},"n185984020":{"id":"n185984020","loc":[-85.636188,41.9498803]},"n185984022":{"id":"n185984022","loc":[-85.636276,41.955919]},"n185984024":{"id":"n185984024","loc":[-85.636279,41.956901]},"n185988036":{"id":"n185988036","loc":[-85.631422,41.948294]},"n185988867":{"id":"n185988867","loc":[-85.63102,41.948805]},"n185988869":{"id":"n185988869","loc":[-85.630773,41.949209]},"n185988871":{"id":"n185988871","loc":[-85.63005,41.95016]},"n185988872":{"id":"n185988872","loc":[-85.629423,41.951016]},"n185988873":{"id":"n185988873","loc":[-85.629252,41.951256]},"n185988875":{"id":"n185988875","loc":[-85.629126,41.951489]},"n185988877":{"id":"n185988877","loc":[-85.628991,41.951704]},"n185988878":{"id":"n185988878","loc":[-85.628689,41.952112]},"n185988879":{"id":"n185988879","loc":[-85.628313,41.952666]},"n185988880":{"id":"n185988880","loc":[-85.627687,41.953529]},"n185988882":{"id":"n185988882","loc":[-85.627394,41.953947]},"n185988884":{"id":"n185988884","loc":[-85.627287,41.954128]},"n1819858502":{"id":"n1819858502","loc":[-85.6328435,41.9455473]},"n1819858510":{"id":"n1819858510","loc":[-85.6324841,41.9453438]},"n1819858515":{"id":"n1819858515","loc":[-85.6318511,41.9446409]},"n1819858520":{"id":"n1819858520","loc":[-85.6326558,41.9454708]},"n1819858522":{"id":"n1819858522","loc":[-85.6319048,41.9447407]},"n1819858524":{"id":"n1819858524","loc":[-85.6317718,41.9443666]},"n1819858530":{"id":"n1819858530","loc":[-85.632055,41.9449128]},"n2139795768":{"id":"n2139795768","loc":[-85.6243023,41.9606102]},"n2139832645":{"id":"n2139832645","loc":[-85.6324455,41.9448607]},"n2139832649":{"id":"n2139832649","loc":[-85.6328043,41.9454773]},"n2139832651":{"id":"n2139832651","loc":[-85.6322547,41.9449621]},"n2139832675":{"id":"n2139832675","loc":[-85.6327356,41.944757]},"n2139832677":{"id":"n2139832677","loc":[-85.6325433,41.9448599]},"n2139832680":{"id":"n2139832680","loc":[-85.6328885,41.9455614]},"n2139832682":{"id":"n2139832682","loc":[-85.6320913,41.9449492]},"n2139832684":{"id":"n2139832684","loc":[-85.6325366,41.9447133]},"n2139832688":{"id":"n2139832688","loc":[-85.6322786,41.94485]},"n2139832718":{"id":"n2139832718","loc":[-85.6327486,41.9432475]},"n2139832719":{"id":"n2139832719","loc":[-85.6327926,41.9431773]},"n2139832720":{"id":"n2139832720","loc":[-85.6329033,41.943153]},"n2139832727":{"id":"n2139832727","loc":[-85.6328975,41.9430783]},"n2139844839":{"id":"n2139844839","loc":[-85.6326261,41.9432308]},"n2189015992":{"id":"n2189015992","loc":[-85.6347706,41.9593383]},"n2189153179":{"id":"n2189153179","loc":[-85.6340476,41.9472565]},"n2189153182":{"id":"n2189153182","loc":[-85.6342638,41.9472522]},"n2189153241":{"id":"n2189153241","loc":[-85.6354184,41.9473091]},"n2189153258":{"id":"n2189153258","loc":[-85.6354611,41.9472366]},"n2189153277":{"id":"n2189153277","loc":[-85.6328948,41.9462374]},"n2199109755":{"id":"n2199109755","loc":[-85.6336729,41.9472417]},"w203970139":{"id":"w203970139","tags":{"building":"yes"},"nodes":["n2139824793","n2139824787","n2139824773","n2139824778","n2139824793"]},"w203970098":{"id":"w203970098","tags":{"building":"yes"},"nodes":["n2139824748","n2139824712","n2139824726","n2139824760","n2139824748"]},"w208643132":{"id":"w208643132","tags":{"area":"yes","building":"yes"},"nodes":["n2189153195","n2189153196","n2189153197","n2189153198","n2189153195"]},"w203970094":{"id":"w203970094","tags":{"building":"yes"},"nodes":["n2139824755","n2139824753","n2139824759","n2139824764","n2139824763","n2139824767","n2139824770","n2139824782","n2139824772","n2139824756","n2139824751","n2139824754","n2139824755"]},"w208643138":{"id":"w208643138","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153231","n2189153232","n2189153240","n2189153244","n2189153236","n2189153238","n2189153237","n2189153239","n2189153252","n2189153235","n2189153234","n2189153253","n2189153233","n2189153231"]},"w203970125":{"id":"w203970125","tags":{"building":"yes"},"nodes":["n2139824735","n2139824738","n2139824757","n2139824749","n2139824735"]},"w170848823":{"id":"w170848823","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819849189","n1819858516","n1819858519","n1819858504","n1819858525","n1819858506","n1819858513"]},"w203970898":{"id":"w203970898","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832645","n2139832647","n2139832649","n2139832651","n2139832645"]},"w203970134":{"id":"w203970134","tags":{"building":"yes"},"nodes":["n2139824796","n2139824803","n2139824797","n2139824788","n2139824796"]},"w203970104":{"id":"w203970104","tags":{"building":"yes"},"nodes":["n2139824733","n2139824730","n2139824714","n2139824721","n2139824733"]},"w206805245":{"id":"w206805245","tags":{"area":"yes","building":"yes"},"nodes":["n2168544780","n2168544781","n2139824796","n2139824803","n2168544780"]},"w206805252":{"id":"w206805252","tags":{"area":"yes","building":"yes"},"nodes":["n2168544838","n2168544839","n2168544840","n2168544841","n2168544842","n2168544843","n2168544844","n2168544845","n2168544846","n2168544847","n2168544838"]},"w203970099":{"id":"w203970099","tags":{"building":"yes"},"nodes":["n2139824783","n2139824795","n2139824790","n2139824779","n2139824783"]},"w17967730":{"id":"w17967730","tags":{"highway":"residential","name":"Water St"},"nodes":["n185963451","n2189153277","n185988036","n185988867","n185988869","n185988871","n185988872","n185988873","n185988875","n185988877","n185988878","n185988879","n185988880","n185988882","n185988884","n185978790"]},"w208643133":{"id":"w208643133","tags":{"area":"yes","building":"yes"},"nodes":["n2189153199","n2189153200","n2189153201","n2189153202","n2189153199"]},"w203970127":{"id":"w203970127","tags":{"building":"yes"},"nodes":["n2139824794","n2139824783","n2139824789","n2139824797","n2139824794"]},"w208643139":{"id":"w208643139","tags":{"highway":"service"},"nodes":["n185988237","n2189153242","n2189153247","n2189153241"]},"w203988297":{"id":"w203988297","tags":{"amenity":"parking","area":"yes"},"nodes":["n2140006423","n2140006441","n2140006425","n2140006426","n2140006440","n2140006427","n2140006428","n2140006429","n2140006430","n2140006423"]},"w206805250":{"id":"w206805250","tags":{"area":"yes","building":"yes"},"nodes":["n2168544827","n2168544823","n2168544825","n2168544800","n2168544829","n2168544827"]},"w208643140":{"id":"w208643140","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153242","n2189153254","n2189153243","n2189153244","n2189153251","n2189153257","n2189153245","n2189153252","n2189153246"]},"w203974055":{"id":"w203974055","tags":{"bridge":"yes","highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870376","n2139870377"]},"w206805247":{"id":"w206805247","tags":{"area":"yes","building":"yes"},"nodes":["n2168544785","n2168544786","n2168544783","n2168544787","n2168544788","n2168544789","n2168544785"]},"w17964996":{"id":"w17964996","tags":{"highway":"residential","name":"Foster St"},"nodes":["n1819858524","n1819858515","n1819858522","n1819858530","n2139832682","n1819858510","n1819858520","n1819858502","n2139832680","n185963451","n1819858527","n185963452","n185963453","n185963454","n185963455","n185963456"]},"w208643144":{"id":"w208643144","tags":{"area":"yes","building":"yes"},"nodes":["n2189153264","n2189153265","n2189153266","n2189153267","n2189153268","n2189153269","n2189153270","n2189153264"]},"w203970914":{"id":"w203970914","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832722","n2139832723","n2139832724","n2139832725","n2139832726","n2139832727","n2139844839","n2139832722"]},"w208643143":{"id":"w208643143","tags":{"area":"yes","building":"yes"},"nodes":["n2189153258","n2189153259","n2189153260","n2189153261","n2189153262","n2189153263","n2189153258"]},"w203049590":{"id":"w203049590","tags":{"amenity":"parking","area":"yes"},"nodes":["n2130304152","n2130304153","n2140006403","n2130304154","n2130304156","n2130304155","n2130304160","n2130304152"]},"w203974054":{"id":"w203974054","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858971","n2139870373","n2139870374"]},"w203049595":{"id":"w203049595","tags":{"highway":"service"},"nodes":["n2130304158","n2130304159","n2130304160","n2139832635","n2139832639"]},"w203970913":{"id":"w203970913","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832715","n2139832716","n2139832717","n2139832718","n2139832719","n2139832720","n2139832721","n2139832716"]},"w208643134":{"id":"w208643134","tags":{"area":"yes","building":"yes"},"nodes":["n2189153203","n2189153204","n2189153205","n2189153206","n2189153207","n2189153208","n2189153209","n2189153210","n2189153211","n2189153212","n2189153213","n2189153214","n2189153203"]},"w134150808":{"id":"w134150808","tags":{"bridge":"yes","highway":"residential","name":"Moore St"},"nodes":["n185988239","n185984009","n185988241","n1475284019"]},"w203970115":{"id":"w203970115","tags":{"building":"yes"},"nodes":["n2139824761","n2139824727","n2139824736","n2139824771","n2139824761"]},"w208643130":{"id":"w208643130","tags":{"area":"yes","building":"yes"},"nodes":["n2189153183","n2189153184","n2189153185","n2189153186","n2189153187","n2189153188","n2189153189","n2189153190","n2189153183"]},"w206805246":{"id":"w206805246","tags":{"area":"yes","building":"yes"},"nodes":["n2168544782","n2168544780","n2168544781","n2168544783","n2168544787","n2168544784","n2168544782"]},"w203970138":{"id":"w203970138","tags":{"building":"yes"},"nodes":["n2139824729","n2139824720","n2139824702","n2139824707","n2139824729"]},"w203970133":{"id":"w203970133","tags":{"building":"yes"},"nodes":["n2139824748","n2139824737","n2139824717","n2139824728","n2139824748"]},"w203970907":{"id":"w203970907","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832700","n2139832701","n2139832702"]},"w203974056":{"id":"w203974056","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870377","n2139870378"]},"w203970897":{"id":"w203970897","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2130304156","n2166205688","n2139832635","n2139832636","n2139832637","n2139832639","n2139832641","n2166205688"]},"w203974057":{"id":"w203974057","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870375","n2139870376"]},"w203049594":{"id":"w203049594","tags":{"highway":"service"},"nodes":["n2130304156","n2139870378","n2139832706","n2139832704","n2130304157"]},"w203970122":{"id":"w203970122","tags":{"building":"yes"},"nodes":["n2139824757","n2139824740","n2139824747","n2139824762","n2139824757"]},"w208643136":{"id":"w208643136","tags":{"area":"yes","building":"yes"},"nodes":["n2189153219","n2189153220","n2189153221","n2189153222","n2189153223","n2189153224","n2189153225","n2189153226","n2189153219"]},"w203970128":{"id":"w203970128","tags":{"building":"yes"},"nodes":["n2139824732","n2139824752","n2139824744","n2139824724","n2139824732"]},"w203970097":{"id":"w203970097","tags":{"building":"yes"},"nodes":["n2139824737","n2139824733","n2139824710","n2139824716","n2139824737"]},"w203970137":{"id":"w203970137","tags":{"building":"yes"},"nodes":["n2139824765","n2139824774","n2139824758","n2139824746","n2139824765"]},"w134150840":{"id":"w134150840","tags":{"highway":"residential","name":"Moore St"},"nodes":["n1475284019","n185988243","n185988244","n185988245"]},"w17967628":{"id":"w17967628","tags":{"highway":"residential","name":"Moore St"},"nodes":["n185978388","n2139832709","n185988237","n185988239"]},"w203988292":{"id":"w203988292","tags":{"bridge":"yes","highway":"footway"},"nodes":["n2140006407","n2140006405"]},"w203970118":{"id":"w203970118","tags":{"building":"yes"},"nodes":["n2139824775","n2139824785","n2139824780","n2139824768","n2139824775"]},"w203970121":{"id":"w203970121","tags":{"building":"yes"},"nodes":["n2139824768","n2139824781","n2139824776","n2139824765","n2139824768"]},"w17967752":{"id":"w17967752","tags":{"highway":"residential","name":"Railroad Drive"},"nodes":["n185964980","n2139832699","n2139832700","n2130304158","n185988969","n185988971","n185988972","n1475284011"]},"w203970136":{"id":"w203970136","tags":{"building":"yes"},"nodes":["n2139824798","n2139824793","n2139824777","n2139824784","n2139824798"]},"w203970142":{"id":"w203970142","tags":{"building":"yes"},"nodes":["n2139824808","n2139824809","n2139824807","n2139824806","n2139824801","n2139824800","n2139824804","n2139824805","n2139824808"]},"w208643137":{"id":"w208643137","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153227","n2189153248","n2189153228","n2189153234","n2189153235","n2189153229","n2189153249","n2189153230","n2189153227"]},"w208643129":{"id":"w208643129","tags":{"area":"yes","building":"yes"},"nodes":["n2189153179","n2189153180","n2189153181","n2189153182","n2189153179"]},"w203970909":{"id":"w203970909","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832703","n2139832704","n2139832706","n2139832708","n2139832703"]},"w203970905":{"id":"w203970905","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832688","n2139832691"]},"w203988298":{"id":"w203988298","tags":{"highway":"service"},"nodes":["n2140006431","n2140006433","n2140006435","n2140006436","n2140006437","n2140006438","n2140006439","n2140006440"]},"w203970106":{"id":"w203970106","tags":{"building":"yes"},"nodes":["n2139824798","n2139824791","n2139824799","n2139824802","n2139824798"]},"w203970129":{"id":"w203970129","tags":{"building":"yes"},"nodes":["n2139824787","n2139824782","n2139824766","n2139824769","n2139824787"]},"w208643131":{"id":"w208643131","tags":{"area":"yes","building":"yes"},"nodes":["n2189153191","n2189153192","n2189153193","n2189153194","n2189153191"]},"w206805249":{"id":"w206805249","tags":{"area":"yes","building":"yes"},"nodes":["n2168544800","n2168544801","n2168544802","n2168544805","n2168544807","n2168544809","n2168544811","n2168544813","n2168544815","n2168544817","n2168544819","n2168544821","n2168544823","n2168544825","n2168544800"]},"w134150800":{"id":"w134150800","tags":{"bridge":"yes","highway":"primary","name":"W Michigan Ave","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964972","n185964976"]},"w17966984":{"id":"w17966984","tags":{"highway":"residential","name":"Portage Avenue"},"nodes":["n185978375","n185963456","n2189153218","n185966937","n185978967","n185967422","n185982193","n185968100","n185982195","n185960195","n185980735","n185978787","n185966948","n185982196","n185982197","n185982198","n185982200","n2139795768","n185972155"]},"w203988294":{"id":"w203988294","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2140006409","n2140006411","n2140006413","n2140006415","n2140006409"]},"w203970912":{"id":"w203970912","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832711","n2139832712","n2139832713","n2139832714","n2139832711"]},"w203970119":{"id":"w203970119","tags":{"building":"yes"},"nodes":["n2139824713","n2139824705","n2139824683","n2139824689","n2139824713"]},"w203970114":{"id":"w203970114","tags":{"building":"yes"},"nodes":["n2139824735","n2139824750","n2139824745","n2139824732","n2139824735"]},"w208643142":{"id":"w208643142","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153254","n2189153255","n2189153256","n2189153257"]},"w206805253":{"id":"w206805253","tags":{"area":"yes","building":"yes"},"nodes":["n2168544848","n2168544849","n2168544850","n2168544851","n2168544852","n2168544853","n2168544854","n2168544855","n2168544848"]},"w143497377":{"id":"w143497377","tags":{"highway":"primary","name":"North Main Street","old_ref":"US 131","ref":"US 131 Business"},"nodes":["n185962625","n185978420","n185978419","n185978417","n185978415","n185978414","n185975735","n1475293254","n185978410","n185978406","n185970515","n185978402","n185978399","n185965399","n2189015992","n185961396","n185967917","n185978394","n185971631","n185960796","n185978392","n185974583","n185978390","n185971578","n185965289","n2189153215","n185978388","n185978383","n185978381","n185978379","n185978377","n185978375","n185964982"]},"w134150811":{"id":"w134150811","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964976","n2130304157","n1475284023","n2139832715","n185964980","n185964982"]},"w208643135":{"id":"w208643135","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153215","n2189153216","n2189153217","n2189153218"]},"w17967183":{"id":"w17967183","tags":{"highway":"residential","name":"West Street"},"nodes":["n1475284011","n185984011","n185984013","n185984015","n2189153246","n2189153250","n185965291","n185984017","n185971580","n185984020","n185974585","n185982163","n185975064","n185971632","n185984022","n185984024","n185967918"]},"w134150778":{"id":"w134150778","tags":{"bridge":"yes","highway":"residential","name":"Moore St"},"nodes":["n185988245","n1475283992","n185975911"]},"w206805248":{"id":"w206805248","tags":{"area":"yes","building":"yes"},"nodes":["n2168544790","n2168544791","n2168544792","n2168544793","n2168544795","n2168544797","n2168544798","n2168544799","n2168544802","n2168544801","n2168544790"]},"w203974058":{"id":"w203974058","tags":{"bridge":"yes","highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870374","n2139870375"]},"w203970902":{"id":"w203970902","tags":{"highway":"service"},"nodes":["n2139832678","n2139832691","n2139832680"]},"w203988296":{"id":"w203988296","tags":{"highway":"path"},"nodes":["n2139858967","n2140006421","n2139858935"]},"w206805251":{"id":"w206805251","tags":{"area":"yes","building":"yes"},"nodes":["n2168544830","n2168544831","n2168544832","n2168544833","n2168544834","n2168544835","n2168544836","n2168544837","n2168544830"]},"w203970906":{"id":"w203970906","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832693","n2139832694","n2139832696","n2139832697","n2139832698","n2139832693"]},"w203049598":{"id":"w203049598","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2130304162","n2130304163","n2130304164","n2130304165","n2130304162"]},"w203970911":{"id":"w203970911","tags":{"highway":"service"},"nodes":["n2139832709","n2139832714","n2139832713","n2139832710","n185988971"]},"w203970105":{"id":"w203970105","tags":{"building":"yes"},"nodes":["n2139824779","n2139824792","n2139824786","n2139824775","n2139824779"]},"w203988290":{"id":"w203988290","tags":{"highway":"footway"},"nodes":["n2140006403","n2140006407"]},"w203970900":{"id":"w203970900","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832653","n2139832663","n2139832665","n2139832667","n2139832669","n2139832671","n2139832673","n2139832675","n2139832677","n2139832653"]},"w209717048":{"id":"w209717048","tags":{"area":"yes","building":"yes"},"nodes":["n2199109755","n2199109756","n2199109757","n2199109758","n2199109759","n2199109760","n2199109755"]},"w208643141":{"id":"w208643141","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153247","n2189153248","n2189153249","n2189153250"]},"w203970903":{"id":"w203970903","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832682","n2139832688","n2139832684","n2139832678","n2139832686"]},"n354002527":{"id":"n354002527","loc":[-85.6236039,41.9458813],"tags":{"amenity":"school","name":"Barrows School"}},"n185963396":{"id":"n185963396","loc":[-85.627401,41.943496]},"n185963397":{"id":"n185963397","loc":[-85.627403,41.943625]},"n185965101":{"id":"n185965101","loc":[-85.626409,41.943215]},"n185971474":{"id":"n185971474","loc":[-85.624884,41.943508]},"n185971475":{"id":"n185971475","loc":[-85.625191,41.943509]},"n185971482":{"id":"n185971482","loc":[-85.624882,41.94382]},"n185983135":{"id":"n185983135","loc":[-85.624893,41.945616]},"n185983137":{"id":"n185983137","loc":[-85.624912,41.946524]},"n185988027":{"id":"n185988027","loc":[-85.622721,41.946535]},"n185963398":{"id":"n185963398","loc":[-85.6273993,41.9446899]},"n185983238":{"id":"n185983238","loc":[-85.6227157,41.9456321]},"n185980374":{"id":"n185980374","loc":[-85.6248856,41.9447242]},"n185980373":{"id":"n185980373","loc":[-85.6226744,41.9447371]},"n2196831342":{"id":"n2196831342","loc":[-85.6250924,41.945063]},"n2196831343":{"id":"n2196831343","loc":[-85.6252335,41.9450636]},"n2196831344":{"id":"n2196831344","loc":[-85.6252286,41.9448707]},"n2196831345":{"id":"n2196831345","loc":[-85.6250661,41.9448707]},"n2196831346":{"id":"n2196831346","loc":[-85.6250243,41.9449012]},"n2196831347":{"id":"n2196831347","loc":[-85.6250251,41.9449244]},"n2196831348":{"id":"n2196831348","loc":[-85.6250867,41.9449257]},"n2196831349":{"id":"n2196831349","loc":[-85.625349,41.9445058]},"n2196831350":{"id":"n2196831350","loc":[-85.6253471,41.9443882]},"n2196831351":{"id":"n2196831351","loc":[-85.6251516,41.94439]},"n2196831352":{"id":"n2196831352","loc":[-85.6251522,41.9444308]},"n2196831353":{"id":"n2196831353","loc":[-85.6251344,41.9444309]},"n2196831354":{"id":"n2196831354","loc":[-85.6251356,41.9445077]},"n2196831355":{"id":"n2196831355","loc":[-85.6232357,41.9463406]},"n2196831356":{"id":"n2196831356","loc":[-85.6232409,41.9460668]},"n2196831357":{"id":"n2196831357","loc":[-85.6232072,41.9460665]},"n2196831358":{"id":"n2196831358","loc":[-85.6232117,41.9458272]},"n2196831359":{"id":"n2196831359","loc":[-85.6229808,41.9458248]},"n2196831360":{"id":"n2196831360","loc":[-85.6229763,41.9460627]},"n2196831361":{"id":"n2196831361","loc":[-85.623006,41.946063]},"n2196831362":{"id":"n2196831362","loc":[-85.6230023,41.9462557]},"n2196831363":{"id":"n2196831363","loc":[-85.6230755,41.9462565]},"n2196831364":{"id":"n2196831364","loc":[-85.6230739,41.9463389]},"n185947349":{"id":"n185947349","loc":[-85.618327,41.945607]},"n185947359":{"id":"n185947359","loc":[-85.615453,41.945597]},"n185947378":{"id":"n185947378","loc":[-85.617231,41.945603]},"n185947474":{"id":"n185947474","loc":[-85.616136,41.945602]},"n185948972":{"id":"n185948972","loc":[-85.615273,41.945637]},"n185955019":{"id":"n185955019","loc":[-85.620172,41.945627]},"n185960682":{"id":"n185960682","loc":[-85.622759,41.951845]},"n185961369":{"id":"n185961369","loc":[-85.622758,41.947444]},"n185961371":{"id":"n185961371","loc":[-85.624908,41.947416]},"n185963392":{"id":"n185963392","loc":[-85.6270462,41.9409953]},"n185963393":{"id":"n185963393","loc":[-85.627295,41.941304]},"n185963394":{"id":"n185963394","loc":[-85.627352,41.94148]},"n185963395":{"id":"n185963395","loc":[-85.62737,41.942261]},"n185965099":{"id":"n185965099","loc":[-85.6264,41.942263]},"n185965108":{"id":"n185965108","loc":[-85.622769,41.949224]},"n185965110":{"id":"n185965110","loc":[-85.624937,41.949237]},"n185966295":{"id":"n185966295","loc":[-85.6299942,41.9446689]},"n185966342":{"id":"n185966342","loc":[-85.624873,41.942022]},"n185970222":{"id":"n185970222","loc":[-85.622761,41.948357]},"n185970224":{"id":"n185970224","loc":[-85.624924,41.9483338]},"n185971477":{"id":"n185971477","loc":[-85.620051,41.94383]},"n185971478":{"id":"n185971478","loc":[-85.621219,41.943801]},"n185971481":{"id":"n185971481","loc":[-85.621812,41.943807]},"n185973866":{"id":"n185973866","loc":[-85.627629,41.946498]},"n185974699":{"id":"n185974699","loc":[-85.6227688,41.950119]},"n185978800":{"id":"n185978800","loc":[-85.623953,41.954684]},"n185980372":{"id":"n185980372","loc":[-85.621459,41.944756]},"n185980378":{"id":"n185980378","loc":[-85.6286375,41.9446764]},"n185980380":{"id":"n185980380","loc":[-85.630139,41.944661]},"n185980382":{"id":"n185980382","loc":[-85.630298,41.944635]},"n185980384":{"id":"n185980384","loc":[-85.630759,41.94454]},"n185980386":{"id":"n185980386","loc":[-85.6312369,41.9444548]},"n185983133":{"id":"n185983133","loc":[-85.6248672,41.9415903]},"n185983139":{"id":"n185983139","loc":[-85.624951,41.950239]},"n185983140":{"id":"n185983140","loc":[-85.624934,41.950681]},"n185983141":{"id":"n185983141","loc":[-85.624813,41.950983]},"n185983143":{"id":"n185983143","loc":[-85.6246225,41.951591]},"n185983144":{"id":"n185983144","loc":[-85.623908,41.9539165]},"n185983145":{"id":"n185983145","loc":[-85.6238903,41.9540956]},"n185983146":{"id":"n185983146","loc":[-85.623898,41.95431]},"n185983236":{"id":"n185983236","loc":[-85.628481,41.945611]},"n185985914":{"id":"n185985914","loc":[-85.620072,41.946538]},"n185986812":{"id":"n185986812","loc":[-85.6227785,41.9510005]},"n185988028":{"id":"n185988028","loc":[-85.6281401,41.9469632]},"n185988030":{"id":"n185988030","loc":[-85.6282451,41.9470314]},"n185988032":{"id":"n185988032","loc":[-85.6283312,41.9470656]},"w17964989":{"id":"w17964989","tags":{"highway":"residential","name":"Middle St"},"nodes":["n185963392","n185963393","n185963394","n185963395","n185963396","n185963397","n185963398"]},"w17965158":{"id":"w17965158","tags":{"access":"private","highway":"service","name":"Battle St"},"nodes":["n185965099","n185965101"]},"w41074896":{"id":"w41074896","tags":{"highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980372","n185980373","n185980374","n185963398","n185980378","n185966295","n185980380","n185980382","n185980384","n185980386"]},"w17965846":{"id":"w17965846","tags":{"highway":"residential","name":"2nd Ave"},"nodes":["n185971477","n185971478","n185971481","n185971482"]},"w209470306":{"id":"w209470306","tags":{"area":"yes","building":"yes"},"nodes":["n2196831349","n2196831350","n2196831351","n2196831352","n2196831353","n2196831354","n2196831349"]},"w17965845":{"id":"w17965845","tags":{"highway":"residential","name":"2nd Ave"},"nodes":["n185971474","n185971475","n185963396"]},"w209470307":{"id":"w209470307","tags":{"area":"yes","building":"yes"},"nodes":["n2196831355","n2196831356","n2196831357","n2196831358","n2196831359","n2196831360","n2196831361","n2196831362","n2196831363","n2196831364","n2196831355"]},"w17968192":{"id":"w17968192","tags":{"highway":"residential","name":"Washington St"},"nodes":["n185980373","n185983238","n185988027","n185961369","n185970222","n185965108","n185974699","n185986812","n185960682"]},"w17967603":{"id":"w17967603","tags":{"highway":"residential","name":"5th Ave"},"nodes":["n185985914","n185988027","n185983137","n185973866","n185988028","n185988030","n185988032"]},"w209470305":{"id":"w209470305","tags":{"area":"yes","building":"yes"},"nodes":["n2196831342","n2196831343","n2196831344","n2196831345","n2196831346","n2196831347","n2196831348","n2196831342"]},"w17967092":{"id":"w17967092","tags":{"highway":"residential","name":"Wood St"},"nodes":["n185983133","n185966342","n185971474","n185971482","n185980374","n185983135","n185983137","n185961371","n185970224","n185965110","n185983139","n185983140","n185983141","n185983143","n185983144","n185983145","n185983146","n185978800"]},"w17967107":{"id":"w17967107","tags":{"highway":"residential","name":"4th Ave"},"nodes":["n185983236","n185983135","n185983238","n185955019","n185947349","n185947378","n185947474","n185947359","n185948972"]},"n354030330":{"id":"n354030330","loc":[-85.6297222,41.9444444],"tags":{"leisure":"park","name":"Scouter Park"}},"n185966296":{"id":"n185966296","loc":[-85.629998,41.944078]},"n185966298":{"id":"n185966298","loc":[-85.629972,41.943927]},"n185966300":{"id":"n185966300","loc":[-85.629948,41.943783]},"n185980391":{"id":"n185980391","loc":[-85.6322992,41.9442766]},"n185980393":{"id":"n185980393","loc":[-85.6324925,41.9442136]},"n185980389":{"id":"n185980389","loc":[-85.6320272,41.9443281]},"n185980388":{"id":"n185980388","loc":[-85.6315778,41.9443959]},"n354031320":{"id":"n354031320","loc":[-85.6280556,41.9447222],"tags":{"amenity":"place_of_worship","name":"Riverside Church","religion":"christian"}},"n185987309":{"id":"n185987309","loc":[-85.6286497,41.9453531]},"n185987311":{"id":"n185987311","loc":[-85.6285942,41.9454805]},"n185988034":{"id":"n185988034","loc":[-85.6285815,41.9471692]},"n185988896":{"id":"n185988896","loc":[-85.6318433,41.9437929]},"n185977764":{"id":"n185977764","loc":[-85.6322988,41.943472]},"n1819848852":{"id":"n1819848852","loc":[-85.6315188,41.9448808]},"n1819848912":{"id":"n1819848912","loc":[-85.6284289,41.9472189]},"n1819848925":{"id":"n1819848925","loc":[-85.6314501,41.9451617]},"n1819848949":{"id":"n1819848949","loc":[-85.6309394,41.9455192]},"n1819848951":{"id":"n1819848951","loc":[-85.6290297,41.9457187]},"n1819848963":{"id":"n1819848963","loc":[-85.630521,41.9455591]},"n1819848981":{"id":"n1819848981","loc":[-85.6292936,41.9455846]},"n1819848989":{"id":"n1819848989","loc":[-85.6298451,41.9455431]},"n1819848998":{"id":"n1819848998","loc":[-85.6314973,41.9446254]},"n1819849018":{"id":"n1819849018","loc":[-85.6302807,41.9455527]},"n1819849043":{"id":"n1819849043","loc":[-85.6285533,41.9469731]},"n1819849087":{"id":"n1819849087","loc":[-85.6314501,41.9453532]},"n1819849090":{"id":"n1819849090","loc":[-85.628843,41.9461033]},"n1819849109":{"id":"n1819849109","loc":[-85.6311926,41.9454729]},"n1819849116":{"id":"n1819849116","loc":[-85.6288967,41.9459437]},"n1819849177":{"id":"n1819849177","loc":[-85.6287894,41.9464544]},"n1819858529":{"id":"n1819858529","loc":[-85.6325485,41.9445625]},"n2189112797":{"id":"n2189112797","loc":[-85.6275271,41.944555]},"n2189112798":{"id":"n2189112798","loc":[-85.6275196,41.9437258]},"n2189112799":{"id":"n2189112799","loc":[-85.6278937,41.943723]},"n2189112800":{"id":"n2189112800","loc":[-85.6278969,41.9439191]},"n2189112801":{"id":"n2189112801","loc":[-85.6279907,41.9439345]},"n2189112802":{"id":"n2189112802","loc":[-85.6280817,41.9439663]},"n2189112803":{"id":"n2189112803","loc":[-85.6281768,41.9440145]},"n2189112804":{"id":"n2189112804","loc":[-85.6281933,41.9440483]},"n2189112805":{"id":"n2189112805","loc":[-85.6281671,41.9440535]},"n2189112806":{"id":"n2189112806","loc":[-85.6281933,41.9440935]},"n2189112807":{"id":"n2189112807","loc":[-85.6282126,41.9441437]},"n2189112808":{"id":"n2189112808","loc":[-85.628214,41.9441991]},"n2189112809":{"id":"n2189112809","loc":[-85.6283298,41.944196]},"n2189112810":{"id":"n2189112810","loc":[-85.6283285,41.9442616]},"n2189112811":{"id":"n2189112811","loc":[-85.6281727,41.9442616]},"n2189112812":{"id":"n2189112812","loc":[-85.6281713,41.9442934]},"n2189112813":{"id":"n2189112813","loc":[-85.6280386,41.9442963]},"n2189112814":{"id":"n2189112814","loc":[-85.6280405,41.9443292]},"n2189112815":{"id":"n2189112815","loc":[-85.627829,41.9443349]},"n2189112816":{"id":"n2189112816","loc":[-85.6278347,41.9445495]},"n2189153271":{"id":"n2189153271","loc":[-85.6321053,41.9460342]},"n2189153272":{"id":"n2189153272","loc":[-85.632278,41.9457841]},"n2189153273":{"id":"n2189153273","loc":[-85.632823,41.9459936]},"n2189153274":{"id":"n2189153274","loc":[-85.6326845,41.9461963]},"n2189153275":{"id":"n2189153275","loc":[-85.6325664,41.9461507]},"n2189153276":{"id":"n2189153276","loc":[-85.6325323,41.946198]},"n2189153278":{"id":"n2189153278","loc":[-85.6321916,41.9459733]},"n2189153279":{"id":"n2189153279","loc":[-85.6322598,41.9458703]},"n2189153280":{"id":"n2189153280","loc":[-85.6327208,41.9460358]},"n2189153281":{"id":"n2189153281","loc":[-85.6326413,41.9461422]},"n185959079":{"id":"n185959079","loc":[-85.6293702,41.9474668]},"n185966301":{"id":"n185966301","loc":[-85.629692,41.943136]},"n185966304":{"id":"n185966304","loc":[-85.629565,41.942916]},"n185966308":{"id":"n185966308","loc":[-85.629501,41.942751]},"n185966315":{"id":"n185966315","loc":[-85.629472,41.942578]},"n185966319":{"id":"n185966319","loc":[-85.629444,41.942414]},"n185966321":{"id":"n185966321","loc":[-85.629391,41.94205]},"n185966323":{"id":"n185966323","loc":[-85.629369,41.941858]},"n185966327":{"id":"n185966327","loc":[-85.629297,41.941604]},"n185966331":{"id":"n185966331","loc":[-85.629233,41.941549]},"n185966336":{"id":"n185966336","loc":[-85.628504,41.941364]},"n185966338":{"id":"n185966338","loc":[-85.628275,41.941303]},"n185966340":{"id":"n185966340","loc":[-85.6269038,41.9410983]},"n185973867":{"id":"n185973867","loc":[-85.626843,41.947333]},"n185977762":{"id":"n185977762","loc":[-85.6318441,41.9429453]},"n1819848853":{"id":"n1819848853","loc":[-85.625854,41.9492218]},"n1819848861":{"id":"n1819848861","loc":[-85.6251459,41.9552376]},"n1819848874":{"id":"n1819848874","loc":[-85.6267445,41.9482961]},"n1819848882":{"id":"n1819848882","loc":[-85.6257209,41.9552396]},"n1819848883":{"id":"n1819848883","loc":[-85.624706,41.9523173]},"n1819848907":{"id":"n1819848907","loc":[-85.62609,41.9561471]},"n1819848908":{"id":"n1819848908","loc":[-85.6244013,41.9549284]},"n1819848911":{"id":"n1819848911","loc":[-85.6265578,41.9553672]},"n1819848923":{"id":"n1819848923","loc":[-85.6246802,41.9550959]},"n1819848936":{"id":"n1819848936","loc":[-85.6241588,41.9539291]},"n1819848940":{"id":"n1819848940","loc":[-85.62506,41.9511129]},"n1819848944":{"id":"n1819848944","loc":[-85.624942,41.9515912]},"n1819848950":{"id":"n1819848950","loc":[-85.6273989,41.9475461]},"n1819848957":{"id":"n1819848957","loc":[-85.627695,41.947404]},"n1819849009":{"id":"n1819849009","loc":[-85.6259248,41.94896]},"n1819849037":{"id":"n1819849037","loc":[-85.6257252,41.9502112]},"n1819849061":{"id":"n1819849061","loc":[-85.6270084,41.9479626]},"n1819849073":{"id":"n1819849073","loc":[-85.6243734,41.9534583]},"n1819849091":{"id":"n1819849091","loc":[-85.6241373,41.9543918]},"n1819849130":{"id":"n1819849130","loc":[-85.6282572,41.9473067]},"n1819849143":{"id":"n1819849143","loc":[-85.625281,41.9506596]},"n1819849153":{"id":"n1819849153","loc":[-85.6258647,41.9498043]},"n1819849168":{"id":"n1819849168","loc":[-85.6265084,41.9559317]},"n1819849173":{"id":"n1819849173","loc":[-85.6263325,41.9552156]},"n1819849175":{"id":"n1819849175","loc":[-85.6266372,41.9556764]},"n1819849178":{"id":"n1819849178","loc":[-85.6242232,41.9545993]},"n1819849181":{"id":"n1819849181","loc":[-85.6262187,41.9486712]},"n1819849188":{"id":"n1819849188","loc":[-85.6245558,41.9530434]},"n1819849190":{"id":"n1819849190","loc":[-85.6255982,41.9563017]},"n2168544738":{"id":"n2168544738","loc":[-85.6245707,41.9529711]},"w208643145":{"id":"w208643145","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153271","n2189153272","n2189153273","n2189153274","n2189153275","n2189153276","n2189153271"]},"w17967561":{"id":"w17967561","tags":{"highway":"residential","name":"Garden St"},"nodes":["n185980378","n185987309","n185987311","n185983236","n185973866"]},"w134150802":{"id":"w134150802","tags":{"bridge":"yes","highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980386","n185980388"]},"w208639462":{"id":"w208639462","tags":{"area":"yes","building":"yes"},"nodes":["n2189112797","n2189112798","n2189112799","n2189112800","n2189112801","n2189112802","n2189112803","n2189112804","n2189112805","n2189112806","n2189112807","n2189112808","n2189112809","n2189112810","n2189112811","n2189112812","n2189112813","n2189112814","n2189112815","n2189112816","n2189112797"]},"w134150830":{"id":"w134150830","tags":{"bridge":"yes","highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977762","n185977764"]},"w134150801":{"id":"w134150801","tags":{"highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977764","n185964982"]},"w208643146":{"id":"w208643146","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153277","n2189153281","n2189153278","n2189153279","n2189153280","n2189153281"]},"w17966061":{"id":"w17966061","tags":{"highway":"residential","name":"John Glenn Ct"},"nodes":["n185973866","n185973867"]},"w134150772":{"id":"w134150772","tags":{"highway":"residential","name":"5th Ave"},"nodes":["n185988034","n185959079","n185988036","n185978967"]},"w134150836":{"id":"w134150836","tags":{"highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980388","n1819858524","n185980389","n185980391","n185980393","n185964982"]},"w17967734":{"id":"w17967734","tags":{"highway":"residential","name":"Water Street"},"nodes":["n185988896","n185980391","n1819858529"]},"w17965305":{"id":"w17965305","tags":{"highway":"residential","name":"River Dr"},"nodes":["n185966295","n185966296","n185966298","n185966300","n185966301","n185966304","n185966308","n185966315","n185966319","n185966321","n185966323","n185966327","n185966331","n185966336","n185966338","n185963392","n185966340","n185966342"]},"w134150826":{"id":"w134150826","tags":{"bridge":"yes","highway":"residential","name":"5th Ave"},"nodes":["n185988032","n185988034"]},"w170848330":{"id":"w170848330","tags":{"name":"Portage River","waterway":"river"},"nodes":["n1819849190","n1819848907","n1819849168","n1819849175","n1819848911","n1819849173","n1819848882","n1819848861","n1819848923","n1819848908","n1819849178","n1819849091","n1819848936","n1819849073","n1819849188","n2168544738","n1819848883","n1819848944","n1819848940","n1819849143","n1819849037","n1819849153","n1819848853","n1819849009","n1819849181","n1819848874","n1819849061","n1819848950","n1819848957","n1819849130","n1819848912","n1819849043","n1819849177","n1819849090","n1819849116","n1819848951","n1819848981","n1819848989","n1819849018","n1819848963","n1819848949","n1819849109","n1819849087","n1819848925","n1819848852","n1819848998","n1819849057"]},"r270264":{"id":"r270264","tags":{"network":"US:MI","ref":"86","route":"road","state_id":"MI","type":"route","url":"http://en.wikipedia.org/wiki/M-86_%28Michigan_highway%29"},"members":[{"id":"w17737723","type":"way","role":""},{"id":"w17735949","type":"way","role":""},{"id":"w17740404","type":"way","role":""},{"id":"w17966273","type":"way","role":""},{"id":"w17964745","type":"way","role":""},{"id":"w151538068","type":"way","role":""},{"id":"w151538067","type":"way","role":""},{"id":"w17964960","type":"way","role":""},{"id":"w17966099","type":"way","role":""},{"id":"w17968009","type":"way","role":""},{"id":"w41259499","type":"way","role":""},{"id":"w151540401","type":"way","role":""},{"id":"w151540418","type":"way","role":""},{"id":"w17967997","type":"way","role":""},{"id":"w17966029","type":"way","role":""},{"id":"w17964801","type":"way","role":""},{"id":"w41259496","type":"way","role":""},{"id":"w151540399","type":"way","role":""},{"id":"w17968004","type":"way","role":""},{"id":"w17966462","type":"way","role":""},{"id":"w134150830","type":"way","role":""},{"id":"w134150801","type":"way","role":""},{"id":"w17732295","type":"way","role":""}]},"n185980093":{"id":"n185980093","loc":[-85.6271414,41.9407274]},"n185964330":{"id":"n185964330","loc":[-85.6235688,41.9399111]},"n185964328":{"id":"n185964328","loc":[-85.6235609,41.9391301]},"n185958034":{"id":"n185958034","loc":[-85.627102,41.939125]},"n185964331":{"id":"n185964331","loc":[-85.623571,41.940124]},"n185964329":{"id":"n185964329","loc":[-85.623562,41.9392411]},"n185972756":{"id":"n185972756","loc":[-85.623802,41.939102]},"n185972757":{"id":"n185972757","loc":[-85.623584,41.93913]},"n185975325":{"id":"n185975325","loc":[-85.624835,41.939318]},"n185975326":{"id":"n185975326","loc":[-85.624811,41.939435]},"n185975327":{"id":"n185975327","loc":[-85.624635,41.939703]},"n185975328":{"id":"n185975328","loc":[-85.624366,41.940055]},"n185975330":{"id":"n185975330","loc":[-85.624287,41.940113]},"n185975332":{"id":"n185975332","loc":[-85.624215,41.940134]},"n185980088":{"id":"n185980088","loc":[-85.627127,41.940086]},"n185988943":{"id":"n185988943","loc":[-85.622643,41.940128]},"n185988961":{"id":"n185988961","loc":[-85.627263,41.940082]},"n185990192":{"id":"n185990192","loc":[-85.622933,41.939224]},"n185990194":{"id":"n185990194","loc":[-85.621976,41.939203]},"n185991378":{"id":"n185991378","loc":[-85.622643,41.940635]},"n1475283999":{"id":"n1475283999","loc":[-85.6271165,41.9408429]},"n185980090":{"id":"n185980090","loc":[-85.6271315,41.9402001]},"n185958036":{"id":"n185958036","loc":[-85.6248366,41.9391615]},"n1819800188":{"id":"n1819800188","loc":[-85.6246947,41.9401644]},"n1819800199":{"id":"n1819800199","loc":[-85.6233686,41.9430896]},"n1819800204":{"id":"n1819800204","loc":[-85.6223236,41.9408587]},"n1819800213":{"id":"n1819800213","loc":[-85.6247526,41.9414138]},"n1819800216":{"id":"n1819800216","loc":[-85.6230961,41.9407151]},"n1819800218":{"id":"n1819800218","loc":[-85.621991,41.9429336]},"n1819800221":{"id":"n1819800221","loc":[-85.6246088,41.9424708]},"n1819800227":{"id":"n1819800227","loc":[-85.6241368,41.9403081]},"n1819800230":{"id":"n1819800230","loc":[-85.6226776,41.9431012]},"n1819800231":{"id":"n1819800231","loc":[-85.6243728,41.9401644]},"n1819800232":{"id":"n1819800232","loc":[-85.6249629,41.9408907]},"n1819800248":{"id":"n1819800248","loc":[-85.6238685,41.9405555]},"n1819800266":{"id":"n1819800266","loc":[-85.6246882,41.9418367]},"n1819800271":{"id":"n1819800271","loc":[-85.62492,41.9413695]},"n1819800294":{"id":"n1819800294","loc":[-85.6243556,41.9427465]},"n1819800304":{"id":"n1819800304","loc":[-85.6251453,41.94117]},"n1819800325":{"id":"n1819800325","loc":[-85.6248234,41.9405714]},"n1819800362":{"id":"n1819800362","loc":[-85.6239544,41.9429416]},"n1819800368":{"id":"n1819800368","loc":[-85.6243406,41.9402283]},"n1819800375":{"id":"n1819800375","loc":[-85.6226562,41.940755]},"n1819800377":{"id":"n1819800377","loc":[-85.6232033,41.9406512]},"n185945133":{"id":"n185945133","loc":[-85.623501,41.933232]},"n185945135":{"id":"n185945135","loc":[-85.624776,41.933205]},"n185945395":{"id":"n185945395","loc":[-85.624741,41.93019]},"n185952239":{"id":"n185952239","loc":[-85.615166,41.9382]},"n185954490":{"id":"n185954490","loc":[-85.624721,41.929278]},"n185957831":{"id":"n185957831","loc":[-85.625041,41.938662]},"n185958030":{"id":"n185958030","loc":[-85.629033,41.93913]},"n185958032":{"id":"n185958032","loc":[-85.628429,41.939143]},"n185958498":{"id":"n185958498","loc":[-85.621605,41.940143]},"n185961186":{"id":"n185961186","loc":[-85.624792,41.935214]},"n185963099":{"id":"n185963099","loc":[-85.6204461,41.9401485]},"n185963698":{"id":"n185963698","loc":[-85.6297342,41.9400783]},"n185964320":{"id":"n185964320","loc":[-85.623511,41.934216]},"n185964322":{"id":"n185964322","loc":[-85.6235312,41.9362084]},"n185964324":{"id":"n185964324","loc":[-85.6235488,41.9371726]},"n185964326":{"id":"n185964326","loc":[-85.6235512,41.9381718]},"n185967077":{"id":"n185967077","loc":[-85.617359,41.940161]},"n185967634":{"id":"n185967634","loc":[-85.6248039,41.9362012]},"n185970833":{"id":"n185970833","loc":[-85.6248019,41.9381684]},"n185972752":{"id":"n185972752","loc":[-85.624582,41.938848]},"n185972754":{"id":"n185972754","loc":[-85.6242,41.939008]},"n185973251":{"id":"n185973251","loc":[-85.602727,41.936012]},"n185974509":{"id":"n185974509","loc":[-85.62478,41.93217]},"n185975315":{"id":"n185975315","loc":[-85.624703,41.925597]},"n185975316":{"id":"n185975316","loc":[-85.624716,41.927359]},"n185975317":{"id":"n185975317","loc":[-85.62475,41.93119]},"n185975318":{"id":"n185975318","loc":[-85.624782,41.934218]},"n185975320":{"id":"n185975320","loc":[-85.6247949,41.9371708]},"n185977754":{"id":"n185977754","loc":[-85.6276,41.937412]},"n185980075":{"id":"n185980075","loc":[-85.627451,41.937549]},"n185980077":{"id":"n185980077","loc":[-85.627375,41.937618]},"n185980078":{"id":"n185980078","loc":[-85.627278,41.937728]},"n185980079":{"id":"n185980079","loc":[-85.627199,41.937842]},"n185980081":{"id":"n185980081","loc":[-85.627141,41.937981]},"n185980083":{"id":"n185980083","loc":[-85.627109,41.938153]},"n185980085":{"id":"n185980085","loc":[-85.627101,41.938699]},"n185981173":{"id":"n185981173","loc":[-85.61433,41.940167]},"n185987021":{"id":"n185987021","loc":[-85.628311,41.942261]},"n185988963":{"id":"n185988963","loc":[-85.628439,41.940086]},"n185990195":{"id":"n185990195","loc":[-85.621225,41.939143]},"n185990196":{"id":"n185990196","loc":[-85.620576,41.939033]},"n185990198":{"id":"n185990198","loc":[-85.619081,41.938804]},"n185990200":{"id":"n185990200","loc":[-85.617593,41.938552]},"n185990202":{"id":"n185990202","loc":[-85.617372,41.938535]},"n185990204":{"id":"n185990204","loc":[-85.616087,41.93832]},"n185990206":{"id":"n185990206","loc":[-85.615754,41.938289]},"n185990209":{"id":"n185990209","loc":[-85.615438,41.938251]},"n185990211":{"id":"n185990211","loc":[-85.613469,41.937867]},"n185990212":{"id":"n185990212","loc":[-85.610172,41.937298]},"n185990213":{"id":"n185990213","loc":[-85.605537,41.936497]},"n185990214":{"id":"n185990214","loc":[-85.604014,41.936234]},"n1819800180":{"id":"n1819800180","loc":[-85.588775,41.9455032]},"n1819800181":{"id":"n1819800181","loc":[-85.6074212,41.9408827]},"n1819800182":{"id":"n1819800182","loc":[-85.6131397,41.9427022]},"n1819800183":{"id":"n1819800183","loc":[-85.6171523,41.9416807]},"n1819800184":{"id":"n1819800184","loc":[-85.602465,41.9397415]},"n1819800185":{"id":"n1819800185","loc":[-85.6109296,41.9410583]},"n1819800186":{"id":"n1819800186","loc":[-85.6165729,41.9418004]},"n1819800189":{"id":"n1819800189","loc":[-85.5866293,41.9458224]},"n1819800191":{"id":"n1819800191","loc":[-85.5853311,41.9466603]},"n1819800201":{"id":"n1819800201","loc":[-85.6101142,41.9433406]},"n1819800202":{"id":"n1819800202","loc":[-85.600963,41.9428618]},"n1819800206":{"id":"n1819800206","loc":[-85.6154357,41.9427501]},"n1819800207":{"id":"n1819800207","loc":[-85.6040309,41.9414094]},"n1819800209":{"id":"n1819800209","loc":[-85.6113694,41.943189]},"n1819800211":{"id":"n1819800211","loc":[-85.618032,41.9416408]},"n1819800214":{"id":"n1819800214","loc":[-85.5959419,41.9402602]},"n1819800219":{"id":"n1819800219","loc":[-85.5972117,41.9420043]},"n1819800223":{"id":"n1819800223","loc":[-85.6117171,41.9430019]},"n1819800224":{"id":"n1819800224","loc":[-85.5977873,41.9395579]},"n1819800226":{"id":"n1819800226","loc":[-85.5917362,41.9432209]},"n1819800228":{"id":"n1819800228","loc":[-85.6055759,41.9419122]},"n1819800229":{"id":"n1819800229","loc":[-85.6203395,41.9425595]},"n1819800233":{"id":"n1819800233","loc":[-85.6107579,41.9433007]},"n1819800234":{"id":"n1819800234","loc":[-85.6039773,41.9412498]},"n1819800235":{"id":"n1819800235","loc":[-85.6000977,41.9412861]},"n1819800236":{"id":"n1819800236","loc":[-85.6026689,41.9407231]},"n1819800237":{"id":"n1819800237","loc":[-85.615161,41.9428662]},"n1819800238":{"id":"n1819800238","loc":[-85.5878953,41.9454314]},"n1819800239":{"id":"n1819800239","loc":[-85.6035267,41.941569]},"n1819800240":{"id":"n1819800240","loc":[-85.5929738,41.9450208]},"n1819800241":{"id":"n1819800241","loc":[-85.6186329,41.9416488]},"n1819800242":{"id":"n1819800242","loc":[-85.5881136,41.9483963]},"n1819800243":{"id":"n1819800243","loc":[-85.5909208,41.9466922]},"n1819800244":{"id":"n1819800244","loc":[-85.5997721,41.9394941]},"n1819800245":{"id":"n1819800245","loc":[-85.6202064,41.9425712]},"n1819800246":{"id":"n1819800246","loc":[-85.591071,41.9448808]},"n1819800247":{"id":"n1819800247","loc":[-85.5866078,41.9490622]},"n1819800250":{"id":"n1819800250","loc":[-85.602383,41.9420841]},"n1819800251":{"id":"n1819800251","loc":[-85.5957418,41.9426906]},"n1819800255":{"id":"n1819800255","loc":[-85.6157039,41.9416727]},"n1819800256":{"id":"n1819800256","loc":[-85.6080328,41.9410982]},"n1819800258":{"id":"n1819800258","loc":[-85.6192551,41.9414892]},"n1819800260":{"id":"n1819800260","loc":[-85.6104253,41.94117]},"n1819800261":{"id":"n1819800261","loc":[-85.6204503,41.9425709]},"n1819800263":{"id":"n1819800263","loc":[-85.5872194,41.9455431]},"n1819800264":{"id":"n1819800264","loc":[-85.616176,41.9418244]},"n1819800268":{"id":"n1819800268","loc":[-85.6120883,41.9426703]},"n1819800269":{"id":"n1819800269","loc":[-85.5894547,41.9474946]},"n1819800272":{"id":"n1819800272","loc":[-85.6209181,41.9425027]},"n1819800274":{"id":"n1819800274","loc":[-85.6122814,41.9412817]},"n1819800276":{"id":"n1819800276","loc":[-85.5895153,41.9452798]},"n1819800277":{"id":"n1819800277","loc":[-85.5884317,41.9455272]},"n1819800279":{"id":"n1819800279","loc":[-85.5884103,41.9480966]},"n1819800287":{"id":"n1819800287","loc":[-85.5904917,41.9453915]},"n1819800288":{"id":"n1819800288","loc":[-85.6212292,41.9412977]},"n1819800289":{"id":"n1819800289","loc":[-85.5954377,41.9406832]},"n1819800290":{"id":"n1819800290","loc":[-85.593721,41.9420957]},"n1819800291":{"id":"n1819800291","loc":[-85.6162832,41.9427102]},"n1819800292":{"id":"n1819800292","loc":[-85.605018,41.9401804]},"n1819800293":{"id":"n1819800293","loc":[-85.6086443,41.941146]},"n1819800296":{"id":"n1819800296","loc":[-85.6204675,41.9413775]},"n1819800297":{"id":"n1819800297","loc":[-85.612496,41.9424947]},"n1819800299":{"id":"n1819800299","loc":[-85.6065629,41.9423431]},"n1819800301":{"id":"n1819800301","loc":[-85.6036125,41.9398452]},"n1819800303":{"id":"n1819800303","loc":[-85.6114767,41.94117]},"n1819800306":{"id":"n1819800306","loc":[-85.592616,41.9428139]},"n1819800308":{"id":"n1819800308","loc":[-85.6023041,41.9419521]},"n1819800310":{"id":"n1819800310","loc":[-85.6218944,41.9411061]},"n1819800311":{"id":"n1819800311","loc":[-85.6097816,41.941162]},"n1819800312":{"id":"n1819800312","loc":[-85.5922549,41.9457869]},"n1819800313":{"id":"n1819800313","loc":[-85.5986027,41.9417206]},"n1819800314":{"id":"n1819800314","loc":[-85.5918687,41.946138]},"n1819800315":{"id":"n1819800315","loc":[-85.5872875,41.948883]},"n1819800316":{"id":"n1819800316","loc":[-85.594272,41.9436642]},"n1819800317":{"id":"n1819800317","loc":[-85.6176351,41.941577]},"n1819800318":{"id":"n1819800318","loc":[-85.6137834,41.9430853]},"n1819800319":{"id":"n1819800319","loc":[-85.6195383,41.942622],"tags":{"leisure":"slipway"}},"n1819800320":{"id":"n1819800320","loc":[-85.5971006,41.9398053]},"n1819800321":{"id":"n1819800321","loc":[-85.601714,41.9406752]},"n1819800322":{"id":"n1819800322","loc":[-85.5908028,41.9453117]},"n1819800323":{"id":"n1819800323","loc":[-85.6062732,41.9404597]},"n1819800324":{"id":"n1819800324","loc":[-85.62124,41.9425905]},"n1819800327":{"id":"n1819800327","loc":[-85.6008664,41.942766]},"n1819800328":{"id":"n1819800328","loc":[-85.6179355,41.9428538]},"n1819800330":{"id":"n1819800330","loc":[-85.6045566,41.9415131]},"n1819800331":{"id":"n1819800331","loc":[-85.5944935,41.9414653]},"n1819800333":{"id":"n1819800333","loc":[-85.6088911,41.943181]},"n1819800334":{"id":"n1819800334","loc":[-85.5946367,41.943369]},"n1819800336":{"id":"n1819800336","loc":[-85.6150494,41.9429656]},"n1819800343":{"id":"n1819800343","loc":[-85.6096099,41.9433326]},"n1819800345":{"id":"n1819800345","loc":[-85.5915216,41.9435401]},"n1819800347":{"id":"n1819800347","loc":[-85.607786,41.9428698]},"n1819800349":{"id":"n1819800349","loc":[-85.6187616,41.9426623]},"n1819800350":{"id":"n1819800350","loc":[-85.6012527,41.9426064]},"n1819800352":{"id":"n1819800352","loc":[-85.6214867,41.9428379]},"n1819800354":{"id":"n1819800354","loc":[-85.61338,41.94293]},"n1819800355":{"id":"n1819800355","loc":[-85.5923156,41.9428139]},"n1819800357":{"id":"n1819800357","loc":[-85.5901591,41.9453197]},"n1819800359":{"id":"n1819800359","loc":[-85.6033979,41.9408827]},"n1819800360":{"id":"n1819800360","loc":[-85.6186543,41.9414653]},"n1819800363":{"id":"n1819800363","loc":[-85.6128607,41.9425665]},"n1819800365":{"id":"n1819800365","loc":[-85.614234,41.9412977]},"n1819800367":{"id":"n1819800367","loc":[-85.6089662,41.9410902]},"n1819800369":{"id":"n1819800369","loc":[-85.6197379,41.9413695]},"n1819800370":{"id":"n1819800370","loc":[-85.6037348,41.941733]},"n1819800371":{"id":"n1819800371","loc":[-85.5993467,41.9415654]},"n1819800372":{"id":"n1819800372","loc":[-85.598077,41.94196]},"n1819800373":{"id":"n1819800373","loc":[-85.5984203,41.9394781]},"n1819800374":{"id":"n1819800374","loc":[-85.6013315,41.9427066]},"n1819800376":{"id":"n1819800376","loc":[-85.5934673,41.944167]},"n1819800378":{"id":"n1819800378","loc":[-85.6011062,41.9407753]},"n1819800379":{"id":"n1819800379","loc":[-85.6150602,41.9415131]},"n1819800380":{"id":"n1819800380","loc":[-85.6132148,41.9412338]},"n1819800381":{"id":"n1819800381","loc":[-85.5889038,41.9453835]},"n2139966621":{"id":"n2139966621","loc":[-85.6198719,41.9426184]},"n2139966622":{"id":"n2139966622","loc":[-85.6197551,41.9426123]},"n2139966623":{"id":"n2139966623","loc":[-85.6196467,41.9426279]},"n2139966624":{"id":"n2139966624","loc":[-85.6191519,41.9426221]},"n2139966625":{"id":"n2139966625","loc":[-85.6194153,41.9426256]},"n2139966626":{"id":"n2139966626","loc":[-85.6200497,41.9425812]},"n2139966629":{"id":"n2139966629","loc":[-85.6192123,41.9426229]},"n2203933101":{"id":"n2203933101","loc":[-85.6030009,41.9360592]},"w17967539":{"id":"w17967539","tags":{"highway":"residential","name":"1st Ave"},"nodes":["n185965099","n185963395","n185987021"]},"w17967751":{"id":"w17967751","tags":{"highway":"residential","name":"River St"},"nodes":["n185980088","n185988961","n185988963","n185963698"]},"w17965088":{"id":"w17965088","tags":{"highway":"residential","name":"9th St"},"nodes":["n185945133","n185964320","n185964322","n185964324","n185964326","n185964328","n185964329","n185964330","n185964331"]},"w17964467":{"id":"w17964467","tags":{"highway":"residential","name":"Mechanic St"},"nodes":["n185958030","n185958032","n185958034","n185958036"]},"w134150842":{"id":"w134150842","tags":{"bridge":"yes","highway":"residential","name":"6th St"},"nodes":["n185980090","n185980093"]},"w17966740":{"id":"w17966740","tags":{"highway":"residential","name":"6th St"},"nodes":["n185977754","n185980075","n185980077","n185980078","n185980079","n185980081","n185980083","n185980085","n185958034","n185980088","n185980090"]},"w170844765":{"id":"w170844765","tags":{"waterway":"dam"},"nodes":["n1819800304","n1819800232","n1819800325","n1819800188"]},"w17967745":{"id":"w17967745","tags":{"highway":"residential","name":"River St"},"nodes":["n185981173","n185967077","n185963099","n185958498","n185988943","n185964331","n185975332"]},"w17968113":{"id":"w17968113","tags":{"highway":"residential","name":"Green St"},"nodes":["n185988943","n185991378"]},"w134150833":{"id":"w134150833","tags":{"highway":"residential","name":"6th St"},"nodes":["n185980093","n1475283999","n185963392"]},"w17967935":{"id":"w17967935","tags":{"name":"Michigan Central Railroad","railway":"abandoned"},"nodes":["n185972757","n185990192","n185990194","n185990195","n185990196","n185990198","n185990200","n185990202","n185990204","n185990206","n185990209","n185952239","n185990211","n185990212","n185990213","n185990214","n2203933101","n185973251"]},"w17965993":{"id":"w17965993","tags":{"name":"Conrail Railroad","railway":"abandoned"},"nodes":["n185957831","n185972752","n185972754","n185972756","n185972757"]},"w17966211":{"id":"w17966211","tags":{"highway":"residential","name":"8th St"},"nodes":["n185975315","n185975316","n185954490","n185945395","n185975317","n185974509","n185945135","n185975318","n185961186","n185967634","n185975320","n185970833","n185958036","n185975325","n185975326","n185975327","n185975328","n185975330","n185975332"]},"w170844766":{"id":"w170844766","tags":{"waterway":"riverbank"},"nodes":["n1819800229","n1819800245","n2139966626","n2139966621","n2139966622","n2139966623","n1819800319","n2139966625","n2139966629","n2139966624","n1819800349","n1819800328","n1819800291","n1819800206","n1819800237","n1819800336","n1819800318","n1819800354","n1819800182","n1819800363","n1819800297","n1819800268","n1819800223","n1819800209","n1819800233","n1819800201","n1819800343","n1819800333","n1819800347","n1819800299","n1819800228","n1819800330","n1819800370","n1819800250","n1819800374","n1819800202","n1819800327","n1819800350","n1819800308","n1819800239","n1819800207","n1819800234","n1819800359","n1819800236","n1819800321","n1819800378","n1819800235","n1819800371","n1819800313","n1819800372","n1819800219","n1819800251","n1819800334","n1819800316","n1819800376","n1819800240","n1819800312","n1819800314","n1819800243","n1819800269","n1819800279","n1819800242","n1819800315","n1819800247","n1819800191","n1819800189","n1819800263","n1819800238","n1819800277","n1819800180","n1819800381","n1819800276","n1819800357","n1819800287","n1819800322","n1819800246","n1819800345","n1819800226","n1819800355","n1819800306","n1819800290","n1819800331","n1819800289","n1819800214","n1819800320","n1819800224","n1819800373","n1819800244","n1819800184","n1819800301","n1819800292","n1819800323","n1819800181","n1819800256","n1819800293","n1819800367","n1819800311","n1819800260","n1819800185","n1819800303","n1819800274","n1819800380","n1819800365","n1819800379","n1819800255","n1819800264","n1819800186","n1819800183","n1819800317","n1819800211","n1819800241","n1819800360","n1819800258","n1819800369","n1819800296","n1819800288","n1819800310","n1819800204","n1819800375","n1819800216","n1819800377","n1819800248","n1819800227","n1819800368","n1819800231","n1819800188","n1819800325","n1819800232","n1819800304","n1819800271","n1819800213","n1819800266","n1819800221","n1819800294","n1819800362","n1819800199","n1819800230","n1819800218","n1819800352","n1819800324","n1819800272","n1819800261","n1819800229"]},"n1875654132":{"id":"n1875654132","loc":[-85.6297439,41.939808]},"n1475293263":{"id":"n1475293263","loc":[-85.6296235,41.939922]},"n185947850":{"id":"n185947850","loc":[-85.631594,41.942613]},"n185952745":{"id":"n185952745","loc":[-85.630986,41.941786]},"n185972907":{"id":"n185972907","loc":[-85.631797,41.9420055]},"n185972911":{"id":"n185972911","loc":[-85.6309723,41.9411623]},"n185972915":{"id":"n185972915","loc":[-85.6295971,41.939267]},"n1475293223":{"id":"n1475293223","loc":[-85.6313962,41.9416114],"tags":{"railway":"level_crossing"}},"n1475293231":{"id":"n1475293231","loc":[-85.6318779,41.9415447]},"n1475293241":{"id":"n1475293241","loc":[-85.6304613,41.9405499]},"n1475293246":{"id":"n1475293246","loc":[-85.6297512,41.9395393],"tags":{"railway":"level_crossing"}},"n1475293251":{"id":"n1475293251","loc":[-85.6316633,41.9415128]},"n2139982404":{"id":"n2139982404","loc":[-85.6313283,41.9413748]},"n2139982407":{"id":"n2139982407","loc":[-85.6325545,41.9417787]},"n2139982408":{"id":"n2139982408","loc":[-85.6324499,41.9417693]},"n2139982409":{"id":"n2139982409","loc":[-85.6324753,41.9416444]},"n2139982410":{"id":"n2139982410","loc":[-85.6325814,41.9416538]},"n2139982411":{"id":"n2139982411","loc":[-85.6319572,41.9413515]},"n2139982412":{"id":"n2139982412","loc":[-85.6322925,41.941139]},"n2139982413":{"id":"n2139982413","loc":[-85.6323153,41.941153]},"n2139982414":{"id":"n2139982414","loc":[-85.6323019,41.9412617]},"n2139982415":{"id":"n2139982415","loc":[-85.6323703,41.9412667]},"n2139982416":{"id":"n2139982416","loc":[-85.6323555,41.941538]},"n2139982417":{"id":"n2139982417","loc":[-85.6321343,41.9416777]},"n2139982418":{"id":"n2139982418","loc":[-85.6319425,41.9416866]},"n2139982419":{"id":"n2139982419","loc":[-85.6320303,41.9416941]},"n2139982420":{"id":"n2139982420","loc":[-85.6321665,41.9415554]},"n2139982421":{"id":"n2139982421","loc":[-85.632412,41.9414164]},"n2139982422":{"id":"n2139982422","loc":[-85.6324801,41.9413421]},"n2139982423":{"id":"n2139982423","loc":[-85.6325023,41.9412585]},"n2139982424":{"id":"n2139982424","loc":[-85.6324532,41.9411607]},"n2139982425":{"id":"n2139982425","loc":[-85.6323502,41.941103]},"n2139982426":{"id":"n2139982426","loc":[-85.6322362,41.9411183]},"n2139982427":{"id":"n2139982427","loc":[-85.6318941,41.9413551]},"n2139982428":{"id":"n2139982428","loc":[-85.6318592,41.9414105]},"n2139982429":{"id":"n2139982429","loc":[-85.6320111,41.9415866]},"n2139982430":{"id":"n2139982430","loc":[-85.632446,41.9413792]},"n2139982431":{"id":"n2139982431","loc":[-85.6325112,41.941416]},"n2139982432":{"id":"n2139982432","loc":[-85.6325449,41.9416345]},"n2139982433":{"id":"n2139982433","loc":[-85.6326122,41.94164]},"n2139982434":{"id":"n2139982434","loc":[-85.6325954,41.9421966]},"n2139982435":{"id":"n2139982435","loc":[-85.6325655,41.9422411]},"n2139982436":{"id":"n2139982436","loc":[-85.632515,41.9422564]},"n2139982437":{"id":"n2139982437","loc":[-85.6324495,41.94223]},"n2139982438":{"id":"n2139982438","loc":[-85.6324009,41.9421743]},"n2139982439":{"id":"n2139982439","loc":[-85.6323915,41.9421145]},"n2139982440":{"id":"n2139982440","loc":[-85.6320287,41.9418585]},"n2139982441":{"id":"n2139982441","loc":[-85.6318285,41.9416387]},"n1475293258":{"id":"n1475293258","loc":[-85.6318289,41.9415077]},"n2168544754":{"id":"n2168544754","loc":[-85.6312814,41.9431198]},"n2168544755":{"id":"n2168544755","loc":[-85.6314212,41.9430646]},"n2168544756":{"id":"n2168544756","loc":[-85.6313387,41.942949]},"n2168544757":{"id":"n2168544757","loc":[-85.6311989,41.9430041]},"n2168544758":{"id":"n2168544758","loc":[-85.6311024,41.9429313]},"n2168544759":{"id":"n2168544759","loc":[-85.6310087,41.9428087]},"n2168544760":{"id":"n2168544760","loc":[-85.6313831,41.9426504]},"n2168544761":{"id":"n2168544761","loc":[-85.6314768,41.9427729]},"n2168544762":{"id":"n2168544762","loc":[-85.6306376,41.942809]},"n2168544763":{"id":"n2168544763","loc":[-85.6307378,41.9429427]},"n2168544764":{"id":"n2168544764","loc":[-85.630841,41.9428998]},"n2168544765":{"id":"n2168544765","loc":[-85.6307408,41.9427662]},"n2168544766":{"id":"n2168544766","loc":[-85.6305404,41.9426029]},"n2168544767":{"id":"n2168544767","loc":[-85.6304976,41.9426194]},"n2168544768":{"id":"n2168544768","loc":[-85.6305673,41.9427184]},"n2168544769":{"id":"n2168544769","loc":[-85.6306164,41.9426984]},"n2168544770":{"id":"n2168544770","loc":[-85.6306418,41.9427302]},"n2168544771":{"id":"n2168544771","loc":[-85.6306861,41.9427137]},"n2168544772":{"id":"n2168544772","loc":[-85.6307146,41.9427537]},"n2168544773":{"id":"n2168544773","loc":[-85.6308999,41.9426807]},"n2168544774":{"id":"n2168544774","loc":[-85.6308429,41.9426053]},"n2168544775":{"id":"n2168544775","loc":[-85.6308999,41.9425806]},"n2168544776":{"id":"n2168544776","loc":[-85.6308318,41.9424875]},"n2168544777":{"id":"n2168544777","loc":[-85.6307732,41.9425087]},"n2168544778":{"id":"n2168544778","loc":[-85.6307178,41.9424357]},"n2168544779":{"id":"n2168544779","loc":[-85.630485,41.942524]},"n2189099387":{"id":"n2189099387","loc":[-85.631203,41.9393371]},"n2189099404":{"id":"n2189099404","loc":[-85.6301963,41.9391363]},"n2189099405":{"id":"n2189099405","loc":[-85.6304447,41.9391352]},"n2189099406":{"id":"n2189099406","loc":[-85.6304463,41.9393391]},"n2189099407":{"id":"n2189099407","loc":[-85.6308435,41.9393373]},"n2189099408":{"id":"n2189099408","loc":[-85.6308418,41.9391251]},"n2189099409":{"id":"n2189099409","loc":[-85.6310929,41.939124]},"n2189099410":{"id":"n2189099410","loc":[-85.6310946,41.9393376]},"n2189112720":{"id":"n2189112720","loc":[-85.6314677,41.9412327]},"n2189112721":{"id":"n2189112721","loc":[-85.6313337,41.9411397]},"n2189112722":{"id":"n2189112722","loc":[-85.6320521,41.9405678]},"n2189112723":{"id":"n2189112723","loc":[-85.6321899,41.9406633]},"n2189112724":{"id":"n2189112724","loc":[-85.6313229,41.9408344]},"n2189112725":{"id":"n2189112725","loc":[-85.6311223,41.9410018]},"n2189112726":{"id":"n2189112726","loc":[-85.6313205,41.9411333]},"n2189112727":{"id":"n2189112727","loc":[-85.6315211,41.9409659]},"n2189112728":{"id":"n2189112728","loc":[-85.6311035,41.9402529]},"n2189112729":{"id":"n2189112729","loc":[-85.631226,41.9402107]},"n2189112730":{"id":"n2189112730","loc":[-85.6315966,41.9408051]},"n2189112731":{"id":"n2189112731","loc":[-85.6314741,41.9408473]},"n2189112732":{"id":"n2189112732","loc":[-85.6318114,41.940534]},"n2189112733":{"id":"n2189112733","loc":[-85.631588,41.94061]},"n2189112734":{"id":"n2189112734","loc":[-85.6314379,41.940366]},"n2189112735":{"id":"n2189112735","loc":[-85.6316613,41.94029]},"n2189112736":{"id":"n2189112736","loc":[-85.6306214,41.9400415]},"n2189112737":{"id":"n2189112737","loc":[-85.6304362,41.9397728]},"n2189112738":{"id":"n2189112738","loc":[-85.6305899,41.9397142]},"n2189112739":{"id":"n2189112739","loc":[-85.6307751,41.9399828]},"n2189112740":{"id":"n2189112740","loc":[-85.6304695,41.9401673]},"n2189112741":{"id":"n2189112741","loc":[-85.6301298,41.9396855]},"n2189112742":{"id":"n2189112742","loc":[-85.6303016,41.9396184]},"n2189112743":{"id":"n2189112743","loc":[-85.6306413,41.9401003]},"n2189112744":{"id":"n2189112744","loc":[-85.6309656,41.9406189]},"n2189112745":{"id":"n2189112745","loc":[-85.6308738,41.940493]},"n2189112746":{"id":"n2189112746","loc":[-85.6309333,41.940469]},"n2189112747":{"id":"n2189112747","loc":[-85.6307634,41.9402358]},"n2189112748":{"id":"n2189112748","loc":[-85.6308798,41.9401889]},"n2189112749":{"id":"n2189112749","loc":[-85.6311416,41.940548]},"n2189112750":{"id":"n2189112750","loc":[-85.6309577,41.9408708]},"n2189112751":{"id":"n2189112751","loc":[-85.630874,41.9407777]},"n2189112752":{"id":"n2189112752","loc":[-85.6310622,41.9406841]},"n2189112753":{"id":"n2189112753","loc":[-85.6311459,41.9407772]},"n2189112754":{"id":"n2189112754","loc":[-85.6320308,41.9405747]},"n2189112755":{"id":"n2189112755","loc":[-85.6317769,41.9401857]},"n2189112756":{"id":"n2189112756","loc":[-85.6313462,41.9401785]},"n2189112757":{"id":"n2189112757","loc":[-85.6313423,41.9401199]},"n2189112758":{"id":"n2189112758","loc":[-85.6318308,41.9401184]},"n2189112759":{"id":"n2189112759","loc":[-85.6321154,41.9405433]},"n2189112760":{"id":"n2189112760","loc":[-85.6310307,41.941683]},"n2189112761":{"id":"n2189112761","loc":[-85.6309808,41.9416078]},"n2189112762":{"id":"n2189112762","loc":[-85.6312094,41.9415156]},"n2189112763":{"id":"n2189112763","loc":[-85.6312636,41.9415865]},"n2189112764":{"id":"n2189112764","loc":[-85.6309384,41.94155]},"n2189112765":{"id":"n2189112765","loc":[-85.631156,41.9414619]},"n2189112766":{"id":"n2189112766","loc":[-85.6311968,41.94152]},"n2189112767":{"id":"n2189112767","loc":[-85.6308946,41.9414851]},"n2189112768":{"id":"n2189112768","loc":[-85.6308237,41.9413888]},"n2189112769":{"id":"n2189112769","loc":[-85.6309858,41.9413228]},"n2189112770":{"id":"n2189112770","loc":[-85.6310567,41.9414192]},"n2189112771":{"id":"n2189112771","loc":[-85.6307774,41.9413276]},"n2189112772":{"id":"n2189112772","loc":[-85.6309068,41.9412735]},"n2189112773":{"id":"n2189112773","loc":[-85.6309531,41.9413347]},"n2189112774":{"id":"n2189112774","loc":[-85.6307975,41.9412466]},"n2189112775":{"id":"n2189112775","loc":[-85.6307006,41.9411699]},"n2189112776":{"id":"n2189112776","loc":[-85.6308289,41.941113]},"n2189112777":{"id":"n2189112777","loc":[-85.6308997,41.9412012]},"n2189112778":{"id":"n2189112778","loc":[-85.630765,41.9412062]},"n2189112779":{"id":"n2189112779","loc":[-85.630739,41.9412177]},"n2189112780":{"id":"n2189112780","loc":[-85.6305822,41.9410391]},"n2189112781":{"id":"n2189112781","loc":[-85.6304117,41.9408177]},"n2189112782":{"id":"n2189112782","loc":[-85.6305086,41.9407769]},"n2189112783":{"id":"n2189112783","loc":[-85.6306779,41.9410044]},"n2189112784":{"id":"n2189112784","loc":[-85.6307734,41.9421663]},"n2189112785":{"id":"n2189112785","loc":[-85.630708,41.9420741]},"n2189112786":{"id":"n2189112786","loc":[-85.630863,41.9420133]},"n2189112787":{"id":"n2189112787","loc":[-85.6309285,41.9421055]},"n2189112788":{"id":"n2189112788","loc":[-85.6307014,41.9420263]},"n2189112789":{"id":"n2189112789","loc":[-85.6306648,41.941971]},"n2189112790":{"id":"n2189112790","loc":[-85.6307927,41.9419178]},"n2189112791":{"id":"n2189112791","loc":[-85.6308366,41.9419696]},"n2189112792":{"id":"n2189112792","loc":[-85.6307574,41.9418708]},"n2189112793":{"id":"n2189112793","loc":[-85.6306288,41.9419231]},"n2189112794":{"id":"n2189112794","loc":[-85.6306943,41.9417835]},"n2189112795":{"id":"n2189112795","loc":[-85.6305344,41.9418474]},"n2189112796":{"id":"n2189112796","loc":[-85.6305981,41.9419355]},"n2189123410":{"id":"n2189123410","loc":[-85.6315476,41.9393801]},"n2189123412":{"id":"n2189123412","loc":[-85.6315247,41.9399188]},"n2189123415":{"id":"n2189123415","loc":[-85.6316484,41.9400433]},"n185945138":{"id":"n185945138","loc":[-85.627073,41.93319]},"n185945142":{"id":"n185945142","loc":[-85.6296891,41.9331674]},"n185945401":{"id":"n185945401","loc":[-85.6269,41.930199]},"n185945405":{"id":"n185945405","loc":[-85.6296598,41.9301676]},"n185956891":{"id":"n185956891","loc":[-85.6251617,41.9255049]},"n185959979":{"id":"n185959979","loc":[-85.626333,41.928347]},"n185959983":{"id":"n185959983","loc":[-85.6296419,41.9283288]},"n185961192":{"id":"n185961192","loc":[-85.627053,41.9352031]},"n185961200":{"id":"n185961200","loc":[-85.6297088,41.9351902]},"n185963655":{"id":"n185963655","loc":[-85.6296112,41.9273948]},"n185963665":{"id":"n185963665","loc":[-85.626047,41.92737]},"n185963688":{"id":"n185963688","loc":[-85.6296503,41.9292199]},"n185963689":{"id":"n185963689","loc":[-85.6296694,41.931157]},"n185963690":{"id":"n185963690","loc":[-85.6296791,41.9321485]},"n185963691":{"id":"n185963691","loc":[-85.6296991,41.9341973]},"n185967638":{"id":"n185967638","loc":[-85.627089,41.9361884]},"n185972917":{"id":"n185972917","loc":[-85.6293759,41.9388605]},"n185972919":{"id":"n185972919","loc":[-85.6290337,41.9380234]},"n185972921":{"id":"n185972921","loc":[-85.628424,41.936212]},"n185972923":{"id":"n185972923","loc":[-85.628367,41.936029]},"n185974511":{"id":"n185974511","loc":[-85.627064,41.932169]},"n185977728":{"id":"n185977728","loc":[-85.625605,41.925842]},"n185977729":{"id":"n185977729","loc":[-85.625685,41.926163]},"n185977731":{"id":"n185977731","loc":[-85.6257845,41.9264872]},"n185977733":{"id":"n185977733","loc":[-85.62663,41.929251]},"n185977734":{"id":"n185977734","loc":[-85.627008,41.930642]},"n185977736":{"id":"n185977736","loc":[-85.627029,41.930775]},"n185977738":{"id":"n185977738","loc":[-85.627041,41.930946]},"n185977739":{"id":"n185977739","loc":[-85.6270379,41.9311746]},"n185977742":{"id":"n185977742","loc":[-85.627055,41.934206]},"n185977744":{"id":"n185977744","loc":[-85.627084,41.936804]},"n185977746":{"id":"n185977746","loc":[-85.627104,41.936914]},"n185977748":{"id":"n185977748","loc":[-85.627156,41.937026]},"n185977750":{"id":"n185977750","loc":[-85.6272406,41.9371672]},"n185977752":{"id":"n185977752","loc":[-85.627317,41.93723]},"n185977753":{"id":"n185977753","loc":[-85.627422,41.937312]},"n185977755":{"id":"n185977755","loc":[-85.627754,41.937504]},"n185977757":{"id":"n185977757","loc":[-85.627883,41.937623]},"n185977761":{"id":"n185977761","loc":[-85.627984,41.93773]},"n1475283996":{"id":"n1475283996","loc":[-85.6270514,41.9317122],"tags":{"railway":"level_crossing"}},"n1475284004":{"id":"n1475284004","loc":[-85.6278177,41.9342117],"tags":{"railway":"level_crossing"}},"n1475284014":{"id":"n1475284014","loc":[-85.6251877,41.9255913],"tags":{"railway":"level_crossing"}},"n1475284017":{"id":"n1475284017","loc":[-85.6274992,41.9331816],"tags":{"railway":"level_crossing"}},"n1475284021":{"id":"n1475284021","loc":[-85.6297108,41.9353939],"tags":{"railway":"level_crossing"}},"n1475284027":{"id":"n1475284027","loc":[-85.62811,41.935198],"tags":{"railway":"level_crossing"}},"n1475284035":{"id":"n1475284035","loc":[-85.626888,41.9311757],"tags":{"railway":"level_crossing"}},"n1475293245":{"id":"n1475293245","loc":[-85.6286047,41.9367881]},"n1875654302":{"id":"n1875654302","loc":[-85.6296367,41.927491]},"n2189099388":{"id":"n2189099388","loc":[-85.6312007,41.9389988]},"n2189099389":{"id":"n2189099389","loc":[-85.6311003,41.9389992]},"n2189099390":{"id":"n2189099390","loc":[-85.6310988,41.9387847]},"n2189099391":{"id":"n2189099391","loc":[-85.6312165,41.9387843]},"n2189099392":{"id":"n2189099392","loc":[-85.6312152,41.9385857]},"n2189099393":{"id":"n2189099393","loc":[-85.6310877,41.9385862]},"n2189099394":{"id":"n2189099394","loc":[-85.6310858,41.9383161]},"n2189099395":{"id":"n2189099395","loc":[-85.6302002,41.9383196]},"n2189099396":{"id":"n2189099396","loc":[-85.6302011,41.9384472]},"n2189099397":{"id":"n2189099397","loc":[-85.6301018,41.9384476]},"n2189099398":{"id":"n2189099398","loc":[-85.6301025,41.9385419]},"n2189099399":{"id":"n2189099399","loc":[-85.6299275,41.9385427]},"n2189099400":{"id":"n2189099400","loc":[-85.62993,41.9388653]},"n2189099401":{"id":"n2189099401","loc":[-85.630107,41.9388645]},"n2189099402":{"id":"n2189099402","loc":[-85.6301079,41.9389908]},"n2189099403":{"id":"n2189099403","loc":[-85.6301951,41.9389904]},"n2189123382":{"id":"n2189123382","loc":[-85.6336279,41.9354365]},"n2189123384":{"id":"n2189123384","loc":[-85.6328492,41.9355177]},"n2189123387":{"id":"n2189123387","loc":[-85.6323762,41.9357396]},"n2189123388":{"id":"n2189123388","loc":[-85.6315174,41.9358966]},"n2189123389":{"id":"n2189123389","loc":[-85.6304331,41.936124]},"n2189123390":{"id":"n2189123390","loc":[-85.6302075,41.9364271]},"n2189123391":{"id":"n2189123391","loc":[-85.6303458,41.9367953]},"n2189123392":{"id":"n2189123392","loc":[-85.6299601,41.9369739]},"n2189123393":{"id":"n2189123393","loc":[-85.6299164,41.9374882]},"n2189123394":{"id":"n2189123394","loc":[-85.6299455,41.9378022]},"n2189123395":{"id":"n2189123395","loc":[-85.6299771,41.9379053]},"n2189123396":{"id":"n2189123396","loc":[-85.6301597,41.9379091]},"n2189123397":{"id":"n2189123397","loc":[-85.6308042,41.9377913]},"n2189123398":{"id":"n2189123398","loc":[-85.6316885,41.9378082]},"n2189123399":{"id":"n2189123399","loc":[-85.6316848,41.9380079]},"n2189123400":{"id":"n2189123400","loc":[-85.6318449,41.9381161]},"n2189123401":{"id":"n2189123401","loc":[-85.6320705,41.9381811]},"n2189123402":{"id":"n2189123402","loc":[-85.6321433,41.9383706]},"n2189123404":{"id":"n2189123404","loc":[-85.632056,41.9384355]},"n2189123406":{"id":"n2189123406","loc":[-85.6317867,41.9384572]},"n2189123409":{"id":"n2189123409","loc":[-85.6316572,41.9387281]},"n2189123417":{"id":"n2189123417","loc":[-85.6315946,41.93775]},"n2189123419":{"id":"n2189123419","loc":[-85.6302641,41.9378393]},"w208640158":{"id":"w208640158","tags":{"area":"yes","natural":"wetland"},"nodes":["n2189123379","n2189123382","n2189123384","n2189123387","n2189123388","n2189123389","n2189123390","n2189123391","n2189123392","n2189123393","n2189123394","n2189123395","n2189123396","n2189123419","n2189123397","n2189123417","n2189123398","n2189123399","n2189123400","n2189123401","n2189123402","n2189123404","n2189123406","n2189123409","n2189123410","n2189123412","n2189123415","n1819805722","n1819805861","n1819805887","n1819805760","n1819805641","n1819805632","n2189123379"]},"w134150787":{"id":"w134150787","tags":{"name":"Conrail Railroad","railway":"rail"},"nodes":["n185972905","n185972907","n1475293223","n185972911","n1475293241","n1475293246","n185972915","n185972917","n185972919","n1475293245","n185972921","n185972923","n1475284027","n1475284004","n1475284017","n1475283996","n1475284035","n1475284014","n185956891"]},"w208639443":{"id":"w208639443","tags":{"area":"yes","building":"yes"},"nodes":["n2189112720","n2189112721","n2189112722","n2189112723","n2189112720"]},"w17966462":{"id":"w17966462","tags":{"highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977728","n185977729","n185977731","n185963665","n185959979","n185977733","n185945401","n185977734","n185977736","n185977738","n185977739","n1475283996","n185974511","n185945138","n185977742","n185961192","n185967638","n185977744","n185977746","n185977748","n185977750","n185977752","n185977753","n185977754","n185977755","n185977757","n185977761","n185958030","n1475293263","n185963698","n185952745","n185947850","n185977762"]},"w203985741":{"id":"w203985741","tags":{"area":"yes","leisure":"park","name":"Conservation Park"},"nodes":["n2139982404","n2139982405","n2139982399","n2139982400","n1819805770","n2139982402","n2139982403","n2139982401","n1819805780","n1819805834","n2139982406","n2139982404"]},"w17963676":{"id":"w17963676","tags":{"highway":"service"},"nodes":["n1475293258","n2139982428","n2139982427","n2139982426","n2139982425","n2139982424","n2139982423","n2139982422","n2139982430","n2139982421","n2139982420","n2139982429","n1475293231","n1475293258","n1475293251","n1475293223","n185952745"]},"w203985745":{"id":"w203985745","tags":{"highway":"footway"},"nodes":["n2139982430","n2139982431","n2139982432","n2139982433","n2139982434","n2139982435","n2139982436","n2139982437","n2139982438","n2139982439","n2139982440","n2139982441","n1475293231"]},"w208639451":{"id":"w208639451","tags":{"area":"yes","building":"yes"},"nodes":["n2189112754","n2189112755","n2189112756","n2189112757","n2189112758","n2189112759","n2189112754"]},"w208639452":{"id":"w208639452","tags":{"area":"yes","building":"yes"},"nodes":["n2189112760","n2189112761","n2189112766","n2189112762","n2189112763","n2189112760"]},"w206805244":{"id":"w206805244","tags":{"area":"yes","building":"yes"},"nodes":["n2168544766","n2168544767","n2168544768","n2168544769","n2168544770","n2168544771","n2168544772","n2168544773","n2168544774","n2168544775","n2168544776","n2168544777","n2168544778","n2168544779","n2168544766"]},"w208639444":{"id":"w208639444","tags":{"area":"yes","building":"yes"},"nodes":["n2189112724","n2189112725","n2189112726","n2189112727","n2189112724"]},"w208639450":{"id":"w208639450","tags":{"area":"yes","building":"yes"},"nodes":["n2189112750","n2189112751","n2189112752","n2189112753","n2189112750"]},"w208639448":{"id":"w208639448","tags":{"area":"yes","building":"yes"},"nodes":["n2189112740","n2189112741","n2189112742","n2189112743","n2189112740"]},"w208637859":{"id":"w208637859","tags":{"area":"yes","building":"yes"},"nodes":["n2189099387","n2189099388","n2189099389","n2189099390","n2189099391","n2189099392","n2189099393","n2189099394","n2189099395","n2189099396","n2189099397","n2189099398","n2189099399","n2189099400","n2189099401","n2189099402","n2189099403","n2189099404","n2189099405","n2189099406","n2189099407","n2189099408","n2189099409","n2189099410","n2189099387"]},"w208639453":{"id":"w208639453","tags":{"area":"yes","building":"yes"},"nodes":["n2189112764","n2189112765","n2189112766","n2189112761","n2189112764"]},"w208639456":{"id":"w208639456","tags":{"area":"yes","building":"yes"},"nodes":["n2189112774","n2189112778","n2189112779","n2189112775","n2189112776","n2189112777","n2189112774"]},"w208639445":{"id":"w208639445","tags":{"area":"yes","building":"yes"},"nodes":["n2189112728","n2189112729","n2189112730","n2189112731","n2189112728"]},"w17967776":{"id":"w17967776","tags":{"highway":"residential","name":"5th St"},"nodes":["n185958032","n185988963"]},"w208639461":{"id":"w208639461","tags":{"area":"yes","building":"yes"},"nodes":["n2189112792","n2189112794","n2189112795","n2189112796","n2189112793","n2189112792"]},"w206805241":{"id":"w206805241","tags":{"area":"yes","building":"yes"},"nodes":["n2168544754","n2168544755","n2168544756","n2168544757","n2168544754"]},"w208639449":{"id":"w208639449","tags":{"area":"yes","building":"yes"},"nodes":["n2189112744","n2189112745","n2189112746","n2189112747","n2189112748","n2189112749","n2189112744"]},"w208639455":{"id":"w208639455","tags":{"area":"yes","building":"yes"},"nodes":["n2189112771","n2189112772","n2189112773","n2189112768","n2189112771"]},"w208639457":{"id":"w208639457","tags":{"area":"yes","building":"yes"},"nodes":["n2189112780","n2189112781","n2189112782","n2189112783","n2189112780"]},"w208639446":{"id":"w208639446","tags":{"area":"yes","building":"yes"},"nodes":["n2189112732","n2189112733","n2189112734","n2189112735","n2189112732"]},"w208639454":{"id":"w208639454","tags":{"area":"yes","building":"yes"},"nodes":["n2189112767","n2189112768","n2189112773","n2189112769","n2189112770","n2189112767"]},"w203985743":{"id":"w203985743","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139982411","n2139982412","n2139982413","n2139982414","n2139982415","n2139982416","n2139982417","n2139982419","n2139982418","n2139982411"]},"w17965023":{"id":"w17965023","tags":{"highway":"residential","name":"4th St"},"nodes":["n185963655","n1875654302","n185959983","n185963688","n185945405","n185963689","n185963690","n185945142","n185963691","n185961200","n1475284021","n1475293246","n1875654132","n1475293263"]},"w206805242":{"id":"w206805242","tags":{"area":"yes","building":"yes"},"nodes":["n2168544758","n2168544759","n2168544760","n2168544761","n2168544758"]},"w208639460":{"id":"w208639460","tags":{"area":"yes","building":"yes"},"nodes":["n2189112792","n2189112793","n2189112789","n2189112790","n2189112792"]},"w208639447":{"id":"w208639447","tags":{"area":"yes","building":"yes"},"nodes":["n2189112736","n2189112737","n2189112738","n2189112739","n2189112736"]},"w208639458":{"id":"w208639458","tags":{"area":"yes","building":"yes"},"nodes":["n2189112784","n2189112785","n2189112786","n2189112787","n2189112784"]},"w203985744":{"id":"w203985744","tags":{"highway":"service"},"nodes":["n2139982425","n2139982400"]},"w208639459":{"id":"w208639459","tags":{"area":"yes","building":"yes"},"nodes":["n2189112788","n2189112789","n2189112790","n2189112791","n2189112788"]},"w203985742":{"id":"w203985742","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139982407","n2139982408","n2139982409","n2139982410","n2139982407"]},"w206805243":{"id":"w206805243","tags":{"area":"yes","building":"yes"},"nodes":["n2168544762","n2168544763","n2168544764","n2168544765","n2168544762"]},"n185959081":{"id":"n185959081","loc":[-85.628469,41.948674]},"n185967427":{"id":"n185967427","loc":[-85.632054,41.951174]},"n185967424":{"id":"n185967424","loc":[-85.6320391,41.9499109]},"n185968101":{"id":"n185968101","loc":[-85.6308395,41.9511969]},"n185960792":{"id":"n185960792","loc":[-85.632074,41.953707]},"n185961389":{"id":"n185961389","loc":[-85.630935,41.959037]},"n185961391":{"id":"n185961391","loc":[-85.632169,41.959025]},"n185965395":{"id":"n185965395","loc":[-85.63216,41.959859]},"n185966953":{"id":"n185966953","loc":[-85.630894,41.957428]},"n185966955":{"id":"n185966955","loc":[-85.632122,41.957427]},"n185967430":{"id":"n185967430","loc":[-85.632077,41.952453]},"n185967432":{"id":"n185967432","loc":[-85.632095,41.954685]},"n185967434":{"id":"n185967434","loc":[-85.632121,41.955914]},"n185967436":{"id":"n185967436","loc":[-85.632128,41.9583]},"n185967438":{"id":"n185967438","loc":[-85.632187,41.960681]},"n185967440":{"id":"n185967440","loc":[-85.632182,41.961493]},"n185968102":{"id":"n185968102","loc":[-85.630855,41.952452]},"n185968104":{"id":"n185968104","loc":[-85.630887,41.953714]},"n185968106":{"id":"n185968106","loc":[-85.630883,41.954692]},"n185968108":{"id":"n185968108","loc":[-85.630904,41.955913]},"n185968110":{"id":"n185968110","loc":[-85.630904,41.958058]},"n185968112":{"id":"n185968112","loc":[-85.630952,41.960667]},"n185968114":{"id":"n185968114","loc":[-85.630972,41.961495]},"n185968116":{"id":"n185968116","loc":[-85.630962,41.961967]},"n185978969":{"id":"n185978969","loc":[-85.633214,41.948618]},"n185985812":{"id":"n185985812","loc":[-85.633274,41.951159]},"n185986155":{"id":"n185986155","loc":[-85.633258,41.949893]},"n2208608826":{"id":"n2208608826","loc":[-85.6339222,41.9486225]},"w17964531":{"id":"w17964531","tags":{"highway":"residential","name":"Willow Dr"},"nodes":["n185959079","n185959081"]},"w17967386":{"id":"w17967386","tags":{"highway":"residential","name":"East Armitage Street"},"nodes":["n185982195","n185968101","n185967427","n185985812","n185974583"]},"w17965502":{"id":"w17965502","tags":{"highway":"residential","name":"Elm Street"},"nodes":["n185968100","n185968101","n185968102","n185968104","n185968106","n185968108","n185966953","n185968110","n185961389","n185968112","n185968114","n185968116"]},"w17967844":{"id":"w17967844","tags":{"highway":"residential","name":"East Bennett Street"},"nodes":["n185982193","n185967424","n185986155","n185978390"]},"w17966581":{"id":"w17966581","tags":{"highway":"residential","name":"E Kelsey St"},"nodes":["n185978967","n185978969","n2208608826","n185971578"]},"w17965402":{"id":"w17965402","tags":{"highway":"residential","name":"Walnut Street"},"nodes":["n185967422","n185967424","n185967427","n185967430","n185960792","n185967432","n185967434","n185966955","n185967436","n185961391","n185965395","n185967438","n185967440"]},"n2199093506":{"id":"n2199093506","loc":[-85.6251879,41.9478322]},"n2199093505":{"id":"n2199093505","loc":[-85.6252076,41.9477749]},"n2199093504":{"id":"n2199093504","loc":[-85.6252289,41.9477602]},"n2199093503":{"id":"n2199093503","loc":[-85.625201,41.9477492]},"n2199093502":{"id":"n2199093502","loc":[-85.6251682,41.9477066]},"n2199093501":{"id":"n2199093501","loc":[-85.6251715,41.947609]},"n2199093500":{"id":"n2199093500","loc":[-85.6252125,41.9475639]},"n2199093499":{"id":"n2199093499","loc":[-85.6252896,41.9475602]},"n2199093498":{"id":"n2199093498","loc":[-85.6253027,41.9475334]},"n2199093497":{"id":"n2199093497","loc":[-85.6253437,41.9474822]},"n2199093496":{"id":"n2199093496","loc":[-85.6254421,41.9474675]},"n2199093495":{"id":"n2199093495","loc":[-85.6256503,41.9474944]},"n2199093494":{"id":"n2199093494","loc":[-85.6257257,41.9476127]},"n2199093493":{"id":"n2199093493","loc":[-85.6257028,41.9477285]},"n2199093492":{"id":"n2199093492","loc":[-85.6255339,41.9478102]},"n2199093491":{"id":"n2199093491","loc":[-85.6253912,41.9478224]},"n2199093490":{"id":"n2199093490","loc":[-85.6253043,41.947859]},"n2199093489":{"id":"n2199093489","loc":[-85.6252027,41.9478846]},"n2199093458":{"id":"n2199093458","loc":[-85.6246876,41.9486617]},"n2199093457":{"id":"n2199093457","loc":[-85.6243127,41.9486583]},"n2199093456":{"id":"n2199093456","loc":[-85.624306,41.9490569]},"n2199093455":{"id":"n2199093455","loc":[-85.624681,41.9490603]},"n2199093514":{"id":"n2199093514","loc":[-85.6236228,41.9496059]},"n2199093513":{"id":"n2199093513","loc":[-85.6236231,41.9496997]},"n2199093512":{"id":"n2199093512","loc":[-85.623357,41.9497002]},"n2199093511":{"id":"n2199093511","loc":[-85.6233567,41.9496136]},"n2199093508":{"id":"n2199093508","loc":[-85.6239735,41.9494287]},"n2199093507":{"id":"n2199093507","loc":[-85.6239741,41.9496052]},"n2199093488":{"id":"n2199093488","loc":[-85.624497,41.9512286]},"n2199093487":{"id":"n2199093487","loc":[-85.6244966,41.9511259]},"n2199093486":{"id":"n2199093486","loc":[-85.6243151,41.9511263]},"n2199093485":{"id":"n2199093485","loc":[-85.6243154,41.951229]},"n2199093484":{"id":"n2199093484","loc":[-85.6241205,41.9508665]},"n2199093483":{"id":"n2199093483","loc":[-85.624115,41.9505249]},"n2199093482":{"id":"n2199093482","loc":[-85.6243149,41.9505231]},"n2199093481":{"id":"n2199093481","loc":[-85.6243203,41.9508648]},"n2199093480":{"id":"n2199093480","loc":[-85.624393,41.9508668]},"n2199093479":{"id":"n2199093479","loc":[-85.6243904,41.9505956]},"n2199093478":{"id":"n2199093478","loc":[-85.6246727,41.950594]},"n2199093477":{"id":"n2199093477","loc":[-85.624675,41.9508203]},"n2199093476":{"id":"n2199093476","loc":[-85.6245097,41.9508212]},"n2199093475":{"id":"n2199093475","loc":[-85.6245101,41.9508662]},"n2199093474":{"id":"n2199093474","loc":[-85.6241008,41.9493459]},"n2199093473":{"id":"n2199093473","loc":[-85.6242442,41.9493459]},"n2199093472":{"id":"n2199093472","loc":[-85.6242442,41.9493681]},"n2199093471":{"id":"n2199093471","loc":[-85.6243397,41.9493681]},"n2199093470":{"id":"n2199093470","loc":[-85.6243417,41.9493511]},"n2199093469":{"id":"n2199093469","loc":[-85.6247251,41.9493485]},"n2199093468":{"id":"n2199093468","loc":[-85.6247548,41.9504949]},"n2199093467":{"id":"n2199093467","loc":[-85.6241214,41.9505017]},"n2199093466":{"id":"n2199093466","loc":[-85.6254398,41.950174]},"n2199093465":{"id":"n2199093465","loc":[-85.6254412,41.9499872]},"n2199093464":{"id":"n2199093464","loc":[-85.6255363,41.9499876]},"n2199093463":{"id":"n2199093463","loc":[-85.6255374,41.9498439]},"n2199093462":{"id":"n2199093462","loc":[-85.6255638,41.949844]},"n2199093461":{"id":"n2199093461","loc":[-85.6255652,41.9496672]},"n2199093460":{"id":"n2199093460","loc":[-85.6251823,41.9496656]},"n2199093459":{"id":"n2199093459","loc":[-85.6251785,41.9501729]},"n2199093510":{"id":"n2199093510","loc":[-85.6229922,41.9496143]},"n2199093509":{"id":"n2199093509","loc":[-85.6229915,41.9494306]},"n185948903":{"id":"n185948903","loc":[-85.616514,41.947449]},"n185955120":{"id":"n185955120","loc":[-85.620103,41.951]},"n185955143":{"id":"n185955143","loc":[-85.619784,41.94746]},"n185960124":{"id":"n185960124","loc":[-85.615238,41.947468]},"n185961362":{"id":"n185961362","loc":[-85.617437,41.947451]},"n185961364":{"id":"n185961364","loc":[-85.61861,41.947456]},"n185961367":{"id":"n185961367","loc":[-85.620088,41.947458]},"n185965105":{"id":"n185965105","loc":[-85.620087,41.94924]},"n185970220":{"id":"n185970220","loc":[-85.62156,41.948333]},"n185974697":{"id":"n185974697","loc":[-85.6201059,41.950132]},"n2138420778":{"id":"n2138420778","loc":[-85.616948,41.9474499]},"w17967535":{"id":"w17967535","tags":{"highway":"residential","name":"10th Ave"},"nodes":["n185955120","n185986812","n185983141"]},"w209716130":{"id":"w209716130","tags":{"area":"yes","building":"yes"},"nodes":["n2199093485","n2199093486","n2199093487","n2199093488","n2199093485"]},"w17964788":{"id":"w17964788","tags":{"highway":"residential","name":"6th Ave"},"nodes":["n185960124","n185948903","n2138420778","n185961362","n185961364","n185955143","n185961367","n185961369","n185961371"]},"w17965159":{"id":"w17965159","tags":{"highway":"residential","name":"8th Ave"},"nodes":["n185965105","n185965108","n185965110"]},"w209716125":{"id":"w209716125","tags":{"area":"yes","building":"yes"},"nodes":["n2199093459","n2199093460","n2199093461","n2199093462","n2199093463","n2199093464","n2199093465","n2199093466","n2199093459"]},"w17965699":{"id":"w17965699","tags":{"highway":"residential","name":"7th Ave"},"nodes":["n185970220","n185970222","n185970224"]},"w209716132":{"id":"w209716132","tags":{"area":"yes","building":"yes"},"nodes":["n2199093507","n2199093508","n2199093509","n2199093510","n2199093511","n2199093512","n2199093513","n2199093514","n2199093507"]},"w17966129":{"id":"w17966129","tags":{"highway":"residential","name":"9th Ave"},"nodes":["n185974697","n185974699"]},"w209716127":{"id":"w209716127","tags":{"area":"yes","building":"yes"},"nodes":["n2199093475","n2199093476","n2199093477","n2199093478","n2199093479","n2199093480","n2199093475"]},"w209716131":{"id":"w209716131","tags":{"area":"yes","natural":"water","water":"pond"},"nodes":["n2199093489","n2199093490","n2199093491","n2199093492","n2199093493","n2199093494","n2199093495","n2199093496","n2199093497","n2199093498","n2199093499","n2199093500","n2199093501","n2199093502","n2199093503","n2199093504","n2199093505","n2199093506","n2199093489"]},"w209716126":{"id":"w209716126","tags":{"area":"yes","building":"yes"},"nodes":["n2199093467","n2199093468","n2199093469","n2199093470","n2199093471","n2199093472","n2199093473","n2199093474","n2199093467"]},"w209716124":{"id":"w209716124","tags":{"area":"yes","building":"yes"},"nodes":["n2199093455","n2199093456","n2199093457","n2199093458","n2199093455"]},"w209716128":{"id":"w209716128","tags":{"area":"yes","building":"yes"},"nodes":["n2199093481","n2199093482","n2199093483","n2199093484","n2199093481"]},"n185949872":{"id":"n185949872","loc":[-85.643009,41.949264]},"n185949875":{"id":"n185949875","loc":[-85.642598,41.94929]},"n185949877":{"id":"n185949877","loc":[-85.642127,41.949382]},"n185949881":{"id":"n185949881","loc":[-85.64169,41.949936]},"n185988165":{"id":"n185988165","loc":[-85.642167,41.947657]},"n185988167":{"id":"n185988167","loc":[-85.642347,41.947662]},"n185988169":{"id":"n185988169","loc":[-85.642621,41.947659]},"n185965019":{"id":"n185965019","loc":[-85.6385084,41.951127]},"n1475293248":{"id":"n1475293248","loc":[-85.6386095,41.9512214]},"n185962639":{"id":"n185962639","loc":[-85.649669,41.949161]},"n185962810":{"id":"n185962810","loc":[-85.649907,41.949157]},"n185964355":{"id":"n185964355","loc":[-85.637412,41.9511359]},"n185965021":{"id":"n185965021","loc":[-85.638661,41.952386]},"n185965023":{"id":"n185965023","loc":[-85.638654,41.953665]},"n185965025":{"id":"n185965025","loc":[-85.638694,41.954649]},"n185965027":{"id":"n185965027","loc":[-85.638724,41.955913]},"n185971415":{"id":"n185971415","loc":[-85.644466,41.949246]},"n185971417":{"id":"n185971417","loc":[-85.647021,41.949193]},"n185971420":{"id":"n185971420","loc":[-85.648476,41.949169]},"n185979975":{"id":"n185979975","loc":[-85.644429,41.947633]},"n185988171":{"id":"n185988171","loc":[-85.645377,41.947622]},"w17963211":{"id":"w17963211","tags":{"highway":"residential"},"nodes":["n185949870","n185949872","n185949875","n185949877","n185949881"]},"w17965839":{"id":"w17965839","tags":{"highway":"residential","name":"Arnold St"},"nodes":["n185949870","n185971415","n185971417","n185971420","n185962639","n185962810"]},"w17967618":{"id":"w17967618","tags":{"highway":"residential","name":"Pierson St"},"nodes":["n185967777","n185988165","n185988167","n185988169","n185985824","n185979975","n185988171"]},"w17965149":{"id":"w17965149","tags":{"highway":"residential","name":"Oak St"},"nodes":["n185965019","n1475293248","n185965021","n185965023","n185965025","n185965027"]},"w17966118":{"id":"w17966118","tags":{"highway":"residential","name":"West Armitage Street"},"nodes":["n185974583","n185974585","n185964355","n185965019"]},"n2208608800":{"id":"n2208608800","loc":[-85.6354294,41.9486201]},"n2199109806":{"id":"n2199109806","loc":[-85.6350474,41.9477884]},"n2199109804":{"id":"n2199109804","loc":[-85.6350476,41.9477962]},"n2199109802":{"id":"n2199109802","loc":[-85.635002,41.9477969]},"n2199109799":{"id":"n2199109799","loc":[-85.6350018,41.9477883]},"n2199109797":{"id":"n2199109797","loc":[-85.6349141,41.9477897]},"n2199109795":{"id":"n2199109795","loc":[-85.6349131,41.9477535]},"n2199109793":{"id":"n2199109793","loc":[-85.6349395,41.9477531]},"n2199109791":{"id":"n2199109791","loc":[-85.6349382,41.9477077]},"n2199109789":{"id":"n2199109789","loc":[-85.6351236,41.9477049]},"n2199109787":{"id":"n2199109787","loc":[-85.6351259,41.9477872]},"n2199109785":{"id":"n2199109785","loc":[-85.634972,41.9475992]},"n2199109783":{"id":"n2199109783","loc":[-85.6349206,41.9475997]},"n2199109770":{"id":"n2199109770","loc":[-85.6348499,41.9475461]},"n2199109768":{"id":"n2199109768","loc":[-85.6348499,41.9475084]},"n2199109765":{"id":"n2199109765","loc":[-85.6349241,41.9474569]},"n2199109763":{"id":"n2199109763","loc":[-85.634967,41.9474564]},"n2199109762":{"id":"n2199109762","loc":[-85.6350405,41.9475121]},"n2199109761":{"id":"n2199109761","loc":[-85.6350405,41.9475419]},"n2199109753":{"id":"n2199109753","loc":[-85.6342443,41.9478391]},"n2199109751":{"id":"n2199109751","loc":[-85.6342427,41.9477927]},"n2199109745":{"id":"n2199109745","loc":[-85.6342439,41.9476859]},"n2199109743":{"id":"n2199109743","loc":[-85.6342429,41.9476575]},"n2199109741":{"id":"n2199109741","loc":[-85.6344615,41.9476533]},"n2199109739":{"id":"n2199109739","loc":[-85.6344678,41.9478348]},"n2199109737":{"id":"n2199109737","loc":[-85.634416,41.9480059]},"n2199109735":{"id":"n2199109735","loc":[-85.6344145,41.9478983]},"n2199109733":{"id":"n2199109733","loc":[-85.6342749,41.9478993]},"n2199109731":{"id":"n2199109731","loc":[-85.6342753,41.9479272]},"n2199109729":{"id":"n2199109729","loc":[-85.6342498,41.9479274]},"n2199109727":{"id":"n2199109727","loc":[-85.6342505,41.9479762]},"n2199109725":{"id":"n2199109725","loc":[-85.6342743,41.947976]},"n2199109723":{"id":"n2199109723","loc":[-85.6342747,41.948007]},"n2199109721":{"id":"n2199109721","loc":[-85.6343415,41.9476355]},"n2199109719":{"id":"n2199109719","loc":[-85.6343391,41.9474973]},"n2199109717":{"id":"n2199109717","loc":[-85.6343133,41.9474798]},"n2199109715":{"id":"n2199109715","loc":[-85.6342874,41.9474737]},"n2199109709":{"id":"n2199109709","loc":[-85.6349804,41.94815]},"n2199109707":{"id":"n2199109707","loc":[-85.6348915,41.9481505]},"n2199109705":{"id":"n2199109705","loc":[-85.6348917,41.9481692]},"n2199109702":{"id":"n2199109702","loc":[-85.6348522,41.9481694]},"n2199109700":{"id":"n2199109700","loc":[-85.6348532,41.9482679]},"n2199109698":{"id":"n2199109698","loc":[-85.6348315,41.948268]},"n2199109696":{"id":"n2199109696","loc":[-85.6348318,41.9482955]},"n2199109694":{"id":"n2199109694","loc":[-85.6349653,41.9482946]},"n2199109692":{"id":"n2199109692","loc":[-85.6349656,41.9483211]},"n2199109690":{"id":"n2199109690","loc":[-85.634999,41.9483209]},"n2199109688":{"id":"n2199109688","loc":[-85.6349987,41.9482947]},"n2199109686":{"id":"n2199109686","loc":[-85.6351753,41.9482935]},"n2199109684":{"id":"n2199109684","loc":[-85.6351749,41.9482617]},"n2199109682":{"id":"n2199109682","loc":[-85.6351588,41.9482618]},"n2199109680":{"id":"n2199109680","loc":[-85.6351575,41.9481518]},"n2199109678":{"id":"n2199109678","loc":[-85.6350671,41.9481524]},"n2199109676":{"id":"n2199109676","loc":[-85.6350649,41.9479659]},"n2199109674":{"id":"n2199109674","loc":[-85.6349785,41.9479665]},"n2199109671":{"id":"n2199109671","loc":[-85.6343069,41.9483263]},"n2199109669":{"id":"n2199109669","loc":[-85.6343052,41.9482981]},"n2199109658":{"id":"n2199109658","loc":[-85.6343314,41.9480549]},"n2199109656":{"id":"n2199109656","loc":[-85.6343305,41.9480461]},"n2199109654":{"id":"n2199109654","loc":[-85.634435,41.9480468]},"n2199109652":{"id":"n2199109652","loc":[-85.6344342,41.9483746]},"n2199109650":{"id":"n2199109650","loc":[-85.6344629,41.9483727]},"n2199109648":{"id":"n2199109648","loc":[-85.6344637,41.9484561]},"n2199109645":{"id":"n2199109645","loc":[-85.63443,41.9484567]},"n2199109642":{"id":"n2199109642","loc":[-85.6344317,41.948505]},"n185964352":{"id":"n185964352","loc":[-85.6373958,41.9489943]},"n185964351":{"id":"n185964351","loc":[-85.637113,41.9486]},"n2208608825":{"id":"n2208608825","loc":[-85.6354483,41.9494241]},"n2208608823":{"id":"n2208608823","loc":[-85.6360418,41.949416]},"n2208608821":{"id":"n2208608821","loc":[-85.6360458,41.9495802]},"n2208608811":{"id":"n2208608811","loc":[-85.6357458,41.9495843]},"n2208608808":{"id":"n2208608808","loc":[-85.6357508,41.9497835]},"n2208608806":{"id":"n2208608806","loc":[-85.6354573,41.9497875]},"n2208608795":{"id":"n2208608795","loc":[-85.6354595,41.9498778]},"n2199109638":{"id":"n2199109638","loc":[-85.6349605,41.949749]},"n2199109636":{"id":"n2199109636","loc":[-85.6349605,41.9497639]},"n2199109634":{"id":"n2199109634","loc":[-85.6349061,41.94971]},"n2199109632":{"id":"n2199109632","loc":[-85.6349048,41.9496569]},"n2199109630":{"id":"n2199109630","loc":[-85.6348835,41.9496571]},"n2199109628":{"id":"n2199109628","loc":[-85.6348829,41.9497103]},"n2199109626":{"id":"n2199109626","loc":[-85.635227,41.9497738]},"n2199109624":{"id":"n2199109624","loc":[-85.6352184,41.9497787]},"n2199109622":{"id":"n2199109622","loc":[-85.6351181,41.9497806]},"n2199109620":{"id":"n2199109620","loc":[-85.6351181,41.9497456]},"n2199109618":{"id":"n2199109618","loc":[-85.6348842,41.9497651]},"n2199109616":{"id":"n2199109616","loc":[-85.6348827,41.9496238]},"n2199109615":{"id":"n2199109615","loc":[-85.6351268,41.9496206]},"n2199109614":{"id":"n2199109614","loc":[-85.6351261,41.9495891]},"n2199109613":{"id":"n2199109613","loc":[-85.6351957,41.9495881]},"n2199109612":{"id":"n2199109612","loc":[-85.6351924,41.9494515]},"n2199109611":{"id":"n2199109611","loc":[-85.6353997,41.9494488]},"n2199109610":{"id":"n2199109610","loc":[-85.6354074,41.9497715]},"n2189015681":{"id":"n2189015681","loc":[-85.6344229,41.9509639]},"n2189015677":{"id":"n2189015677","loc":[-85.634424,41.9507396]},"n2138493843":{"id":"n2138493843","loc":[-85.6343935,41.9502836]},"n2138493840":{"id":"n2138493840","loc":[-85.634398,41.9506264]},"n354002838":{"id":"n354002838","loc":[-85.6345197,41.9510631]},"n2114807590":{"id":"n2114807590","loc":[-85.634511,41.9499767]},"n185964353":{"id":"n185964353","loc":[-85.6374092,41.9498755]},"n1819849180":{"id":"n1819849180","loc":[-85.6348236,41.94996]},"n1819849115":{"id":"n1819849115","loc":[-85.6354372,41.9499538]},"n1819848921":{"id":"n1819848921","loc":[-85.6348439,41.951064]},"n1819848885":{"id":"n1819848885","loc":[-85.6354575,41.9510578]},"n185984281":{"id":"n185984281","loc":[-85.638075,41.949872]},"n2208608827":{"id":"n2208608827","loc":[-85.6339169,41.9473191]},"n2199109749":{"id":"n2199109749","loc":[-85.6342082,41.9477934]},"n2199109747":{"id":"n2199109747","loc":[-85.6342045,41.9476867]},"n2199109713":{"id":"n2199109713","loc":[-85.6342404,41.9474746]},"n2199109711":{"id":"n2199109711","loc":[-85.6342404,41.9476355]},"n2199109673":{"id":"n2199109673","loc":[-85.6340886,41.9483282]},"n2199109667":{"id":"n2199109667","loc":[-85.6342403,41.9482988]},"n2199109665":{"id":"n2199109665","loc":[-85.6342386,41.9482116]},"n2199109662":{"id":"n2199109662","loc":[-85.6340861,41.9482135]},"n2199109660":{"id":"n2199109660","loc":[-85.6340802,41.9480562]},"n2199109640":{"id":"n2199109640","loc":[-85.6340928,41.9485063]},"n354031366":{"id":"n354031366","loc":[-85.6341667,41.9477778],"tags":{"amenity":"place_of_worship","name":"Faith Tabernacle Church","religion":"christian"}},"n2189015686":{"id":"n2189015686","loc":[-85.6337798,41.95099]},"n2189015684":{"id":"n2189015684","loc":[-85.6337794,41.9509674]},"n2189015673":{"id":"n2189015673","loc":[-85.6337501,41.9507457]},"n2189015669":{"id":"n2189015669","loc":[-85.6337501,41.9506974]},"n2189015665":{"id":"n2189015665","loc":[-85.6339034,41.9506959]},"n2189015662":{"id":"n2189015662","loc":[-85.6339015,41.950436]},"n2189015658":{"id":"n2189015658","loc":[-85.6334916,41.9504376]},"n2189015655":{"id":"n2189015655","loc":[-85.6334939,41.9507558]},"n2189015650":{"id":"n2189015650","loc":[-85.6334543,41.950756]},"n2189015649":{"id":"n2189015649","loc":[-85.633456,41.9509915]},"n2138493842":{"id":"n2138493842","loc":[-85.6339937,41.9502836]},"n2138493841":{"id":"n2138493841","loc":[-85.6339983,41.9506281]},"n2114807579":{"id":"n2114807579","loc":[-85.6333644,41.9510682]},"n2114807573":{"id":"n2114807573","loc":[-85.6333557,41.9499819]},"n354031330":{"id":"n354031330","loc":[-85.6341667,41.9497222],"tags":{"amenity":"place_of_worship","name":"Trinity Episcopal Church","religion":"christian"}},"n185960794":{"id":"n185960794","loc":[-85.633307,41.9537]},"n185964357":{"id":"n185964357","loc":[-85.637432,41.952399]},"n185964358":{"id":"n185964358","loc":[-85.637452,41.953665]},"n185964359":{"id":"n185964359","loc":[-85.63746,41.954658]},"n185964360":{"id":"n185964360","loc":[-85.637473,41.95592]},"n185964361":{"id":"n185964361","loc":[-85.637468,41.956906]},"n185964362":{"id":"n185964362","loc":[-85.637483,41.958313]},"n185966957":{"id":"n185966957","loc":[-85.633361,41.957422]},"n185975351":{"id":"n185975351","loc":[-85.63334,41.9559]},"n185978784":{"id":"n185978784","loc":[-85.633311,41.954679]},"n185986157":{"id":"n185986157","loc":[-85.633287,41.952426]},"n185986158":{"id":"n185986158","loc":[-85.6333607,41.9582301],"tags":{"highway":"turning_circle"}},"w17965182":{"id":"w17965182","tags":{"highway":"residential","name":"W Prutzman St"},"nodes":["n185965289","n2189153241","n185965291"]},"w208627205":{"id":"w208627205","tags":{"area":"yes","building":"yes"},"nodes":["n2189015649","n2189015650","n2189015655","n2189015658","n2189015662","n2189015665","n2189015669","n2189015673","n2189015677","n2189015681","n2189015684","n2189015686","n2189015649"]},"w209717042":{"id":"w209717042","tags":{"amenity":"place_of_worship","area":"yes","building":"yes","denomination":"presbyterian","name":"First Presbyterian Church","religion":"christian"},"nodes":["n2199109610","n2199109611","n2199109612","n2199109613","n2199109614","n2199109615","n2199109616","n2199109630","n2199109632","n2199109634","n2199109628","n2199109618","n2199109636","n2199109638","n2199109620","n2199109622","n2199109624","n2199109626","n2199109610"]},"w209717045":{"id":"w209717045","tags":{"area":"yes","building":"yes"},"nodes":["n2199109711","n2199109713","n2199109715","n2199109717","n2199109719","n2199109721","n2199109711"]},"w209717047":{"id":"w209717047","tags":{"area":"yes","building":"yes"},"nodes":["n2199109739","n2199109741","n2199109743","n2199109745","n2199109747","n2199109749","n2199109751","n2199109753","n2199109739"]},"w209717044":{"id":"w209717044","tags":{"area":"yes","building":"yes"},"nodes":["n2199109674","n2199109676","n2199109678","n2199109680","n2199109682","n2199109684","n2199109686","n2199109688","n2199109690","n2199109692","n2199109694","n2199109696","n2199109698","n2199109700","n2199109702","n2199109705","n2199109707","n2199109709","n2199109674"]},"w210822776":{"id":"w210822776","tags":{"highway":"service","service":"alley","surface":"unpaved"},"nodes":["n2208608795","n2208608806","n2208608825","n2208608800","n2189153241"]},"w210822778":{"id":"w210822778","tags":{"highway":"service","service":"alley"},"nodes":["n2208608826","n2208608827"]},"w209717050":{"id":"w209717050","tags":{"area":"yes","building":"yes"},"nodes":["n2199109787","n2199109789","n2199109791","n2199109793","n2199109795","n2199109797","n2199109799","n2199109802","n2199109804","n2199109806","n2199109787"]},"w17965097":{"id":"w17965097","tags":{"highway":"residential","name":"Maple Street"},"nodes":["n185964351","n185964352","n185964353","n185964355","n185964357","n185964358","n185964359","n185964360","n185964361","n185964362"]},"w17965856":{"id":"w17965856","tags":{"highway":"residential","name":"W Kelsey St"},"nodes":["n185971578","n2208608800","n185971580","n185964351"]},"w17967444":{"id":"w17967444","tags":{"highway":"residential","name":"East Street"},"nodes":["n185966937","n185978969","n185986155","n185985812","n185986157","n185960794","n185978784","n185975351","n185966957","n185986158"]},"w17967764":{"id":"w17967764","tags":{"highway":"residential","name":"Rock River Ave"},"nodes":["n185984017","n185964351"]},"w170848329":{"id":"w170848329","tags":{"leisure":"park","name":"LaFayette Park"},"nodes":["n1819849180","n1819849115","n1819848885","n1819848921","n1819849180"]},"w17967208":{"id":"w17967208","tags":{"highway":"residential","name":"West Bennett Street"},"nodes":["n185978390","n2208608795","n185984020","n185964353","n185984281"]},"w17965349":{"id":"w17965349","tags":{"highway":"residential","name":"E Prutzman St"},"nodes":["n185966937","n2208608827","n185965289"]},"w209717049":{"id":"w209717049","tags":{"area":"yes","building":"yes"},"nodes":["n2199109761","n2199109762","n2199109763","n2199109765","n2199109768","n2199109770","n2199109783","n2199109785","n2199109761"]},"w203841840":{"id":"w203841840","tags":{"area":"yes","leisure":"playground"},"nodes":["n2138493840","n2138493841","n2138493842","n2138493843","n2138493840"]},"w209717043":{"id":"w209717043","tags":{"amenity":"place_of_worship","area":"yes","building":"church","denomination":"methodist","name":"First United Methodist Church","religion":"christian"},"nodes":["n2199109640","n2199109642","n2199109645","n2199109648","n2199109650","n2199109652","n2199109654","n2199109656","n2199109658","n2199109660","n2199109662","n2199109665","n2199109667","n2199109669","n2199109671","n2199109673","n2199109640"]},"w201484341":{"id":"w201484341","tags":{"amenity":"school","name":"Hoppin School"},"nodes":["n354002838","n2114807579","n2114807573","n2114807590","n354002838"]},"w209717046":{"id":"w209717046","tags":{"area":"yes","building":"yes"},"nodes":["n2199109723","n2199109725","n2199109727","n2199109729","n2199109731","n2199109733","n2199109735","n2199109737","n2199109723"]},"w210822777":{"id":"w210822777","tags":{"amenity":"parking","area":"yes"},"nodes":["n2208608806","n2208608808","n2208608811","n2208608821","n2208608823","n2208608825","n2208608806"]},"n185954965":{"id":"n185954965","loc":[-85.6191189,41.9441922]},"n185954968":{"id":"n185954968","loc":[-85.6194384,41.9442405]},"n185954970":{"id":"n185954970","loc":[-85.6196543,41.9443252]},"n185954972":{"id":"n185954972","loc":[-85.6197862,41.9444539]},"n354002931":{"id":"n354002931","loc":[-85.6198991,41.9455269]},"n354030853":{"id":"n354030853","loc":[-85.6219444,41.9455556],"tags":{"amenity":"place_of_worship","name":"Grant Chapel","religion":"christian"}},"n367815963":{"id":"n367815963","loc":[-85.6202778,41.9461111],"tags":{"building":"yes","name":"George Washington Carver Community Center"}},"n185947331":{"id":"n185947331","loc":[-85.618779,41.943269]},"n185947333":{"id":"n185947333","loc":[-85.618795,41.943511]},"n185947336":{"id":"n185947336","loc":[-85.618711,41.94413]},"n185947338":{"id":"n185947338","loc":[-85.618704,41.944189]},"n185947339":{"id":"n185947339","loc":[-85.618597,41.944337]},"n185947340":{"id":"n185947340","loc":[-85.618485,41.944528]},"n185947343":{"id":"n185947343","loc":[-85.618442,41.944716]},"n185947345":{"id":"n185947345","loc":[-85.618457,41.945107]},"n185947347":{"id":"n185947347","loc":[-85.618296,41.945338]},"n185947374":{"id":"n185947374","loc":[-85.616748,41.944453]},"n185947375":{"id":"n185947375","loc":[-85.616813,41.944646]},"n185947376":{"id":"n185947376","loc":[-85.616859,41.945196]},"n185947377":{"id":"n185947377","loc":[-85.616941,41.945352]},"n185947406":{"id":"n185947406","loc":[-85.618184,41.944227]},"n185947409":{"id":"n185947409","loc":[-85.617911,41.943875]},"n185947410":{"id":"n185947410","loc":[-85.617579,41.943682]},"n185947411":{"id":"n185947411","loc":[-85.61713,41.943589]},"n185947412":{"id":"n185947412","loc":[-85.616549,41.943559]},"n185947414":{"id":"n185947414","loc":[-85.616482,41.943556]},"n185947464":{"id":"n185947464","loc":[-85.616526,41.943788]},"n185947466":{"id":"n185947466","loc":[-85.616504,41.944002]},"n185948863":{"id":"n185948863","loc":[-85.619017,41.943391]},"n185948865":{"id":"n185948865","loc":[-85.619059,41.943368]},"n185955022":{"id":"n185955022","loc":[-85.620088,41.945571]},"n185955025":{"id":"n185955025","loc":[-85.620051,41.945505]},"n185955028":{"id":"n185955028","loc":[-85.62001,41.94541]},"n185980371":{"id":"n185980371","loc":[-85.620982,41.944742]},"n185980398":{"id":"n185980398","loc":[-85.621305,41.944782]},"n185980401":{"id":"n185980401","loc":[-85.621174,41.944819]},"n185980403":{"id":"n185980403","loc":[-85.621029,41.944871]},"n185980405":{"id":"n185980405","loc":[-85.620741,41.945011]},"n185980407":{"id":"n185980407","loc":[-85.620616,41.945085]},"n185980409":{"id":"n185980409","loc":[-85.620506,41.945172]},"n185980411":{"id":"n185980411","loc":[-85.620394,41.945273]},"n185980413":{"id":"n185980413","loc":[-85.620316,41.94536]},"n185980415":{"id":"n185980415","loc":[-85.620257,41.945452]},"n185980417":{"id":"n185980417","loc":[-85.620212,41.945535]},"n185985910":{"id":"n185985910","loc":[-85.620101,41.945811]},"n185985912":{"id":"n185985912","loc":[-85.620081,41.945937]},"n1475283972":{"id":"n1475283972","loc":[-85.6198991,41.9437179]},"n1475283982":{"id":"n1475283982","loc":[-85.6195022,41.9433463]},"n1475284007":{"id":"n1475284007","loc":[-85.6193037,41.9433383]},"n1475284040":{"id":"n1475284040","loc":[-85.6197329,41.9434121]},"n1475284044":{"id":"n1475284044","loc":[-85.6198756,41.9435363]},"n1475284050":{"id":"n1475284050","loc":[-85.6199689,41.9432106]},"n1475284053":{"id":"n1475284053","loc":[-85.6198943,41.9432921]},"n185954974":{"id":"n185954974","loc":[-85.6198296,41.94473]},"n185954977":{"id":"n185954977","loc":[-85.6200474,41.9447384]},"n2196831365":{"id":"n2196831365","loc":[-85.6202259,41.9460883]},"n2196831366":{"id":"n2196831366","loc":[-85.6202245,41.9458642]},"n2196831367":{"id":"n2196831367","loc":[-85.6205184,41.9458631]},"n2196831368":{"id":"n2196831368","loc":[-85.6205189,41.9459437]},"n2196831369":{"id":"n2196831369","loc":[-85.6203879,41.9459441]},"n2196831370":{"id":"n2196831370","loc":[-85.6203888,41.9460878]},"n2196831371":{"id":"n2196831371","loc":[-85.6184046,41.9465663]},"n2196831372":{"id":"n2196831372","loc":[-85.6191563,41.9465618]},"n2196831373":{"id":"n2196831373","loc":[-85.6191536,41.946319]},"n2196831374":{"id":"n2196831374","loc":[-85.6187356,41.9463216]},"n2196831375":{"id":"n2196831375","loc":[-85.6187334,41.9461197]},"n2196831376":{"id":"n2196831376","loc":[-85.6193167,41.9461162]},"n2196831377":{"id":"n2196831377","loc":[-85.6193156,41.9460229]},"n2196831378":{"id":"n2196831378","loc":[-85.619622,41.946021]},"n2196831379":{"id":"n2196831379","loc":[-85.6196237,41.9461712]},"n2196831380":{"id":"n2196831380","loc":[-85.6197702,41.9461703]},"n2196831381":{"id":"n2196831381","loc":[-85.6197685,41.9460202]},"n2196831382":{"id":"n2196831382","loc":[-85.6197323,41.9460204]},"n2196831383":{"id":"n2196831383","loc":[-85.6197305,41.9458563]},"n2196831384":{"id":"n2196831384","loc":[-85.6196165,41.945857]},"n2196831385":{"id":"n2196831385","loc":[-85.6196156,41.9457764]},"n2196831386":{"id":"n2196831386","loc":[-85.6194472,41.9457775]},"n2196831387":{"id":"n2196831387","loc":[-85.6194151,41.9457777]},"n2196831388":{"id":"n2196831388","loc":[-85.6183779,41.9457883]},"n2196831389":{"id":"n2196831389","loc":[-85.6183842,41.9461317]},"n2196831390":{"id":"n2196831390","loc":[-85.6185026,41.9461304]},"n2196831391":{"id":"n2196831391","loc":[-85.6185061,41.9463194]},"n2196831392":{"id":"n2196831392","loc":[-85.6184001,41.9463205]},"n2196831393":{"id":"n2196831393","loc":[-85.6182482,41.9464163]},"n2196831394":{"id":"n2196831394","loc":[-85.6182467,41.9463193]},"n2196831395":{"id":"n2196831395","loc":[-85.6180389,41.946321]},"n2196831397":{"id":"n2196831397","loc":[-85.6180404,41.946418]},"n185947303":{"id":"n185947303","loc":[-85.611074,41.943389]},"n185947304":{"id":"n185947304","loc":[-85.611332,41.943267]},"n185947305":{"id":"n185947305","loc":[-85.611635,41.943218]},"n185947306":{"id":"n185947306","loc":[-85.612762,41.943311]},"n185947308":{"id":"n185947308","loc":[-85.613027,41.943327]},"n185947310":{"id":"n185947310","loc":[-85.615377,41.942996]},"n185947312":{"id":"n185947312","loc":[-85.615701,41.943007]},"n185947314":{"id":"n185947314","loc":[-85.61604,41.943067]},"n185947315":{"id":"n185947315","loc":[-85.61626,41.943083]},"n185947316":{"id":"n185947316","loc":[-85.616507,41.943048]},"n185947319":{"id":"n185947319","loc":[-85.616702,41.94299]},"n185947321":{"id":"n185947321","loc":[-85.617078,41.942918]},"n185947322":{"id":"n185947322","loc":[-85.617366,41.942973]},"n185947323":{"id":"n185947323","loc":[-85.617601,41.943033]},"n185947325":{"id":"n185947325","loc":[-85.617799,41.943027]},"n185947327":{"id":"n185947327","loc":[-85.618264,41.942961]},"n185947328":{"id":"n185947328","loc":[-85.618508,41.942972]},"n185947329":{"id":"n185947329","loc":[-85.618707,41.943076]},"n185947361":{"id":"n185947361","loc":[-85.615356,41.944922]},"n185947363":{"id":"n185947363","loc":[-85.61536,41.944893]},"n185947365":{"id":"n185947365","loc":[-85.615406,41.944547]},"n185947367":{"id":"n185947367","loc":[-85.61548,41.944351]},"n185947369":{"id":"n185947369","loc":[-85.615805,41.94419]},"n185947371":{"id":"n185947371","loc":[-85.616166,41.944156]},"n185947373":{"id":"n185947373","loc":[-85.616411,41.944197]},"n185947416":{"id":"n185947416","loc":[-85.616335,41.94343]},"n185947417":{"id":"n185947417","loc":[-85.616069,41.943293]},"n185947419":{"id":"n185947419","loc":[-85.615803,41.943249]},"n185947420":{"id":"n185947420","loc":[-85.615524,41.943342]},"n185947421":{"id":"n185947421","loc":[-85.615311,41.94353]},"n185947422":{"id":"n185947422","loc":[-85.614338,41.943558]},"n185947423":{"id":"n185947423","loc":[-85.61422,41.94369]},"n185947425":{"id":"n185947425","loc":[-85.614221,41.944224]},"n185947427":{"id":"n185947427","loc":[-85.614198,41.944888]},"n185947429":{"id":"n185947429","loc":[-85.614221,41.945439]},"n185947468":{"id":"n185947468","loc":[-85.615908,41.944756]},"n185947470":{"id":"n185947470","loc":[-85.615871,41.944888]},"n185947472":{"id":"n185947472","loc":[-85.615878,41.94507]},"n185955153":{"id":"n185955153","loc":[-85.620087,41.947701]},"n185960690":{"id":"n185960690","loc":[-85.620141,41.951901]},"n185978817":{"id":"n185978817","loc":[-85.617193,41.954706]},"n185985916":{"id":"n185985916","loc":[-85.620088,41.94758]},"n185985918":{"id":"n185985918","loc":[-85.620133,41.951538]},"n185985919":{"id":"n185985919","loc":[-85.62013,41.952104]},"n185985920":{"id":"n185985920","loc":[-85.620104,41.952305]},"n185985921":{"id":"n185985921","loc":[-85.620062,41.952499]},"n185985922":{"id":"n185985922","loc":[-85.619993,41.952702]},"n185985925":{"id":"n185985925","loc":[-85.619879,41.952986]},"n185985927":{"id":"n185985927","loc":[-85.619689,41.95329]},"n185985928":{"id":"n185985928","loc":[-85.619508,41.953521]},"n185985929":{"id":"n185985929","loc":[-85.619286,41.953728]},"n185985930":{"id":"n185985930","loc":[-85.618925,41.954007]},"n185985931":{"id":"n185985931","loc":[-85.618638,41.954189]},"n185985932":{"id":"n185985932","loc":[-85.61831,41.954358]},"n185985934":{"id":"n185985934","loc":[-85.618015,41.954485]},"n185985936":{"id":"n185985936","loc":[-85.617606,41.954611]},"n1475283975":{"id":"n1475283975","loc":[-85.6150935,41.9434118]},"n1475283979":{"id":"n1475283979","loc":[-85.6193367,41.9430252]},"n1475283989":{"id":"n1475283989","loc":[-85.6104771,41.9455269]},"n1475283990":{"id":"n1475283990","loc":[-85.6104771,41.9437179]},"n1475283994":{"id":"n1475283994","loc":[-85.6198042,41.9429763]},"n1475283998":{"id":"n1475283998","loc":[-85.6192101,41.9426716]},"n1475284000":{"id":"n1475284000","loc":[-85.6198622,41.942836]},"n1475284002":{"id":"n1475284002","loc":[-85.6163262,41.9427688]},"n1475284006":{"id":"n1475284006","loc":[-85.6179527,41.9429168]},"n1475284029":{"id":"n1475284029","loc":[-85.6197195,41.9427278]},"n1475284038":{"id":"n1475284038","loc":[-85.6194405,41.9427837]},"n1475284052":{"id":"n1475284052","loc":[-85.6153225,41.942841]},"n1475284055":{"id":"n1475284055","loc":[-85.6129233,41.9437179]},"n2139966627":{"id":"n2139966627","loc":[-85.61958,41.9427558]},"w17966773":{"id":"w17966773","tags":{"highway":"secondary","name":"E Michigan Ave","ref":"M 60"},"nodes":["n185980372","n185980398","n185980401","n185980403","n185980405","n185980407","n185980409","n185980411","n185980413","n185980415","n185980417","n185955019"]},"w17964043":{"id":"w17964043","tags":{"highway":"residential"},"nodes":["n185955019","n185955022","n185955025","n185955028","n185954977","n185971477","n1475284050","n1475284000","n1475284029","n2139966627","n1475284038"]},"w17962834":{"id":"w17962834","tags":{"highway":"service"},"nodes":["n185947316","n185947414","n185947464","n185947466","n185947373","n185947468","n185947470","n185947472","n185947474"]},"w209470310":{"id":"w209470310","tags":{"area":"yes","building":"yes"},"nodes":["n2196831393","n2196831394","n2196831395","n2196831397","n2196831393"]},"w17963058":{"id":"w17963058","tags":{"highway":"service"},"nodes":["n185947333","n185948863","n185948865","n1475284007","n1475283982","n1475284040","n1475284044"]},"w17962823":{"id":"w17962823","tags":{"highway":"service"},"nodes":["n185947359","n185947361","n185947363","n185947365","n185947367","n185947369","n185947371","n185947373","n185947374","n185947375","n185947376","n185947377","n185947378"]},"w17962821":{"id":"w17962821","tags":{"highway":"service"},"nodes":["n185947303","n185947304","n185947305","n185947306","n185947308","n185947310","n185947312","n185947314","n185947315","n185947316","n185947319","n185947321","n185947322","n185947323","n185947325","n185947327","n185947328","n185947329","n185947331","n185947333","n185947336","n185947338","n185947339","n185947340","n185947343","n185947345","n185947347","n185947349"]},"w134150798":{"id":"w134150798","tags":{"amenity":"grave_yard","name":"Riverside Cemetery"},"nodes":["n354002931","n1475283972","n1475284053","n1475283994","n1475283979","n1475283998","n1475284006","n1475284002","n1475284052","n1475283975","n1475284055","n1475283990","n1475283989","n354002931"]},"w17964040":{"id":"w17964040","tags":{"highway":"service"},"nodes":["n185947336","n185954965","n185954968","n185954970","n185954972","n185954974","n185954977"]},"w209470308":{"id":"w209470308","tags":{"area":"yes","building":"yes"},"nodes":["n2196831365","n2196831366","n2196831367","n2196831368","n2196831369","n2196831370","n2196831365"]},"w17962828":{"id":"w17962828","tags":{"highway":"service"},"nodes":["n185947340","n185947406","n185947409","n185947410","n185947411","n185947412","n185947414","n185947416","n185947417","n185947419","n185947420","n185947421","n185947422","n185947423","n185947425","n185947427","n185947429"]},"w209470309":{"id":"w209470309","tags":{"area":"yes","building":"yes"},"nodes":["n2196831371","n2196831372","n2196831373","n2196831374","n2196831375","n2196831376","n2196831377","n2196831378","n2196831379","n2196831380","n2196831381","n2196831382","n2196831383","n2196831384","n2196831385","n2196831386","n2196831387","n2196831388","n2196831389","n2196831390","n2196831391","n2196831392","n2196831371"]},"w17967415":{"id":"w17967415","tags":{"highway":"secondary","name":"Jefferson St","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185955019","n185985910","n185985912","n185985914","n185961367","n185985916","n185955153","n185965105","n185974697","n185955120","n185985918","n185960690","n185985919","n185985920","n185985921","n185985922","n185985925","n185985927","n185985928","n185985929","n185985930","n185985931","n185985932","n185985934","n185985936","n185978817"]},"w17966772":{"id":"w17966772","tags":{"highway":"unclassified","name":"E Michigan Ave","name_1":"State Highway 60"},"nodes":["n185954977","n185980371","n185980372"]},"n185958500":{"id":"n185958500","loc":[-85.621591,41.941075]},"n185963110":{"id":"n185963110","loc":[-85.6204416,41.9408882]},"n2139966628":{"id":"n2139966628","loc":[-85.6196431,41.9426467],"tags":{"leisure":"fishing"}},"n2139966630":{"id":"n2139966630","loc":[-85.6199354,41.9429616]},"n2199127051":{"id":"n2199127051","loc":[-85.6170556,41.939696]},"n2199127052":{"id":"n2199127052","loc":[-85.6170536,41.9392909]},"n2199127053":{"id":"n2199127053","loc":[-85.6172067,41.9392905]},"n2199127054":{"id":"n2199127054","loc":[-85.6172061,41.9391853]},"n2199127055":{"id":"n2199127055","loc":[-85.6171481,41.9391854]},"n2199127060":{"id":"n2199127060","loc":[-85.6167389,41.9392896]},"n2199127061":{"id":"n2199127061","loc":[-85.6168728,41.9392892]},"n2199127062":{"id":"n2199127062","loc":[-85.6168747,41.9396965]},"n2199127071":{"id":"n2199127071","loc":[-85.620196,41.9399446]},"n2199127072":{"id":"n2199127072","loc":[-85.620193,41.9397316]},"n2199127073":{"id":"n2199127073","loc":[-85.6200381,41.9397328]},"n2199127074":{"id":"n2199127074","loc":[-85.6200412,41.9399458]},"n2199127075":{"id":"n2199127075","loc":[-85.6203606,41.9399939]},"n2199127076":{"id":"n2199127076","loc":[-85.6205527,41.9399922]},"n2199127077":{"id":"n2199127077","loc":[-85.6205482,41.9397115]},"n2199127078":{"id":"n2199127078","loc":[-85.6204132,41.9397124]},"n2199127079":{"id":"n2199127079","loc":[-85.6204144,41.9396341]},"n2199127080":{"id":"n2199127080","loc":[-85.6205699,41.9396324]},"n2199127081":{"id":"n2199127081","loc":[-85.6205722,41.939498]},"n2199127082":{"id":"n2199127082","loc":[-85.6204064,41.9394997]},"n2199127083":{"id":"n2199127083","loc":[-85.6204087,41.939561]},"n2199127084":{"id":"n2199127084","loc":[-85.6203103,41.9395618]},"n2199127085":{"id":"n2199127085","loc":[-85.620308,41.9396069]},"n2199127086":{"id":"n2199127086","loc":[-85.6200347,41.9396086]},"n2199127087":{"id":"n2199127087","loc":[-85.6200382,41.9397141]},"n2199127088":{"id":"n2199127088","loc":[-85.6202257,41.9397149]},"n2199127089":{"id":"n2199127089","loc":[-85.6202269,41.9399182]},"n2199127090":{"id":"n2199127090","loc":[-85.6203595,41.9399199]},"n2199127091":{"id":"n2199127091","loc":[-85.6212335,41.939688]},"n2199127092":{"id":"n2199127092","loc":[-85.6212328,41.939595]},"n2199127093":{"id":"n2199127093","loc":[-85.6208807,41.9395966]},"n2199127094":{"id":"n2199127094","loc":[-85.6208815,41.9396896]},"n2199127095":{"id":"n2199127095","loc":[-85.6208676,41.9396872]},"n2199127096":{"id":"n2199127096","loc":[-85.6208583,41.9393539]},"n2199127097":{"id":"n2199127097","loc":[-85.6207006,41.9393563]},"n2199127098":{"id":"n2199127098","loc":[-85.6207099,41.9396896]},"n185967054":{"id":"n185967054","loc":[-85.6173384,41.9356126]},"n185967063":{"id":"n185967063","loc":[-85.617371,41.936243]},"n185967065":{"id":"n185967065","loc":[-85.617337,41.936299]},"n185967068":{"id":"n185967068","loc":[-85.617321,41.936373]},"n185967070":{"id":"n185967070","loc":[-85.6173562,41.9366969]},"n185967074":{"id":"n185967074","loc":[-85.6173635,41.9377414]},"n185967075":{"id":"n185967075","loc":[-85.6173696,41.9381886]},"n185967076":{"id":"n185967076","loc":[-85.617372,41.938535]},"n2199127056":{"id":"n2199127056","loc":[-85.617147,41.9389616]},"n2199127057":{"id":"n2199127057","loc":[-85.6172136,41.9389614]},"n2199127058":{"id":"n2199127058","loc":[-85.6172123,41.9386909]},"n2199127059":{"id":"n2199127059","loc":[-85.616736,41.9386922]},"n2203921041":{"id":"n2203921041","loc":[-85.6173018,41.9346369]},"w203983952":{"id":"w203983952","tags":{"highway":"service"},"nodes":["n2139966627","n1819800319"]},"w209718301":{"id":"w209718301","tags":{"area":"yes","building":"yes"},"nodes":["n2199127051","n2199127052","n2199127053","n2199127054","n2199127055","n2199127056","n2199127057","n2199127058","n2199127059","n2199127060","n2199127061","n2199127062","n2199127051"]},"w209718304":{"id":"w209718304","tags":{"area":"yes","building":"yes"},"nodes":["n2199127071","n2199127072","n2199127073","n2199127074","n2199127071"]},"w17964961":{"id":"w17964961","tags":{"highway":"residential","name":"Whipple St"},"nodes":["n185963099","n185963110"]},"w17964489":{"id":"w17964489","tags":{"highway":"residential","name":"Jackson St"},"nodes":["n185958498","n185958500"]},"w203983953":{"id":"w203983953","tags":{"area":"yes","leisure":"park","name":"Marina Park"},"nodes":["n1475283994","n1475283979","n1475283998","n2139966629","n2139966625","n1819800319","n2139966623","n2139966622","n2139966621","n2139966630","n1475283994"]},"w17965366":{"id":"w17965366","tags":{"highway":"residential","name":"14th St"},"nodes":["n2203921041","n185967054","n185967063","n185967065","n185967068","n185967070","n185967074","n185967075","n185967076","n185967077"]},"w209718306":{"id":"w209718306","tags":{"area":"yes","building":"yes"},"nodes":["n2199127091","n2199127092","n2199127093","n2199127094","n2199127091"]},"w209718307":{"id":"w209718307","tags":{"area":"yes","building":"yes"},"nodes":["n2199127095","n2199127096","n2199127097","n2199127098","n2199127095"]},"w209718305":{"id":"w209718305","tags":{"area":"yes","building":"yes"},"nodes":["n2199127075","n2199127076","n2199127077","n2199127078","n2199127079","n2199127080","n2199127081","n2199127082","n2199127083","n2199127084","n2199127085","n2199127086","n2199127087","n2199127088","n2199127089","n2199127090","n2199127075"]},"n185960199":{"id":"n185960199","loc":[-85.62965,41.95469]},"n185980737":{"id":"n185980737","loc":[-85.629083,41.953725]},"n2114807561":{"id":"n2114807561","loc":[-85.6297681,41.9524688]},"n2114807597":{"id":"n2114807597","loc":[-85.6296517,41.952563]},"n185960197":{"id":"n185960197","loc":[-85.629676,41.9537314]},"n185978791":{"id":"n185978791","loc":[-85.6244542,41.9547066]},"w17967573":{"id":"w17967573","tags":{"highway":"residential","name":"E Wheeler St"},"nodes":["n185960195","n2114807561","n185968102","n185967430","n185986157","n185978392"]},"w17966553":{"id":"w17966553","tags":{"highway":"residential","name":"East Hoffman Street"},"nodes":["n185971631","n185978784","n185967432","n185968106","n185960199","n185978787","n185978790","n185978791"]},"w17966787":{"id":"w17966787","tags":{"highway":"residential","name":"East Cushman Street"},"nodes":["n185980735","n185980737","n185960197","n185968104","n185960792"]},"w17964723":{"id":"w17964723","tags":{"highway":"residential","name":"Cushman Street"},"nodes":["n185960792","n185960794","n185960796"]},"w17964654":{"id":"w17964654","tags":{"highway":"residential","name":"Pine Street"},"nodes":["n185960195","n2114807597","n185960197","n185960199"]},"n1819848862":{"id":"n1819848862","loc":[-85.6346087,41.9545845]},"n1819848935":{"id":"n1819848935","loc":[-85.6345948,41.9537717]},"n1819848973":{"id":"n1819848973","loc":[-85.6334247,41.9537827]},"n1819848997":{"id":"n1819848997","loc":[-85.6334386,41.9545956]},"n2189015861":{"id":"n2189015861","loc":[-85.6375906,41.954836]},"n2189015865":{"id":"n2189015865","loc":[-85.6383307,41.9548291]},"n2189015867":{"id":"n2189015867","loc":[-85.6383337,41.9550072]},"n2189015868":{"id":"n2189015868","loc":[-85.6380986,41.9550094]},"n2189015869":{"id":"n2189015869","loc":[-85.6381005,41.9551226]},"n2199109808":{"id":"n2199109808","loc":[-85.6372702,41.9522894]},"n2199109810":{"id":"n2199109810","loc":[-85.6372677,41.9521583]},"n2199109812":{"id":"n2199109812","loc":[-85.6369505,41.9521617]},"n2199109814":{"id":"n2199109814","loc":[-85.636953,41.9522927]},"n185952156":{"id":"n185952156","loc":[-85.640983,41.9546557]},"n185953423":{"id":"n185953423","loc":[-85.641871,41.954652]},"n185971637":{"id":"n185971637","loc":[-85.641583,41.95465]},"n185971639":{"id":"n185971639","loc":[-85.6421344,41.9546444]},"n185971642":{"id":"n185971642","loc":[-85.6428264,41.9545612]},"n185971648":{"id":"n185971648","loc":[-85.6436023,41.9544262]},"n185975066":{"id":"n185975066","loc":[-85.640532,41.953638]},"n185975067":{"id":"n185975067","loc":[-85.64079,41.953638]},"n185982166":{"id":"n185982166","loc":[-85.6399012,41.9523817]},"n2189015858":{"id":"n2189015858","loc":[-85.6376104,41.9560138]},"n2189015870":{"id":"n2189015870","loc":[-85.6386794,41.9551172]},"n2189015871":{"id":"n2189015871","loc":[-85.6386817,41.955256]},"n2189015873":{"id":"n2189015873","loc":[-85.6385437,41.9552573]},"n2189015876":{"id":"n2189015876","loc":[-85.638555,41.9559278]},"n2189015879":{"id":"n2189015879","loc":[-85.6384954,41.9559283]},"n2189015882":{"id":"n2189015882","loc":[-85.6384965,41.9559935]},"n2189015885":{"id":"n2189015885","loc":[-85.6383533,41.9559949]},"n2189015888":{"id":"n2189015888","loc":[-85.638351,41.9558607]},"n2189015891":{"id":"n2189015891","loc":[-85.6382178,41.9558619]},"n2189015894":{"id":"n2189015894","loc":[-85.6382203,41.956008]},"w208627223":{"id":"w208627223","tags":{"area":"yes","building":"yes"},"nodes":["n2189015858","n2189015861","n2189015865","n2189015867","n2189015868","n2189015869","n2189015870","n2189015871","n2189015873","n2189015876","n2189015879","n2189015882","n2189015885","n2189015888","n2189015891","n2189015894","n2189015858"]},"w170848328":{"id":"w170848328","tags":{"leisure":"park","name":"Bowman Park"},"nodes":["n1819848935","n1819848973","n1819848997","n1819848862","n1819848935"]},"w17965866":{"id":"w17965866","tags":{"highway":"residential","name":"West Hoffman Street"},"nodes":["n185971631","n185971632","n185964359","n185965025","n1475293264","n185952156","n185971637","n185953423","n185971639","n185971642","n185971648"]},"w209717051":{"id":"w209717051","tags":{"amenity":"place_of_worship","area":"yes","building":"yes","denomination":"baptist","name":"Calvary Missionary Baptist Church","religion":"christian"},"nodes":["n2199109808","n2199109810","n2199109812","n2199109814","n2199109808"]},"w17966172":{"id":"w17966172","tags":{"highway":"residential","name":"West Cushman Street"},"nodes":["n185960796","n185975064","n185964358","n185965023","n1475293222","n185975066","n185975067"]},"w17966975":{"id":"w17966975","tags":{"highway":"residential","name":"W Wheeler St"},"nodes":["n185978392","n185982163","n185964357","n185965021","n1475293261","n185982166"]},"n185960684":{"id":"n185960684","loc":[-85.622687,41.951885]},"n185960686":{"id":"n185960686","loc":[-85.622492,41.951901]},"n185978795":{"id":"n185978795","loc":[-85.6240991,41.954708]},"n185978803":{"id":"n185978803","loc":[-85.623348,41.954547]},"n185978806":{"id":"n185978806","loc":[-85.623123,41.954502]},"n185978808":{"id":"n185978808","loc":[-85.622923,41.954469]},"n185978810":{"id":"n185978810","loc":[-85.622787,41.954457]},"n185978811":{"id":"n185978811","loc":[-85.622612,41.954458]},"n185978813":{"id":"n185978813","loc":[-85.622368,41.954472]},"n1819790545":{"id":"n1819790545","loc":[-85.6240295,41.9548949]},"n1819790621":{"id":"n1819790621","loc":[-85.6235789,41.954855]},"n1819790664":{"id":"n1819790664","loc":[-85.6238363,41.9549507]},"n1819790683":{"id":"n1819790683","loc":[-85.6224727,41.9545921]},"n1819790730":{"id":"n1819790730","loc":[-85.6227527,41.9545795]},"n1819790740":{"id":"n1819790740","loc":[-85.6240402,41.9550784]},"n1819790831":{"id":"n1819790831","loc":[-85.624126,41.9549986]},"n1819790861":{"id":"n1819790861","loc":[-85.6231712,41.9546872]},"n1819790887":{"id":"n1819790887","loc":[-85.6242762,41.955206]},"n2168544739":{"id":"n2168544739","loc":[-85.6249102,41.952801]},"n2168544740":{"id":"n2168544740","loc":[-85.6251859,41.9527564]},"n2168544741":{"id":"n2168544741","loc":[-85.6255515,41.9527921]},"n2168544742":{"id":"n2168544742","loc":[-85.626001,41.9529481]},"n2168544743":{"id":"n2168544743","loc":[-85.6265284,41.9529838]},"n2168544744":{"id":"n2168544744","loc":[-85.626942,41.9528857]},"n2168544745":{"id":"n2168544745","loc":[-85.6270918,41.9526851]},"n2168544746":{"id":"n2168544746","loc":[-85.6272117,41.95244]},"n2168544747":{"id":"n2168544747","loc":[-85.6271578,41.952226]},"n2168544748":{"id":"n2168544748","loc":[-85.6270019,41.9519719]},"n2168544749":{"id":"n2168544749","loc":[-85.6268221,41.9518382]},"n2168544750":{"id":"n2168544750","loc":[-85.6265284,41.951807]},"n2168544751":{"id":"n2168544751","loc":[-85.6256534,41.9518516]},"n2168544752":{"id":"n2168544752","loc":[-85.6253477,41.9518338]},"n2168544753":{"id":"n2168544753","loc":[-85.6251139,41.9517669]},"n185955747":{"id":"n185955747","loc":[-85.620674,41.954709]},"n185960688":{"id":"n185960688","loc":[-85.621032,41.951913]},"n185972054":{"id":"n185972054","loc":[-85.6186728,41.9547335]},"n185978814":{"id":"n185978814","loc":[-85.6201708,41.9547403]},"n1819790532":{"id":"n1819790532","loc":[-85.6244908,41.9555731]},"n1819790536":{"id":"n1819790536","loc":[-85.6217925,41.9583135]},"n1819790538":{"id":"n1819790538","loc":[-85.6233954,41.9600014]},"n1819790539":{"id":"n1819790539","loc":[-85.6204611,41.9562117]},"n1819790546":{"id":"n1819790546","loc":[-85.6210898,41.9567657]},"n1819790548":{"id":"n1819790548","loc":[-85.6202465,41.9562237]},"n1819790550":{"id":"n1819790550","loc":[-85.6250165,41.9560677]},"n1819790551":{"id":"n1819790551","loc":[-85.6227946,41.9597023]},"n1819790553":{"id":"n1819790553","loc":[-85.6215726,41.9584571]},"n1819790556":{"id":"n1819790556","loc":[-85.6196306,41.9573002]},"n1819790557":{"id":"n1819790557","loc":[-85.6209503,41.9563109]},"n1819790558":{"id":"n1819790558","loc":[-85.6196939,41.9574085]},"n1819790561":{"id":"n1819790561","loc":[-85.621079,41.957751]},"n1819790562":{"id":"n1819790562","loc":[-85.6224255,41.9611417]},"n1819790565":{"id":"n1819790565","loc":[-85.6232506,41.9604841]},"n1819790566":{"id":"n1819790566","loc":[-85.6190835,41.9562909]},"n1819790567":{"id":"n1819790567","loc":[-85.622227,41.9593028]},"n1819790569":{"id":"n1819790569","loc":[-85.620976,41.9591039]},"n1819790571":{"id":"n1819790571","loc":[-85.6212078,41.9565303]},"n1819790572":{"id":"n1819790572","loc":[-85.6235306,41.9595102]},"n1819790581":{"id":"n1819790581","loc":[-85.6235563,41.9579351]},"n1819790584":{"id":"n1819790584","loc":[-85.6230371,41.9574598]},"n1819790586":{"id":"n1819790586","loc":[-85.6211748,41.9564272]},"n1819790588":{"id":"n1819790588","loc":[-85.6226508,41.9601086]},"n1819790591":{"id":"n1819790591","loc":[-85.6218032,41.9607468]},"n1819790593":{"id":"n1819790593","loc":[-85.6207915,41.9618735]},"n1819790596":{"id":"n1819790596","loc":[-85.6252955,41.9567858]},"n1819790598":{"id":"n1819790598","loc":[-85.6196618,41.9568939]},"n1819790600":{"id":"n1819790600","loc":[-85.6224416,41.9587084]},"n1819790602":{"id":"n1819790602","loc":[-85.6217442,41.9558641]},"n1819790603":{"id":"n1819790603","loc":[-85.6213355,41.9592116]},"n1819790604":{"id":"n1819790604","loc":[-85.622801,41.9573042]},"n1819790608":{"id":"n1819790608","loc":[-85.6199729,41.9574325]},"n1819790610":{"id":"n1819790610","loc":[-85.6195555,41.9557165]},"n1819790611":{"id":"n1819790611","loc":[-85.622978,41.9586007]},"n1819790613":{"id":"n1819790613","loc":[-85.6253963,41.9562636]},"n1819790614":{"id":"n1819790614","loc":[-85.6235252,41.9580342]},"n1819790616":{"id":"n1819790616","loc":[-85.6232988,41.9596305]},"n1819790617":{"id":"n1819790617","loc":[-85.6226776,41.9598732]},"n1819790619":{"id":"n1819790619","loc":[-85.625553,41.9561794]},"n1819790620":{"id":"n1819790620","loc":[-85.6235574,41.959231]},"n1819790624":{"id":"n1819790624","loc":[-85.6228429,41.9573726]},"n1819790626":{"id":"n1819790626","loc":[-85.6193785,41.9556766]},"n1819790628":{"id":"n1819790628","loc":[-85.620092,41.9554253]},"n1819790630":{"id":"n1819790630","loc":[-85.6226658,41.9604402]},"n1819790638":{"id":"n1819790638","loc":[-85.6219964,41.9602561]},"n1819790640":{"id":"n1819790640","loc":[-85.6232731,41.9599969]},"n1819790643":{"id":"n1819790643","loc":[-85.6247698,41.9568895]},"n1819790650":{"id":"n1819790650","loc":[-85.6216412,41.9550149]},"n1819790652":{"id":"n1819790652","loc":[-85.6224952,41.9603918]},"n1819790656":{"id":"n1819790656","loc":[-85.61918,41.9555649]},"n1819790661":{"id":"n1819790661","loc":[-85.6200169,41.955505]},"n1819790662":{"id":"n1819790662","loc":[-85.6217389,41.9563149]},"n1819790666":{"id":"n1819790666","loc":[-85.6229566,41.9598373]},"n1819790667":{"id":"n1819790667","loc":[-85.6209117,41.9609189]},"n1819790669":{"id":"n1819790669","loc":[-85.6252311,41.9562353]},"n1819790670":{"id":"n1819790670","loc":[-85.6209758,41.961868]},"n1819790672":{"id":"n1819790672","loc":[-85.6209557,41.9589078]},"n1819790673":{"id":"n1819790673","loc":[-85.6190352,41.9561393]},"n1819790675":{"id":"n1819790675","loc":[-85.6236432,41.9586685]},"n1819790676":{"id":"n1819790676","loc":[-85.6194901,41.9565389]},"n1819790678":{"id":"n1819790678","loc":[-85.6219266,41.9582417]},"n1819790680":{"id":"n1819790680","loc":[-85.6208258,41.9557211]},"n1819790681":{"id":"n1819790681","loc":[-85.6212024,41.9613212]},"n1819790682":{"id":"n1819790682","loc":[-85.624877,41.9559401]},"n1819790684":{"id":"n1819790684","loc":[-85.6206499,41.9583693]},"n1819790699":{"id":"n1819790699","loc":[-85.6215243,41.956279]},"n1819790701":{"id":"n1819790701","loc":[-85.6246625,41.9559321]},"n1819790703":{"id":"n1819790703","loc":[-85.6230478,41.9585089]},"n1819790708":{"id":"n1819790708","loc":[-85.6211102,41.9575402]},"n1819790710":{"id":"n1819790710","loc":[-85.6215082,41.9548468]},"n1819790711":{"id":"n1819790711","loc":[-85.6206552,41.9586007]},"n1819790713":{"id":"n1819790713","loc":[-85.6215404,41.9549705]},"n1819790715":{"id":"n1819790715","loc":[-85.6216906,41.955521]},"n1819790717":{"id":"n1819790717","loc":[-85.6215404,41.9547391]},"n1819790722":{"id":"n1819790722","loc":[-85.6219964,41.9599131]},"n1819790723":{"id":"n1819790723","loc":[-85.622286,41.9606989]},"n1819790725":{"id":"n1819790725","loc":[-85.6228439,41.9572005]},"n1819790727":{"id":"n1819790727","loc":[-85.6202518,41.9554458]},"n1819790728":{"id":"n1819790728","loc":[-85.623434,41.9575276]},"n1819790729":{"id":"n1819790729","loc":[-85.6234287,41.9568576]},"n1819790732":{"id":"n1819790732","loc":[-85.6229566,41.9571369]},"n1819790733":{"id":"n1819790733","loc":[-85.6225543,41.9590275]},"n1819790734":{"id":"n1819790734","loc":[-85.6232892,41.9583135]},"n1819790736":{"id":"n1819790736","loc":[-85.622977,41.9608551]},"n1819790737":{"id":"n1819790737","loc":[-85.624008,41.9569533]},"n1819790741":{"id":"n1819790741","loc":[-85.6212775,41.9608545]},"n1819790742":{"id":"n1819790742","loc":[-85.6231282,41.9569932]},"n1819790743":{"id":"n1819790743","loc":[-85.6224523,41.9591831]},"n1819790744":{"id":"n1819790744","loc":[-85.6210951,41.9610819]},"n1819790745":{"id":"n1819790745","loc":[-85.6220114,41.960544]},"n1819790755":{"id":"n1819790755","loc":[-85.6216369,41.9553854]},"n1819790757":{"id":"n1819790757","loc":[-85.6209986,41.9592709]},"n1819790758":{"id":"n1819790758","loc":[-85.6200437,41.9563468]},"n1819790764":{"id":"n1819790764","loc":[-85.6219363,41.9596823]},"n1819790765":{"id":"n1819790765","loc":[-85.6237612,41.9568496]},"n1819790769":{"id":"n1819790769","loc":[-85.6212389,41.9593433]},"n1819790771":{"id":"n1819790771","loc":[-85.6210726,41.9560123]},"n1819790772":{"id":"n1819790772","loc":[-85.6212711,41.9561838]},"n1819790776":{"id":"n1819790776","loc":[-85.6234437,41.9577795]},"n1819790777":{"id":"n1819790777","loc":[-85.6212502,41.9618599]},"n1819790783":{"id":"n1819790783","loc":[-85.6216895,41.9610585]},"n1819790784":{"id":"n1819790784","loc":[-85.6200115,41.9556367]},"n1819790785":{"id":"n1819790785","loc":[-85.6210576,41.9573002]},"n1819790786":{"id":"n1819790786","loc":[-85.621138,41.9576632]},"n1819790788":{"id":"n1819790788","loc":[-85.6207733,41.9578946]},"n1819790789":{"id":"n1819790789","loc":[-85.6200705,41.9571566]},"n1819790790":{"id":"n1819790790","loc":[-85.6245337,41.9558443]},"n1819790792":{"id":"n1819790792","loc":[-85.621932,41.9608066]},"n1819790793":{"id":"n1819790793","loc":[-85.6233578,41.9581385]},"n1819790794":{"id":"n1819790794","loc":[-85.6204557,41.9555136]},"n1819790797":{"id":"n1819790797","loc":[-85.6235038,41.9576074]},"n1819790800":{"id":"n1819790800","loc":[-85.6214438,41.9607508]},"n1819790801":{"id":"n1819790801","loc":[-85.623492,41.9602129]},"n1819790802":{"id":"n1819790802","loc":[-85.6216691,41.9546553]},"n1819790803":{"id":"n1819790803","loc":[-85.6231057,41.9586851]},"n1819790804":{"id":"n1819790804","loc":[-85.6209224,41.9578673]},"n1819790813":{"id":"n1819790813","loc":[-85.620092,41.9572962]},"n1819790814":{"id":"n1819790814","loc":[-85.6216691,41.9552218]},"n1819790816":{"id":"n1819790816","loc":[-85.6216144,41.9609668]},"n1819790818":{"id":"n1819790818","loc":[-85.6216906,41.9557324]},"n1819790820":{"id":"n1819790820","loc":[-85.6192069,41.9564186]},"n1819790823":{"id":"n1819790823","loc":[-85.6211155,41.9566027]},"n1819790825":{"id":"n1819790825","loc":[-85.6233106,41.9569294]},"n1819790839":{"id":"n1819790839","loc":[-85.625671,41.9564986]},"n1819790842":{"id":"n1819790842","loc":[-85.6235252,41.9567379]},"n1819790844":{"id":"n1819790844","loc":[-85.6253813,41.9566342]},"n1819790847":{"id":"n1819790847","loc":[-85.6200963,41.9567702]},"n1819790849":{"id":"n1819790849","loc":[-85.6238031,41.9587449]},"n1819790851":{"id":"n1819790851","loc":[-85.6234984,41.9584571]},"n1819790856":{"id":"n1819790856","loc":[-85.6242226,41.9570092]},"n1819790865":{"id":"n1819790865","loc":[-85.6200265,41.9569458]},"n1819790869":{"id":"n1819790869","loc":[-85.6230049,41.9601245]},"n1819790871":{"id":"n1819790871","loc":[-85.6190727,41.9558322]},"n1819790873":{"id":"n1819790873","loc":[-85.6217442,41.9550104]},"n1819790875":{"id":"n1819790875","loc":[-85.6208044,41.9587808]},"n1819790879":{"id":"n1819790879","loc":[-85.6198444,41.9574484]},"n1819790883":{"id":"n1819790883","loc":[-85.623713,41.9588719]},"n1819790885":{"id":"n1819790885","loc":[-85.6223289,41.9605075]},"n1819790889":{"id":"n1819790889","loc":[-85.6208044,41.9562437]},"n1819790893":{"id":"n1819790893","loc":[-85.6218183,41.9559684]},"n1819790906":{"id":"n1819790906","loc":[-85.6214052,41.958697]},"n1819790913":{"id":"n1819790913","loc":[-85.6209981,41.9609957]},"n1819790917":{"id":"n1819790917","loc":[-85.6216208,41.9604436]},"n1819790919":{"id":"n1819790919","loc":[-85.6209406,41.9616373]},"n1819790920":{"id":"n1819790920","loc":[-85.6221948,41.9583334]},"n1819790922":{"id":"n1819790922","loc":[-85.6216681,41.9615292]},"n1819790924":{"id":"n1819790924","loc":[-85.6210147,41.9570489]},"n1819790929":{"id":"n1819790929","loc":[-85.6193678,41.955521]},"w17964707":{"id":"w17964707","tags":{"highway":"residential","name":"11th Ave"},"nodes":["n185960682","n185960684","n185960686","n185960688","n185960690"]},"w201484345":{"id":"w201484345","tags":{"bridge":"yes","highway":"residential","name":"E Hoffman St"},"nodes":["n185978791","n185978795"]},"w201484348":{"id":"w201484348","tags":{"highway":"residential","name":"E Hoffman St"},"nodes":["n185978795","n185978800","n185978803","n185978806","n185978808","n185978810","n185978811","n185978813","n185955747","n185978814","n185972054","n185978817"]},"w170843845":{"id":"w170843845","tags":{"landuse":"reservoir","name":"Hoffman Pond","natural":"water"},"nodes":["n1819790732","n1819790742","n1819790825","n1819790729","n1819790842","n1819790765","n1819790737","n1819790856","n1819790643","n1819790596","n1819790844","n1819790839","n1819849190","n1819790619","n1819790613","n1819790669","n1819790550","n1819790682","n1819790701","n1819790790","n1819790532","n1819790887","n1819790740","n1819790831","n1819790545","n1819790664","n1819790621","n1819790861","n1819790730","n1819790683","n1819790802","n1819790717","n1819790710","n1819790713","n1819790650","n1819790873","n1819790814","n1819790755","n1819790715","n1819790818","n1819790602","n1819790893","n1819790662","n1819790699","n1819790772","n1819790771","n1819790680","n1819790794","n1819790727","n1819790628","n1819790661","n1819790784","n1819790610","n1819790626","n1819790929","n1819790656","n1819790871","n1819790673","n1819790566","n1819790820","n1819790676","n1819790598","n1819790556","n1819790558","n1819790879","n1819790608","n1819790813","n1819790789","n1819790865","n1819790847","n1819790758","n1819790548","n1819790539","n1819790889","n1819790557","n1819790586","n1819790571","n1819790823","n1819790546","n1819790924","n1819790785","n1819790708","n1819790786","n1819790561","n1819790804","n1819790788","n1819790684","n1819790711","n1819790875","n1819790672","n1819790569","n1819790757","n1819790769","n1819790603","n1819790906","n1819790553","n1819790536","n1819790678","n1819790920","n1819790600","n1819790733","n1819790743","n1819790567","n1819790764","n1819790722","n1819790638","n1819790917","n1819790800","n1819790741","n1819790667","n1819790913","n1819790744","n1819790816","n1819790591","n1819790745","n1819790885","n1819790652","n1819790588","n1819790617","n1819790551","n1819790666","n1819790869","n1819790630","n1819790723","n1819790792","n1819790783","n1819790681","n1819790919","n1819790593","n1819790670","n1819790777","n1819790922","n1819790562","n1819790736","n1819790565","n1819790801","n1819790538","n1819790640","n1819790616","n1819790572","n1819790620","n1819790883","n1819790849","n1819790675","n1819790851","n1819790803","n1819790611","n1819790703","n1819790734","n1819790793","n1819790614","n1819790581","n1819790776","n1819790797","n1819790728","n1819790584","n1819790624","n1819790604","n1819790725","n1819790732"]},"w206805240":{"id":"w206805240","tags":{"waterway":"river"},"nodes":["n2168544738","n2168544739","n2168544740","n2168544741","n2168544742","n2168544743","n2168544744","n2168544745","n2168544746","n2168544747","n2168544748","n2168544749","n2168544750","n2168544751","n2168544752","n2168544753","n1819848944"]},"n394490429":{"id":"n394490429","loc":[-85.643883,41.954365]},"n185953421":{"id":"n185953421","loc":[-85.641876,41.954946]},"n185953417":{"id":"n185953417","loc":[-85.6418306,41.9551597]},"n185977233":{"id":"n185977233","loc":[-85.642987,41.95486]},"n185977232":{"id":"n185977232","loc":[-85.642894,41.9547842]},"n1475293244":{"id":"n1475293244","loc":[-85.63974,41.9521543]},"n1819848890":{"id":"n1819848890","loc":[-85.6410004,41.9552822]},"n1819848965":{"id":"n1819848965","loc":[-85.6409795,41.9553892]},"n2189015846":{"id":"n2189015846","loc":[-85.6420457,41.9549528]},"n2189015849":{"id":"n2189015849","loc":[-85.6425867,41.9551392]},"n2189015852":{"id":"n2189015852","loc":[-85.6426877,41.9549771]},"n2199109816":{"id":"n2199109816","loc":[-85.6399215,41.9540925]},"n2199109818":{"id":"n2199109818","loc":[-85.6399182,41.9538236]},"n2199109820":{"id":"n2199109820","loc":[-85.6402201,41.9538216]},"n2199109822":{"id":"n2199109822","loc":[-85.640222,41.9539771]},"n2199109825":{"id":"n2199109825","loc":[-85.6402904,41.9539766]},"n2199109827":{"id":"n2199109827","loc":[-85.6402918,41.95409]},"n2199109829":{"id":"n2199109829","loc":[-85.6395845,41.9544626]},"n2199109831":{"id":"n2199109831","loc":[-85.6395792,41.9540671]},"n2199109833":{"id":"n2199109833","loc":[-85.6397173,41.9540661]},"n2199109835":{"id":"n2199109835","loc":[-85.6397226,41.9544616]},"n2199109837":{"id":"n2199109837","loc":[-85.6399641,41.9545058]},"n2199109839":{"id":"n2199109839","loc":[-85.6399637,41.9541859]},"n2199109841":{"id":"n2199109841","loc":[-85.6401098,41.9541858]},"n2199109843":{"id":"n2199109843","loc":[-85.64011,41.9543272]},"n2199109845":{"id":"n2199109845","loc":[-85.6400783,41.9543273]},"n2199109847":{"id":"n2199109847","loc":[-85.6400785,41.9545058]},"n2199109853":{"id":"n2199109853","loc":[-85.6396184,41.9554049]},"n2199109855":{"id":"n2199109855","loc":[-85.6396825,41.9553713]},"n185949745":{"id":"n185949745","loc":[-85.6442727,41.9553112]},"n185949748":{"id":"n185949748","loc":[-85.6448804,41.9555238]},"n185949755":{"id":"n185949755","loc":[-85.6420011,41.9603536]},"n185949763":{"id":"n185949763","loc":[-85.6408843,41.9555822]},"n185949765":{"id":"n185949765","loc":[-85.6414548,41.9557751]},"n185952158":{"id":"n185952158","loc":[-85.640066,41.956854]},"n185952160":{"id":"n185952160","loc":[-85.639848,41.957229]},"n185952161":{"id":"n185952161","loc":[-85.6396089,41.9576192]},"n185952163":{"id":"n185952163","loc":[-85.63892,41.957957]},"n185953413":{"id":"n185953413","loc":[-85.64162,41.955475]},"n185971651":{"id":"n185971651","loc":[-85.6440766,41.9543462]},"n185977234":{"id":"n185977234","loc":[-85.645044,41.955581]},"n394490395":{"id":"n394490395","loc":[-85.657336,41.936762]},"n394490396":{"id":"n394490396","loc":[-85.653896,41.936978]},"n394490397":{"id":"n394490397","loc":[-85.653732,41.937386]},"n394490398":{"id":"n394490398","loc":[-85.65182,41.937378]},"n394490399":{"id":"n394490399","loc":[-85.651843,41.938445]},"n394490400":{"id":"n394490400","loc":[-85.652536,41.938447]},"n394490401":{"id":"n394490401","loc":[-85.652533,41.938901]},"n394490402":{"id":"n394490402","loc":[-85.652084,41.9389]},"n394490403":{"id":"n394490403","loc":[-85.6521,41.939627]},"n394490404":{"id":"n394490404","loc":[-85.652301,41.939628]},"n394490405":{"id":"n394490405","loc":[-85.652302,41.939755]},"n394490406":{"id":"n394490406","loc":[-85.652783,41.939747]},"n394490407":{"id":"n394490407","loc":[-85.652835,41.94112]},"n394490408":{"id":"n394490408","loc":[-85.651968,41.941123]},"n394490409":{"id":"n394490409","loc":[-85.651983,41.941969]},"n394490410":{"id":"n394490410","loc":[-85.652908,41.941961]},"n394490411":{"id":"n394490411","loc":[-85.65292,41.94278]},"n394490412":{"id":"n394490412","loc":[-85.651698,41.942816]},"n394490413":{"id":"n394490413","loc":[-85.651509,41.942823]},"n394490414":{"id":"n394490414","loc":[-85.651272,41.942837]},"n394490415":{"id":"n394490415","loc":[-85.651272,41.943325]},"n394490416":{"id":"n394490416","loc":[-85.65122,41.944053]},"n394490417":{"id":"n394490417","loc":[-85.651193,41.944449]},"n394490418":{"id":"n394490418","loc":[-85.651088,41.944969]},"n394490419":{"id":"n394490419","loc":[-85.650949,41.945554]},"n394490420":{"id":"n394490420","loc":[-85.650907,41.945719]},"n394490421":{"id":"n394490421","loc":[-85.650808,41.946016]},"n394490422":{"id":"n394490422","loc":[-85.650712,41.946516]},"n394490423":{"id":"n394490423","loc":[-85.650493,41.947166]},"n394490424":{"id":"n394490424","loc":[-85.650626,41.947213]},"n394490425":{"id":"n394490425","loc":[-85.650201,41.948109]},"n394490426":{"id":"n394490426","loc":[-85.649868,41.948797]},"n394490427":{"id":"n394490427","loc":[-85.649669,41.949161]},"n394490428":{"id":"n394490428","loc":[-85.64659,41.954067]},"n394490430":{"id":"n394490430","loc":[-85.644034,41.95444]},"n394490431":{"id":"n394490431","loc":[-85.644248,41.954507]},"n394490432":{"id":"n394490432","loc":[-85.64491,41.954481]},"n394490433":{"id":"n394490433","loc":[-85.645213,41.954433]},"n394490434":{"id":"n394490434","loc":[-85.645426,41.954477]},"n394490435":{"id":"n394490435","loc":[-85.6458,41.954704]},"n394490436":{"id":"n394490436","loc":[-85.64605,41.954804]},"n394490437":{"id":"n394490437","loc":[-85.646125,41.954817]},"n394490438":{"id":"n394490438","loc":[-85.646002,41.954997]},"n394490439":{"id":"n394490439","loc":[-85.645764,41.955366]},"n394490440":{"id":"n394490440","loc":[-85.645525,41.955734]},"n394490441":{"id":"n394490441","loc":[-85.64443,41.957424]},"n394490442":{"id":"n394490442","loc":[-85.641712,41.961723]},"n394490443":{"id":"n394490443","loc":[-85.640747,41.963246]},"n394490444":{"id":"n394490444","loc":[-85.637803,41.967894]},"n394490445":{"id":"n394490445","loc":[-85.637673,41.967861]},"n394490446":{"id":"n394490446","loc":[-85.636637,41.969275]},"n394490447":{"id":"n394490447","loc":[-85.634923,41.969269]},"n394490448":{"id":"n394490448","loc":[-85.634893,41.968537]},"n394490449":{"id":"n394490449","loc":[-85.634544,41.96927]},"n394490450":{"id":"n394490450","loc":[-85.630835,41.969274]},"n394490451":{"id":"n394490451","loc":[-85.630834,41.968348]},"n394490452":{"id":"n394490452","loc":[-85.630857,41.968179]},"n394490453":{"id":"n394490453","loc":[-85.630924,41.968044]},"n394490454":{"id":"n394490454","loc":[-85.631004,41.967925]},"n394490455":{"id":"n394490455","loc":[-85.631143,41.967811]},"n394490456":{"id":"n394490456","loc":[-85.631311,41.967736]},"n394490457":{"id":"n394490457","loc":[-85.631595,41.967693]},"n394490458":{"id":"n394490458","loc":[-85.63325,41.967702]},"n394490459":{"id":"n394490459","loc":[-85.633247,41.967021]},"n394490460":{"id":"n394490460","loc":[-85.634858,41.967021]},"n394490461":{"id":"n394490461","loc":[-85.634865,41.967711]},"n394490462":{"id":"n394490462","loc":[-85.634884,41.968231]},"n394490463":{"id":"n394490463","loc":[-85.636559,41.963867]},"n394490464":{"id":"n394490464","loc":[-85.634832,41.963866]},"n394490465":{"id":"n394490465","loc":[-85.63481,41.961899]},"n394490466":{"id":"n394490466","loc":[-85.637219,41.961842]},"n394490467":{"id":"n394490467","loc":[-85.637837,41.960019]},"n394490468":{"id":"n394490468","loc":[-85.637459,41.960022]},"n394490469":{"id":"n394490469","loc":[-85.635295,41.959987]},"n394490470":{"id":"n394490470","loc":[-85.634783,41.959979]},"n394490471":{"id":"n394490471","loc":[-85.634776,41.959834]},"n394490472":{"id":"n394490472","loc":[-85.634767,41.959009]},"n394490473":{"id":"n394490473","loc":[-85.634763,41.958292]},"n394490474":{"id":"n394490474","loc":[-85.633346,41.958287]},"n394490475":{"id":"n394490475","loc":[-85.632128,41.9583]},"n394490476":{"id":"n394490476","loc":[-85.631414,41.958318]},"n394490477":{"id":"n394490477","loc":[-85.63137,41.959033]},"n394490478":{"id":"n394490478","loc":[-85.631325,41.959753]},"n394490479":{"id":"n394490479","loc":[-85.631494,41.95977]},"n394490480":{"id":"n394490480","loc":[-85.631456,41.960673]},"n394490481":{"id":"n394490481","loc":[-85.631421,41.961494]},"n394490482":{"id":"n394490482","loc":[-85.631404,41.961887]},"n394490483":{"id":"n394490483","loc":[-85.631401,41.961968]},"n394490484":{"id":"n394490484","loc":[-85.630962,41.961967]},"n394490485":{"id":"n394490485","loc":[-85.6299,41.961973]},"n394490486":{"id":"n394490486","loc":[-85.624929,41.962002]},"n394490487":{"id":"n394490487","loc":[-85.623333,41.961987]},"n394490488":{"id":"n394490488","loc":[-85.621894,41.963956]},"n394490489":{"id":"n394490489","loc":[-85.62131,41.963727]},"n394490490":{"id":"n394490490","loc":[-85.621216,41.963868]},"n394490491":{"id":"n394490491","loc":[-85.620356,41.965119]},"n394490492":{"id":"n394490492","loc":[-85.620848,41.965341]},"n394490493":{"id":"n394490493","loc":[-85.620684,41.965558]},"n394490494":{"id":"n394490494","loc":[-85.620621,41.965658]},"n394490495":{"id":"n394490495","loc":[-85.618165,41.965759]},"n394490496":{"id":"n394490496","loc":[-85.618071,41.965759]},"n394490497":{"id":"n394490497","loc":[-85.617986,41.965759]},"n394490498":{"id":"n394490498","loc":[-85.605673,41.965764]},"n394490499":{"id":"n394490499","loc":[-85.605668,41.963548]},"n394490500":{"id":"n394490500","loc":[-85.605664,41.962094]},"n394490501":{"id":"n394490501","loc":[-85.595828,41.962159]},"n394490502":{"id":"n394490502","loc":[-85.587869,41.962169]},"n394490503":{"id":"n394490503","loc":[-85.586289,41.962179]},"n394490504":{"id":"n394490504","loc":[-85.583774,41.962178]},"n394490505":{"id":"n394490505","loc":[-85.583774,41.961789]},"n394490506":{"id":"n394490506","loc":[-85.581303,41.961783]},"n394490507":{"id":"n394490507","loc":[-85.581304,41.961616]},"n394490508":{"id":"n394490508","loc":[-85.581292,41.961616]},"n394490509":{"id":"n394490509","loc":[-85.581247,41.959244]},"n394490510":{"id":"n394490510","loc":[-85.581245,41.958394]},"n394490511":{"id":"n394490511","loc":[-85.581276,41.958372]},"n394490512":{"id":"n394490512","loc":[-85.581302,41.958353]},"n394490513":{"id":"n394490513","loc":[-85.581376,41.9583]},"n394490514":{"id":"n394490514","loc":[-85.582256,41.957663]},"n394490515":{"id":"n394490515","loc":[-85.585299,41.955483]},"n394490516":{"id":"n394490516","loc":[-85.585588,41.955331]},"n394490517":{"id":"n394490517","loc":[-85.586053,41.955163]},"n394490518":{"id":"n394490518","loc":[-85.58632,41.955076]},"n394490519":{"id":"n394490519","loc":[-85.586478,41.955025]},"n394490520":{"id":"n394490520","loc":[-85.58692,41.954947]},"n394490521":{"id":"n394490521","loc":[-85.587327,41.954914]},"n394490522":{"id":"n394490522","loc":[-85.587345,41.954913]},"n394490523":{"id":"n394490523","loc":[-85.587358,41.954913]},"n394490524":{"id":"n394490524","loc":[-85.58963,41.954877]},"n394490525":{"id":"n394490525","loc":[-85.591077,41.954865]},"n394490526":{"id":"n394490526","loc":[-85.594824,41.954843]},"n394490527":{"id":"n394490527","loc":[-85.594804,41.95331]},"n394490528":{"id":"n394490528","loc":[-85.599336,41.95331]},"n394490529":{"id":"n394490529","loc":[-85.599336,41.954825]},"n394490530":{"id":"n394490530","loc":[-85.597828,41.954839]},"n394490531":{"id":"n394490531","loc":[-85.597833,41.95614]},"n394490532":{"id":"n394490532","loc":[-85.596586,41.956151]},"n394490533":{"id":"n394490533","loc":[-85.596586,41.956394]},"n394490534":{"id":"n394490534","loc":[-85.595933,41.956394]},"n394490535":{"id":"n394490535","loc":[-85.595933,41.958176]},"n394490536":{"id":"n394490536","loc":[-85.597635,41.958179]},"n394490537":{"id":"n394490537","loc":[-85.597717,41.958177]},"n394490538":{"id":"n394490538","loc":[-85.601671,41.958194]},"n394490539":{"id":"n394490539","loc":[-85.605619,41.958194]},"n394490540":{"id":"n394490540","loc":[-85.608054,41.958187]},"n394490542":{"id":"n394490542","loc":[-85.6080762,41.9547864]},"n394490545":{"id":"n394490545","loc":[-85.6104354,41.9548263]},"n394490546":{"id":"n394490546","loc":[-85.610274,41.951106]},"n394490547":{"id":"n394490547","loc":[-85.610278,41.950829]},"n394490548":{"id":"n394490548","loc":[-85.610309,41.948377]},"n394490549":{"id":"n394490549","loc":[-85.610314,41.947986]},"n394490550":{"id":"n394490550","loc":[-85.610464,41.947985]},"n394490551":{"id":"n394490551","loc":[-85.610447,41.947468]},"n394490552":{"id":"n394490552","loc":[-85.612469,41.947471]},"n394490553":{"id":"n394490553","loc":[-85.612494,41.945576]},"n394490554":{"id":"n394490554","loc":[-85.610292,41.94558]},"n394490555":{"id":"n394490555","loc":[-85.608412,41.945625]},"n394490556":{"id":"n394490556","loc":[-85.608412,41.943036]},"n394490557":{"id":"n394490557","loc":[-85.608702,41.943087]},"n394490558":{"id":"n394490558","loc":[-85.609196,41.943224]},"n394490559":{"id":"n394490559","loc":[-85.609571,41.943263]},"n394490560":{"id":"n394490560","loc":[-85.610116,41.943295]},"n394490561":{"id":"n394490561","loc":[-85.610273,41.943275]},"n394490562":{"id":"n394490562","loc":[-85.611339,41.943075]},"n394490563":{"id":"n394490563","loc":[-85.611575,41.942997]},"n394490564":{"id":"n394490564","loc":[-85.611847,41.942849]},"n394490565":{"id":"n394490565","loc":[-85.612164,41.942568]},"n394490566":{"id":"n394490566","loc":[-85.612341,41.942529]},"n394490567":{"id":"n394490567","loc":[-85.612562,41.942524]},"n394490568":{"id":"n394490568","loc":[-85.612768,41.942546]},"n394490569":{"id":"n394490569","loc":[-85.612938,41.942633]},"n394490570":{"id":"n394490570","loc":[-85.6131,41.942782]},"n394490571":{"id":"n394490571","loc":[-85.613299,41.942919]},"n394490572":{"id":"n394490572","loc":[-85.613498,41.942996]},"n394490573":{"id":"n394490573","loc":[-85.614698,41.942842]},"n394490574":{"id":"n394490574","loc":[-85.615288,41.942698]},"n394490575":{"id":"n394490575","loc":[-85.616054,41.942693]},"n394490576":{"id":"n394490576","loc":[-85.61603,41.942175]},"n394490577":{"id":"n394490577","loc":[-85.616004,41.941741]},"n394490578":{"id":"n394490578","loc":[-85.615994,41.940156]},"n394490579":{"id":"n394490579","loc":[-85.615144,41.940159]},"n394490580":{"id":"n394490580","loc":[-85.614915,41.940161]},"n394490582":{"id":"n394490582","loc":[-85.614875,41.938532]},"n394490583":{"id":"n394490583","loc":[-85.616167,41.938787]},"n394490585":{"id":"n394490585","loc":[-85.616176,41.938589]},"n394490586":{"id":"n394490586","loc":[-85.614537,41.938282]},"n394490588":{"id":"n394490588","loc":[-85.610141,41.937459]},"n394490589":{"id":"n394490589","loc":[-85.610172,41.937298]},"n394490590":{"id":"n394490590","loc":[-85.609918,41.935495]},"n394490592":{"id":"n394490592","loc":[-85.610092,41.935451]},"n394490594":{"id":"n394490594","loc":[-85.610681,41.935247]},"n394490595":{"id":"n394490595","loc":[-85.611446,41.934955]},"n394490596":{"id":"n394490596","loc":[-85.612057,41.934696]},"n394490598":{"id":"n394490598","loc":[-85.613256,41.934084]},"n394490599":{"id":"n394490599","loc":[-85.613948,41.933682]},"n394490601":{"id":"n394490601","loc":[-85.61436,41.933417]},"n394490602":{"id":"n394490602","loc":[-85.614638,41.933212]},"n394490604":{"id":"n394490604","loc":[-85.615249,41.9332]},"n394490605":{"id":"n394490605","loc":[-85.618218,41.933223]},"n394490607":{"id":"n394490607","loc":[-85.618241,41.933479]},"n394490608":{"id":"n394490608","loc":[-85.618257,41.93365]},"n394490609":{"id":"n394490609","loc":[-85.618298,41.935067]},"n394490611":{"id":"n394490611","loc":[-85.619791,41.935067]},"n394490612":{"id":"n394490612","loc":[-85.619794,41.933301]},"n394490613":{"id":"n394490613","loc":[-85.619795,41.932692]},"n394490614":{"id":"n394490614","loc":[-85.619729,41.929517]},"n394490615":{"id":"n394490615","loc":[-85.619801,41.929305]},"n394490616":{"id":"n394490616","loc":[-85.619809,41.927391]},"n394490617":{"id":"n394490617","loc":[-85.620883,41.927378]},"n394490618":{"id":"n394490618","loc":[-85.620988,41.927368]},"n394490619":{"id":"n394490619","loc":[-85.621076,41.927368]},"n394490620":{"id":"n394490620","loc":[-85.621156,41.927376]},"n394490621":{"id":"n394490621","loc":[-85.621685,41.92737]},"n394490622":{"id":"n394490622","loc":[-85.624716,41.927359]},"n394490623":{"id":"n394490623","loc":[-85.625308,41.92737]},"n394490624":{"id":"n394490624","loc":[-85.625655,41.927377]},"n394490625":{"id":"n394490625","loc":[-85.625093,41.925591]},"n394490626":{"id":"n394490626","loc":[-85.625174,41.92559]},"n394490627":{"id":"n394490627","loc":[-85.625249,41.925597]},"n394490628":{"id":"n394490628","loc":[-85.625532,41.925604]},"n394490629":{"id":"n394490629","loc":[-85.625761,41.925597]},"n394490630":{"id":"n394490630","loc":[-85.625955,41.926153]},"n394490631":{"id":"n394490631","loc":[-85.626209,41.926155]},"n394490632":{"id":"n394490632","loc":[-85.627757,41.926151]},"n394490633":{"id":"n394490633","loc":[-85.627825,41.926298]},"n394490634":{"id":"n394490634","loc":[-85.627994,41.926315]},"n394490635":{"id":"n394490635","loc":[-85.628049,41.927196]},"n394490636":{"id":"n394490636","loc":[-85.62949,41.927221]},"n394490637":{"id":"n394490637","loc":[-85.629602,41.927277]},"n394490638":{"id":"n394490638","loc":[-85.6297102,41.9273279]},"n394490639":{"id":"n394490639","loc":[-85.630958,41.927398]},"n394490699":{"id":"n394490699","loc":[-85.632741,41.927388]},"n394490700":{"id":"n394490700","loc":[-85.632997,41.927391]},"n394490701":{"id":"n394490701","loc":[-85.633149,41.927393]},"n394490702":{"id":"n394490702","loc":[-85.633334,41.927393]},"n394490703":{"id":"n394490703","loc":[-85.633468,41.927561]},"n394490704":{"id":"n394490704","loc":[-85.633563,41.927755]},"n394490705":{"id":"n394490705","loc":[-85.633662,41.928192]},"n394490706":{"id":"n394490706","loc":[-85.633679,41.928807]},"n394490707":{"id":"n394490707","loc":[-85.633687,41.929107]},"n394490708":{"id":"n394490708","loc":[-85.633927,41.929109]},"n394490709":{"id":"n394490709","loc":[-85.634126,41.929111]},"n394490710":{"id":"n394490710","loc":[-85.634207,41.92911]},"n394490711":{"id":"n394490711","loc":[-85.634323,41.929111]},"n394490712":{"id":"n394490712","loc":[-85.636712,41.929128]},"n394490713":{"id":"n394490713","loc":[-85.63808,41.9291]},"n394490714":{"id":"n394490714","loc":[-85.639213,41.929088]},"n394490715":{"id":"n394490715","loc":[-85.639189,41.92852]},"n394490716":{"id":"n394490716","loc":[-85.639204,41.925488]},"n394490717":{"id":"n394490717","loc":[-85.644204,41.925452]},"n394490718":{"id":"n394490718","loc":[-85.651425,41.925406]},"n394490719":{"id":"n394490719","loc":[-85.651449,41.926321]},"n394490720":{"id":"n394490720","loc":[-85.651451,41.926969]},"n394490721":{"id":"n394490721","loc":[-85.651458,41.928052]},"n394490722":{"id":"n394490722","loc":[-85.651446,41.928892]},"n394490723":{"id":"n394490723","loc":[-85.651456,41.929447]},"n394490724":{"id":"n394490724","loc":[-85.651707,41.929454]},"n394490725":{"id":"n394490725","loc":[-85.652369,41.929473]},"n394490726":{"id":"n394490726","loc":[-85.6525,41.929452]},"n394490727":{"id":"n394490727","loc":[-85.654066,41.92946]},"n394490728":{"id":"n394490728","loc":[-85.654816,41.92946]},"n394490729":{"id":"n394490729","loc":[-85.654816,41.930337]},"n394490730":{"id":"n394490730","loc":[-85.654587,41.930337]},"n394490731":{"id":"n394490731","loc":[-85.654548,41.931072]},"n394490732":{"id":"n394490732","loc":[-85.654538,41.931701]},"n394490733":{"id":"n394490733","loc":[-85.654898,41.931689]},"n394490734":{"id":"n394490734","loc":[-85.654898,41.932505]},"n394490735":{"id":"n394490735","loc":[-85.654854,41.932514]},"n394490736":{"id":"n394490736","loc":[-85.655497,41.932499]},"n394490737":{"id":"n394490737","loc":[-85.656405,41.932493]},"n394490738":{"id":"n394490738","loc":[-85.656422,41.933416]},"n394490739":{"id":"n394490739","loc":[-85.657322,41.933438]},"n1475293233":{"id":"n1475293233","loc":[-85.6385522,41.9585167]},"n1475293242":{"id":"n1475293242","loc":[-85.64609,41.9540815]},"n1475293249":{"id":"n1475293249","loc":[-85.6358079,41.9692721]},"n1475293256":{"id":"n1475293256","loc":[-85.6387369,41.9581583]},"n1475293259":{"id":"n1475293259","loc":[-85.6455882,41.9541138]},"n1475293266":{"id":"n1475293266","loc":[-85.6451008,41.9541821]},"n1819800253":{"id":"n1819800253","loc":[-85.6134286,41.9429692]},"n2114807558":{"id":"n2114807558","loc":[-85.6365609,41.963866],"tags":{"railway":"level_crossing"}},"n2189015728":{"id":"n2189015728","loc":[-85.6383956,41.9590576]},"n2189015838":{"id":"n2189015838","loc":[-85.6435144,41.9563705]},"n2189015842":{"id":"n2189015842","loc":[-85.6415782,41.9557035]},"n2189015855":{"id":"n2189015855","loc":[-85.6440829,41.9554577]},"n2199109849":{"id":"n2199109849","loc":[-85.6393434,41.9565591]},"n2199109851":{"id":"n2199109851","loc":[-85.6393208,41.9565002]},"n2199109857":{"id":"n2199109857","loc":[-85.6401986,41.955545]},"n2199109859":{"id":"n2199109859","loc":[-85.6402362,41.955587]},"n2199109861":{"id":"n2199109861","loc":[-85.6395958,41.9565675]},"n2199109863":{"id":"n2199109863","loc":[-85.639528,41.9566011]},"w209717053":{"id":"w209717053","tags":{"area":"yes","building":"yes"},"nodes":["n2199109829","n2199109831","n2199109833","n2199109835","n2199109829"]},"w17966415":{"id":"w17966415","tags":{"access":"private","highway":"service","name":"Manufacturing Way"},"nodes":["n185971642","n185977232","n185977233","n185949745","n185949748","n185977234"]},"w209717054":{"id":"w209717054","tags":{"area":"yes","building":"yes"},"nodes":["n2199109837","n2199109839","n2199109841","n2199109843","n2199109845","n2199109847","n2199109837"]},"w208627214":{"id":"w208627214","tags":{"highway":"service"},"nodes":["n185949755","n2189015728","n1475293233","n1475293256","n185952163","n185952161","n185952160","n185952158","n185949763","n1819848965","n1819848890","n185952156"]},"w17963817":{"id":"w17963817","tags":{"access":"private","highway":"service"},"nodes":["n185949765","n185953413","n185953417","n185953421","n185953423"]},"w34369809":{"id":"w34369809","tags":{"admin_level":"8","boundary":"administrative","landuse":"residential"},"nodes":["n394490395","n394490396","n394490397","n394490398","n394490399","n394490400","n394490401","n394490402","n394490403","n394490404","n394490405","n394490406","n394490407","n394490408","n394490409","n394490410","n394490411","n394490412","n394490413","n394490414","n394490415","n394490416","n394490417","n394490418","n394490419","n394490420","n394490421","n394490422","n394490423","n394490424","n394490425","n394490426","n394490427","n394490428","n1475293242","n1475293259","n1475293266","n394490429","n394490430","n394490431","n394490432","n394490433","n394490434","n394490435","n394490436","n394490437","n394490438","n394490439","n394490440","n394490441","n394490442","n394490443","n394490444","n394490445","n394490446","n1475293249","n394490447","n394490448","n394490449","n394490450","n394490451","n394490452","n394490453","n394490454","n394490455","n394490456","n394490457","n394490458","n394490459","n394490460","n394490461","n394490462","n2114807558","n394490463","n1475293226","n394490464","n394490465","n394490466","n394490467","n394490468","n394490469","n394490470","n394490471","n394490472","n394490473","n394490474","n394490475","n394490476","n394490477","n394490478","n394490479","n394490480","n394490481","n394490482","n394490483","n394490484","n394490485","n394490486","n394490487","n394490488","n394490489","n394490490","n394490491","n394490492","n394490493","n394490494","n394490495","n394490496","n394490497","n394490498","n394490499","n394490500","n394490501","n394490502","n394490503","n394490504","n394490505","n394490506","n394490507","n394490508","n394490509","n394490510","n394490511","n394490512","n394490513","n394490514","n394490515","n394490516","n394490517","n394490518","n394490519","n394490520","n394490521","n394490522","n394490523","n394490524","n394490525","n394490526","n394490527","n394490528","n394490529","n394490530","n394490531","n394490532","n394490533","n394490534","n394490535","n394490536","n394490537","n394490538","n394490539","n394490540","n394490542","n394490545","n394490546","n394490547","n394490548","n394490549","n394490550","n394490551","n394490552","n394490553","n394490554","n394490555","n394490556","n394490557","n394490558","n394490559","n394490560","n394490561","n394490562","n394490563","n394490564","n394490565","n394490566","n394490567","n394490568","n394490569","n394490570","n394490571","n1819800253","n394490572","n394490573","n394490574","n394490575","n394490576","n394490577","n394490578","n394490579","n394490580","n394490582","n394490583","n394490585","n394490586","n394490588","n394490589","n394490590","n394490592","n394490594","n394490595","n394490596","n394490598","n394490599","n394490601","n394490602","n394490604","n394490605","n394490607","n394490608","n394490609","n394490611","n394490612","n394490613","n394490614","n394490615","n394490616","n394490617","n394490618","n394490619","n394490620","n394490621","n394490622","n394490623","n394490624","n394490625","n394490626","n394490627","n394490628","n394490629","n394490630","n394490631","n394490632","n394490633","n394490634","n394490635","n394490636","n394490637","n394490638","n394490639","n394490699","n394490700","n394490701","n394490702","n394490703","n394490704","n394490705","n394490706","n394490707","n394490708","n394490709","n394490710","n394490711","n394490712","n394490713","n394490714","n394490715","n394490716","n394490717","n394490718","n394490719","n394490720","n394490721","n394490722","n394490723","n394490724","n394490725","n394490726","n394490727","n394490728","n394490729","n394490730","n394490731","n394490732","n394490733","n394490734","n394490735","n394490736","n394490737","n394490738","n394490739","n394490395"]},"w208627221":{"id":"w208627221","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189015838","n2189015842","n2189015846","n2189015849","n2189015852","n2189015855","n2189015838"]},"w209717052":{"id":"w209717052","tags":{"area":"yes","building":"yes"},"nodes":["n2199109816","n2199109818","n2199109820","n2199109822","n2199109825","n2199109827","n2199109816"]},"w134151784":{"id":"w134151784","tags":{"bridge":"yes","highway":"residential","name":"W Hoffman St"},"nodes":["n185971648","n185971651"]},"w209717055":{"id":"w209717055","tags":{"area":"yes","landuse":"basin"},"nodes":["n2199109849","n2199109851","n2199109853","n2199109855","n2199109857","n2199109859","n2199109861","n2199109863","n2199109849"]},"w17967763":{"id":"w17967763","tags":{"highway":"residential","name":"Rock River Ave"},"nodes":["n1475293244","n185982166","n185975067","n185971637"]},"r134949":{"id":"r134949","tags":{"admin_level":"8","border_type":"city","boundary":"administrative","name":"Three Rivers","place":"city","type":"boundary"},"members":[{"id":"w34369809","type":"way","role":"outer"},{"id":"w34369821","type":"way","role":"outer"},{"id":"w34369822","type":"way","role":"outer"},{"id":"w34369823","type":"way","role":"outer"},{"id":"w34369824","type":"way","role":"outer"},{"id":"w34369825","type":"way","role":"outer"},{"id":"w34369826","type":"way","role":"outer"},{"id":"w34369810","type":"way","role":"inner"},{"id":"w34369811","type":"way","role":"inner"},{"id":"w34369812","type":"way","role":"inner"},{"id":"w34367079","type":"way","role":"inner"},{"id":"w34369814","type":"way","role":"inner"},{"id":"w34367080","type":"way","role":"inner"},{"id":"w34369815","type":"way","role":"inner"},{"id":"w34369820","type":"way","role":"inner"}]},"n1819848881":{"id":"n1819848881","loc":[-85.638562,41.9569965]},"n1819848947":{"id":"n1819848947","loc":[-85.6384348,41.9576565]},"n1819849044":{"id":"n1819849044","loc":[-85.6385749,41.9573345]},"n2114807547":{"id":"n2114807547","loc":[-85.6384626,41.9583756]},"n2114807564":{"id":"n2114807564","loc":[-85.638535,41.9581283]},"n2189015691":{"id":"n2189015691","loc":[-85.6435584,41.9565243]},"n2189015696":{"id":"n2189015696","loc":[-85.6435805,41.9566049]},"n2189015722":{"id":"n2189015722","loc":[-85.6435035,41.9567438]},"n2189015744":{"id":"n2189015744","loc":[-85.6437991,41.9569582]},"n2189015747":{"id":"n2189015747","loc":[-85.6433042,41.9567742]},"n2189015750":{"id":"n2189015750","loc":[-85.6433827,41.9566844]},"n2189015753":{"id":"n2189015753","loc":[-85.6430447,41.9565588]},"n2189015756":{"id":"n2189015756","loc":[-85.6431111,41.956451]},"n2189015759":{"id":"n2189015759","loc":[-85.6420247,41.956083]},"n2189015760":{"id":"n2189015760","loc":[-85.6419945,41.9561369]},"n2189015764":{"id":"n2189015764","loc":[-85.6413729,41.9558945]},"n2189015766":{"id":"n2189015766","loc":[-85.6412884,41.9560606]},"n2189015770":{"id":"n2189015770","loc":[-85.6411798,41.9560112]},"n2189015771":{"id":"n2189015771","loc":[-85.6410651,41.9562132]},"n2189015774":{"id":"n2189015774","loc":[-85.6409504,41.9561728]},"n2189015778":{"id":"n2189015778","loc":[-85.6407996,41.9564241]},"n2189015781":{"id":"n2189015781","loc":[-85.6406889,41.9563892]},"n2189015785":{"id":"n2189015785","loc":[-85.6404857,41.9567024]},"n2189015789":{"id":"n2189015789","loc":[-85.6406909,41.9567877]},"n2189015793":{"id":"n2189015793","loc":[-85.6405642,41.9570165]},"n2189015796":{"id":"n2189015796","loc":[-85.6415359,41.9573711]},"n2189015800":{"id":"n2189015800","loc":[-85.6411738,41.9579501]},"n2189015804":{"id":"n2189015804","loc":[-85.6411119,41.957921]},"n2189015808":{"id":"n2189015808","loc":[-85.6403186,41.9591751]},"n2189015909":{"id":"n2189015909","loc":[-85.6389293,41.9564636]},"n2189015926":{"id":"n2189015926","loc":[-85.6385431,41.9564617]},"n2189015929":{"id":"n2189015929","loc":[-85.6385457,41.9561823]},"n2189015932":{"id":"n2189015932","loc":[-85.6389319,41.9561843]},"n2199109865":{"id":"n2199109865","loc":[-85.6400768,41.956776]},"n2199109867":{"id":"n2199109867","loc":[-85.639902,41.9567153]},"n2199109869":{"id":"n2199109869","loc":[-85.640004,41.956553]},"n2199109871":{"id":"n2199109871","loc":[-85.6401788,41.9566137]},"n2199109873":{"id":"n2199109873","loc":[-85.6399316,41.9564506],"tags":{"man_made":"water_tower"}},"n2199109876":{"id":"n2199109876","loc":[-85.6397689,41.9572354]},"n2199109878":{"id":"n2199109878","loc":[-85.6399229,41.9569826]},"n2199109880":{"id":"n2199109880","loc":[-85.639706,41.9569095]},"n2199109882":{"id":"n2199109882","loc":[-85.639552,41.9571623]},"n2199109884":{"id":"n2199109884","loc":[-85.6391028,41.9569517]},"n2199109886":{"id":"n2199109886","loc":[-85.6392876,41.956646]},"n2199109888":{"id":"n2199109888","loc":[-85.639484,41.9567117]},"n2199109889":{"id":"n2199109889","loc":[-85.6394322,41.9567973]},"n2199109890":{"id":"n2199109890","loc":[-85.6393718,41.9567771]},"n2199109891":{"id":"n2199109891","loc":[-85.6392387,41.9569972]},"n1819848900":{"id":"n1819848900","loc":[-85.638281,41.9576578]},"n1819848978":{"id":"n1819848978","loc":[-85.6377186,41.9580867]},"n1819849039":{"id":"n1819849039","loc":[-85.6384217,41.9573405]},"n1819849050":{"id":"n1819849050","loc":[-85.6377011,41.9570042]},"n1819849088":{"id":"n1819849088","loc":[-85.6382879,41.9580817]},"n2114807549":{"id":"n2114807549","loc":[-85.6362551,41.96473]},"n2114807587":{"id":"n2114807587","loc":[-85.6368694,41.9629829]},"n2189015725":{"id":"n2189015725","loc":[-85.644156,41.9569753]},"n2189015741":{"id":"n2189015741","loc":[-85.6419825,41.9597632]},"w208627217":{"id":"w208627217","tags":{"area":"yes","building":"yes"},"nodes":["n2189015741","n2189015744","n2189015747","n2189015750","n2189015753","n2189015756","n2189015759","n2189015760","n2189015764","n2189015766","n2189015770","n2189015771","n2189015774","n2189015778","n2189015781","n2189015785","n2189015789","n2189015793","n2189015796","n2189015800","n2189015804","n2189015808","n2189015741"]},"w208627212":{"id":"w208627212","tags":{"highway":"service"},"nodes":["n2189015691","n2189015696","n2189015722","n2189015725"]},"w209717057":{"id":"w209717057","tags":{"area":"yes","building":"yes"},"nodes":["n2199109876","n2199109878","n2199109880","n2199109882","n2199109876"]},"w209717056":{"id":"w209717056","tags":{"area":"yes","building":"yes"},"nodes":["n2199109865","n2199109867","n2199109869","n2199109871","n2199109865"]},"w208627231":{"id":"w208627231","tags":{"area":"yes","building":"yes"},"nodes":["n2189015909","n2189015926","n2189015929","n2189015932","n2189015909"]},"w170848326":{"id":"w170848326","tags":{"building":"yes"},"nodes":["n1819848881","n1819849050","n1819848978","n1819849088","n1819848900","n1819848947","n1819849039","n1819849044","n1819848881"]},"w17963182":{"id":"w17963182","tags":{"highway":"service"},"nodes":["n185949763","n185949765","n2189015691","n185949745"]},"w201484340":{"id":"w201484340","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807565","n2114807564","n2114807547","n2114807587","n2114807558","n2114807549","n2114807593"]},"w209717058":{"id":"w209717058","tags":{"area":"yes","building":"yes"},"nodes":["n2199109884","n2199109886","n2199109888","n2199109889","n2199109890","n2199109891","n2199109884"]},"n185954650":{"id":"n185954650","loc":[-85.627331,41.957439]},"n185966949":{"id":"n185966949","loc":[-85.626868,41.957314]},"n185989335":{"id":"n185989335","loc":[-85.62529,41.958568]},"n185989337":{"id":"n185989337","loc":[-85.624962,41.958453]},"n185989339":{"id":"n185989339","loc":[-85.624832,41.958399]},"n185989340":{"id":"n185989340","loc":[-85.624707,41.958325]},"n185989342":{"id":"n185989342","loc":[-85.624636,41.958251]},"n185989345":{"id":"n185989345","loc":[-85.624578,41.95818]},"n185989347":{"id":"n185989347","loc":[-85.624533,41.958099]},"n185989349":{"id":"n185989349","loc":[-85.624507,41.957985]},"n185989351":{"id":"n185989351","loc":[-85.624495,41.957807]},"n185989353":{"id":"n185989353","loc":[-85.624514,41.957663]},"n185989354":{"id":"n185989354","loc":[-85.624577,41.957593]},"n185989356":{"id":"n185989356","loc":[-85.624685,41.95754]},"n185989357":{"id":"n185989357","loc":[-85.624802,41.957523]},"n185989359":{"id":"n185989359","loc":[-85.624996,41.957524]},"n185989361":{"id":"n185989361","loc":[-85.625409,41.957515]},"n185989364":{"id":"n185989364","loc":[-85.625634,41.957496]},"n185989367":{"id":"n185989367","loc":[-85.625832,41.957453]},"n185989368":{"id":"n185989368","loc":[-85.626044,41.957394]},"n354031352":{"id":"n354031352","loc":[-85.6252778,41.9586111],"tags":{"amenity":"place_of_worship","denomination":"baptist","name":"First Baptist Church","religion":"christian"}},"n2199109892":{"id":"n2199109892","loc":[-85.6261578,41.9589963]},"n2199109893":{"id":"n2199109893","loc":[-85.6263191,41.9586865]},"n2199109894":{"id":"n2199109894","loc":[-85.6261186,41.9586288]},"n2199109895":{"id":"n2199109895","loc":[-85.6260644,41.9587329]},"n2199109896":{"id":"n2199109896","loc":[-85.6261547,41.9587589]},"n2199109898":{"id":"n2199109898","loc":[-85.6260476,41.9589646]},"n185966951":{"id":"n185966951","loc":[-85.628404,41.957438]},"w17965351":{"id":"w17965351","tags":{"highway":"residential","name":"Flower Street"},"nodes":["n185966948","n185966949","n185954650","n185966951","n185966953","n185966955","n185966957"]},"w17967809":{"id":"w17967809","tags":{"highway":"residential","name":"Azaleamum Drive"},"nodes":["n185982197","n185989335","n185989337","n185989339","n185989340","n185989342","n185989345","n185989347","n185989349","n185989351","n185989353","n185989354","n185989356","n185989357","n185989359","n185989361","n185989364","n185989367","n185989368","n185982196"]},"w209717059":{"id":"w209717059","tags":{"area":"yes","building":"yes"},"nodes":["n2199109892","n2199109893","n2199109894","n2199109895","n2199109896","n2199109898","n2199109892"]},"n185961390":{"id":"n185961390","loc":[-85.63137,41.959033]},"n185961393":{"id":"n185961393","loc":[-85.634315,41.959017]},"w17966214":{"id":"w17966214","tags":{"highway":"residential","name":"East Adams Street"},"nodes":["n185975351","n185967434","n185968108"]},"w17964793":{"id":"w17964793","tags":{"highway":"residential","name":"Morris Ave"},"nodes":["n185961389","n185961390","n185961391","n185961393","n185961396"]},"n185952166":{"id":"n185952166","loc":[-85.638174,41.95831]},"n2114807552":{"id":"n2114807552","loc":[-85.6383526,41.9593788]},"n2114807591":{"id":"n2114807591","loc":[-85.6383741,41.9593968]},"n2189015731":{"id":"n2189015731","loc":[-85.6368404,41.9592785]},"n2189015734":{"id":"n2189015734","loc":[-85.6368404,41.9585918]},"n2189015737":{"id":"n2189015737","loc":[-85.6376009,41.9585918]},"n2189015738":{"id":"n2189015738","loc":[-85.6376009,41.9592785]},"n2189015897":{"id":"n2189015897","loc":[-85.6376839,41.9566137]},"n2189015900":{"id":"n2189015900","loc":[-85.6376831,41.9564865]},"n2189015903":{"id":"n2189015903","loc":[-85.6381161,41.9564851]},"n2189015906":{"id":"n2189015906","loc":[-85.6381168,41.9566122]},"n2189015937":{"id":"n2189015937","loc":[-85.6364789,41.9590634]},"n2189015940":{"id":"n2189015940","loc":[-85.6361137,41.9590672]},"n2189015943":{"id":"n2189015943","loc":[-85.6361169,41.9594033]},"n2189015945":{"id":"n2189015945","loc":[-85.6363456,41.9594021]},"n2189015952":{"id":"n2189015952","loc":[-85.636112,41.958892]},"n2189015955":{"id":"n2189015955","loc":[-85.6364757,41.9588894]},"n2189015957":{"id":"n2189015957","loc":[-85.6364729,41.9586747]},"n2189015958":{"id":"n2189015958","loc":[-85.6361103,41.9586765]},"n2189015959":{"id":"n2189015959","loc":[-85.6364719,41.9585562]},"n2189015960":{"id":"n2189015960","loc":[-85.6361093,41.958558]},"n2189015961":{"id":"n2189015961","loc":[-85.6355494,41.9586403]},"n2189015962":{"id":"n2189015962","loc":[-85.635549,41.9584711]},"n2189015963":{"id":"n2189015963","loc":[-85.6351831,41.9584715]},"n2189015964":{"id":"n2189015964","loc":[-85.6351834,41.9586408]},"n2189015966":{"id":"n2189015966","loc":[-85.6359579,41.9586359]},"n2189015968":{"id":"n2189015968","loc":[-85.6359561,41.9585465]},"n2189015971":{"id":"n2189015971","loc":[-85.6355476,41.9585509]},"n2189015974":{"id":"n2189015974","loc":[-85.6359516,41.9592934]},"n2189015977":{"id":"n2189015977","loc":[-85.635949,41.9586697]},"n2189015980":{"id":"n2189015980","loc":[-85.6351329,41.9586716]},"n2189015983":{"id":"n2189015983","loc":[-85.6351318,41.9583949]},"n2189015986":{"id":"n2189015986","loc":[-85.6349148,41.9583954]},"n2189015989":{"id":"n2189015989","loc":[-85.6349186,41.9592958]},"n2189015995":{"id":"n2189015995","loc":[-85.6360173,41.9593286]},"n2189015998":{"id":"n2189015998","loc":[-85.6360278,41.9583079]},"n2114807550":{"id":"n2114807550","loc":[-85.6383392,41.9595404]},"n2114807551":{"id":"n2114807551","loc":[-85.6375855,41.9616107]},"n2114807559":{"id":"n2114807559","loc":[-85.6373978,41.9621273]},"n2114807562":{"id":"n2114807562","loc":[-85.6373361,41.9622609]},"n2114807563":{"id":"n2114807563","loc":[-85.6376472,41.9613953]},"n2114807574":{"id":"n2114807574","loc":[-85.636974,41.9627695]},"n2114807589":{"id":"n2114807589","loc":[-85.6383017,41.9595005]},"n2114807592":{"id":"n2114807592","loc":[-85.6377169,41.9613494]},"n2114807595":{"id":"n2114807595","loc":[-85.6371081,41.962574]},"n2189015934":{"id":"n2189015934","loc":[-85.6364855,41.9595098]},"n2189015949":{"id":"n2189015949","loc":[-85.6363466,41.9595105]},"w208627244":{"id":"w208627244","tags":{"highway":"service"},"nodes":["n2189015992","n2189015995","n2189015998"]},"w208627240":{"id":"w208627240","tags":{"area":"yes","building":"yes"},"nodes":["n2189015961","n2189015971","n2189015962","n2189015963","n2189015964","n2189015961"]},"w17967437":{"id":"w17967437","tags":{"highway":"residential","name":"Lyman St"},"nodes":["n185964361","n185984024"]},"w208627237":{"id":"w208627237","tags":{"area":"yes","building":"yes"},"nodes":["n2189015955","n2189015957","n2189015958","n2189015952","n2189015955"]},"w17967465":{"id":"w17967465","tags":{"highway":"residential","name":"W Adams St"},"nodes":["n185978394","n185984022","n185964360"]},"w208627228":{"id":"w208627228","tags":{"area":"yes","building":"yes"},"nodes":["n2189015897","n2189015900","n2189015903","n2189015906","n2189015897"]},"w201484351":{"id":"w201484351","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807587","n2114807574","n2114807595","n2114807562","n2114807559","n2114807551","n2114807563","n2114807589","n2114807552"]},"w208627239":{"id":"w208627239","tags":{"area":"yes","building":"yes"},"nodes":["n2189015957","n2189015959","n2189015960","n2189015958","n2189015957"]},"w208627233":{"id":"w208627233","tags":{"area":"yes","building":"yes"},"nodes":["n2189015934","n2189015937","n2189015940","n2189015943","n2189015945","n2189015949","n2189015934"]},"w208627241":{"id":"w208627241","tags":{"area":"yes","building":"yes"},"nodes":["n2189015961","n2189015966","n2189015968","n2189015971","n2189015961"]},"w17967970":{"id":"w17967970","tags":{"highway":"residential","name":"Adams St"},"nodes":["n185975351","n185978394"]},"w208627235":{"id":"w208627235","tags":{"area":"yes","building":"yes"},"nodes":["n2189015940","n2189015952","n2189015955","n2189015937","n2189015940"]},"w17965468":{"id":"w17965468","tags":{"highway":"residential","name":"Armstrong Blvd"},"nodes":["n185967917","n2189015998","n185967918","n185964362","n185952166"]},"w201484346":{"id":"w201484346","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807551","n2114807592","n2114807550","n2114807591"]},"w208627242":{"id":"w208627242","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189015974","n2189015977","n2189015980","n2189015983","n2189015986","n2189015989","n2189015974"]},"w208627216":{"id":"w208627216","tags":{"area":"yes","building":"yes"},"nodes":["n2189015731","n2189015734","n2189015737","n2189015738","n2189015731"]},"n185984309":{"id":"n185984309","loc":[-85.631421,41.961494]},"n185987987":{"id":"n185987987","loc":[-85.631456,41.960673]},"n185965397":{"id":"n185965397","loc":[-85.634603,41.959838]},"w17965196":{"id":"w17965196","tags":{"highway":"residential","name":"Burke Ave"},"nodes":["n185965395","n185965397","n185965399"]},"w17967215":{"id":"w17967215","tags":{"highway":"residential","name":"Kellogg Ave"},"nodes":["n185968114","n185984309","n185967440","n185978402"]},"w17967597":{"id":"w17967597","tags":{"highway":"residential","name":"Barnard Ave"},"nodes":["n185968112","n185987987","n185967438","n185978399"]},"n394490857":{"id":"n394490857","loc":[-85.633952,41.960664]},"n394490858":{"id":"n394490858","loc":[-85.633938,41.960227]},"n394490859":{"id":"n394490859","loc":[-85.634794,41.960212]},"n394490860":{"id":"n394490860","loc":[-85.634815,41.960662]},"n394490861":{"id":"n394490861","loc":[-85.634103,41.961268]},"n394490862":{"id":"n394490862","loc":[-85.634103,41.961001]},"n394490863":{"id":"n394490863","loc":[-85.634504,41.961003]},"n394490864":{"id":"n394490864","loc":[-85.634561,41.961269]},"n1057629869":{"id":"n1057629869","loc":[-85.6382599,41.9612134]},"n1057629937":{"id":"n1057629937","loc":[-85.6380035,41.9616137]},"n2189016014":{"id":"n2189016014","loc":[-85.6360365,41.9626496]},"n2189016017":{"id":"n2189016017","loc":[-85.6360374,41.9623228]},"n2189016020":{"id":"n2189016020","loc":[-85.6367557,41.9623239]},"n2189016022":{"id":"n2189016022","loc":[-85.6367566,41.9619919]},"n2189016025":{"id":"n2189016025","loc":[-85.6351794,41.9619893]},"n2189016028":{"id":"n2189016028","loc":[-85.6351788,41.9622011]},"n2189016031":{"id":"n2189016031","loc":[-85.6350855,41.9622009]},"n2189016034":{"id":"n2189016034","loc":[-85.6350845,41.962527]},"n2189016037":{"id":"n2189016037","loc":[-85.6352732,41.9625273]},"n2189016039":{"id":"n2189016039","loc":[-85.6352738,41.9623178]},"n2189016042":{"id":"n2189016042","loc":[-85.6357712,41.9623186]},"n2189016044":{"id":"n2189016044","loc":[-85.6357702,41.9626492]},"n1057629880":{"id":"n1057629880","loc":[-85.638817,41.9619017]},"n1057629923":{"id":"n1057629923","loc":[-85.6390733,41.9615014]},"w91092312":{"id":"w91092312","tags":{"power":"station"},"nodes":["n1057629923","n1057629869","n1057629937","n1057629880","n1057629923"]},"w34369826":{"id":"w34369826","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490861","n394490862","n394490863","n394490864","n394490861"]},"w34369825":{"id":"w34369825","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490857","n394490858","n394490859","n394490860","n394490857"]},"w208627248":{"id":"w208627248","tags":{"area":"yes","building":"yes"},"nodes":["n2189016014","n2189016017","n2189016020","n2189016022","n2189016025","n2189016028","n2189016031","n2189016034","n2189016037","n2189016039","n2189016042","n2189016044","n2189016014"]},"n394490766":{"id":"n394490766","loc":[-85.616777,41.955642]},"n394490768":{"id":"n394490768","loc":[-85.617239,41.955644]},"n394490792":{"id":"n394490792","loc":[-85.619034,41.95543]},"n185972055":{"id":"n185972055","loc":[-85.6185905,41.9568211]},"n185972057":{"id":"n185972057","loc":[-85.6186688,41.9570086]},"n185972059":{"id":"n185972059","loc":[-85.6186924,41.9581453]},"n185972060":{"id":"n185972060","loc":[-85.6187082,41.9588211],"tags":{"highway":"turning_circle"}},"n1819790724":{"id":"n1819790724","loc":[-85.6182155,41.9555703]},"n1819790735":{"id":"n1819790735","loc":[-85.6184059,41.9566188]},"n1819790799":{"id":"n1819790799","loc":[-85.6182372,41.9563771]},"n1819790896":{"id":"n1819790896","loc":[-85.6181431,41.9557227]},"n185971405":{"id":"n185971405","loc":[-85.6186766,41.9577468]},"n185971565":{"id":"n185971565","loc":[-85.6181613,41.9560879]},"n185967985":{"id":"n185967985","loc":[-85.6186798,41.9585791]},"n185955753":{"id":"n185955753","loc":[-85.620773,41.9555854]},"n185955755":{"id":"n185955755","loc":[-85.6212652,41.9559891]},"n185955748":{"id":"n185955748","loc":[-85.620722,41.954858]},"n185955751":{"id":"n185955751","loc":[-85.6206912,41.955367]},"n185967987":{"id":"n185967987","loc":[-85.6159351,41.9585809]},"n185971407":{"id":"n185971407","loc":[-85.6159142,41.9577578]},"n185971570":{"id":"n185971570","loc":[-85.6162248,41.95603]},"n185971572":{"id":"n185971572","loc":[-85.6160402,41.9560749]},"n185971574":{"id":"n185971574","loc":[-85.61593,41.956201]},"n185981301":{"id":"n185981301","loc":[-85.6158973,41.9581601]},"n394490762":{"id":"n394490762","loc":[-85.617193,41.954706]},"n394490764":{"id":"n394490764","loc":[-85.616773,41.954737]},"n394490787":{"id":"n394490787","loc":[-85.618972,41.954737]},"n394490790":{"id":"n394490790","loc":[-85.619046,41.954929]},"n394490794":{"id":"n394490794","loc":[-85.619922,41.955296]},"n394490796":{"id":"n394490796","loc":[-85.61991,41.95501]},"n394490798":{"id":"n394490798","loc":[-85.619974,41.954751]},"n1819790677":{"id":"n1819790677","loc":[-85.6187031,41.9550522]},"n1819790787":{"id":"n1819790787","loc":[-85.6186436,41.9552022]},"n1819790828":{"id":"n1819790828","loc":[-85.6185127,41.9553393]},"w17966857":{"id":"w17966857","tags":{"access":"private","highway":"service","name":"Sable River Rd"},"nodes":["n185972059","n185981301"]},"w34369814":{"id":"w34369814","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490787","n394490790","n394490792","n394490794","n394490796","n394490798","n394490787"]},"w17964176":{"id":"w17964176","tags":{"highway":"residential"},"nodes":["n185955747","n185955748","n185955751","n185955753","n185955755"]},"w17965838":{"id":"w17965838","tags":{"access":"private","highway":"service","name":"Pine River Rd"},"nodes":["n185971405","n185971407"]},"w17965476":{"id":"w17965476","tags":{"access":"private","highway":"service","name":"Raisin River Rd"},"nodes":["n185967985","n185967987"]},"w17965913":{"id":"w17965913","tags":{"access":"private","highway":"service","name":"Shiawassee River Rd"},"nodes":["n185972054","n1819790677","n1819790787","n1819790828","n1819790724","n1819790896","n185971565","n1819790799","n1819790735","n185972055","n185972057","n185971405","n185972059","n185967985","n185972060"]},"w34369811":{"id":"w34369811","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490762","n394490764","n394490766","n394490768","n394490762"]},"w17965854":{"id":"w17965854","tags":{"access":"private","highway":"service","name":"Sturgeon River Rd"},"nodes":["n185971565","n185971570","n185971572","n185971574"]},"n2139795769":{"id":"n2139795769","loc":[-85.6250804,41.9608796]},"n2139795770":{"id":"n2139795770","loc":[-85.6250315,41.9613684]},"n2139795771":{"id":"n2139795771","loc":[-85.6249671,41.9614362]},"n2139795772":{"id":"n2139795772","loc":[-85.6249698,41.961522]},"n2139795773":{"id":"n2139795773","loc":[-85.6250798,41.9615838]},"n2139795774":{"id":"n2139795774","loc":[-85.6252273,41.9615639]},"n2139795775":{"id":"n2139795775","loc":[-85.6252863,41.9614622]},"n2139795776":{"id":"n2139795776","loc":[-85.6252273,41.9613764]},"n2139795777":{"id":"n2139795777","loc":[-85.6251227,41.9613525]},"n2139795778":{"id":"n2139795778","loc":[-85.6249564,41.9612527]},"n2139795779":{"id":"n2139795779","loc":[-85.6249846,41.9610254]},"n2139795780":{"id":"n2139795780","loc":[-85.6266725,41.9599647]},"n2139795781":{"id":"n2139795781","loc":[-85.6259162,41.9599711]},"n2139795782":{"id":"n2139795782","loc":[-85.6257185,41.960019]},"n2139795783":{"id":"n2139795783","loc":[-85.6255509,41.9601213]},"n185963539":{"id":"n185963539","loc":[-85.615718,41.983893]},"n185964418":{"id":"n185964418","loc":[-85.616626,42.049512]},"n185966614":{"id":"n185966614","loc":[-85.615514,41.976603]},"n185966635":{"id":"n185966635","loc":[-85.616118,42.013017]},"n185969040":{"id":"n185969040","loc":[-85.615632,41.972357]},"n185969070":{"id":"n185969070","loc":[-85.619145,41.967648]},"n185972156":{"id":"n185972156","loc":[-85.621894,41.963956]},"n185972157":{"id":"n185972157","loc":[-85.621806,41.964077]},"n185972158":{"id":"n185972158","loc":[-85.620848,41.965341]},"n185972159":{"id":"n185972159","loc":[-85.620684,41.965558]},"n185972160":{"id":"n185972160","loc":[-85.620621,41.965658]},"n185972161":{"id":"n185972161","loc":[-85.617844,41.969359]},"n185972162":{"id":"n185972162","loc":[-85.616843,41.97068]},"n185972164":{"id":"n185972164","loc":[-85.616714,41.970839]},"n185972166":{"id":"n185972166","loc":[-85.615879,41.971969]},"n185972168":{"id":"n185972168","loc":[-85.615748,41.972159]},"n185972170":{"id":"n185972170","loc":[-85.615589,41.972502]},"n185972172":{"id":"n185972172","loc":[-85.615542,41.972733]},"n185972175":{"id":"n185972175","loc":[-85.615524,41.972947]},"n185972177":{"id":"n185972177","loc":[-85.615512,41.973715]},"n185972179":{"id":"n185972179","loc":[-85.615513,41.976496]},"n185972180":{"id":"n185972180","loc":[-85.615538,41.977246]},"n185972181":{"id":"n185972181","loc":[-85.61558,41.982139]},"n185972184":{"id":"n185972184","loc":[-85.61557,41.983317]},"n185972186":{"id":"n185972186","loc":[-85.615591,41.983463]},"n185972188":{"id":"n185972188","loc":[-85.615763,41.984146]},"n185972190":{"id":"n185972190","loc":[-85.615814,41.98435]},"n185972192":{"id":"n185972192","loc":[-85.615965,41.998453]},"n185972194":{"id":"n185972194","loc":[-85.615982,42.001237]},"n185972195":{"id":"n185972195","loc":[-85.616055,42.00555]},"n185972197":{"id":"n185972197","loc":[-85.616134,42.014887]},"n185972199":{"id":"n185972199","loc":[-85.616177,42.018465]},"n185972201":{"id":"n185972201","loc":[-85.616298,42.027627]},"n185972203":{"id":"n185972203","loc":[-85.616513,42.042212]},"w203968015":{"id":"w203968015","tags":{"highway":"residential"},"nodes":["n2139795768","n2139795769"]},"w17965932":{"id":"w17965932","tags":{"highway":"residential","name":"Buckhorn Road","name_1":"County Highway 122"},"nodes":["n185972155","n185972156","n185972157","n185972158","n185972159","n185972160","n185969070","n185972161","n185972162","n185972164","n185972166","n185972168","n185969040","n185972170","n185972172","n185972175","n185972177","n185972179","n185966614","n185972180","n185972181","n185972184","n185972186","n185963539","n185972188","n185972190","n185972192","n185972194","n185972195","n185966635","n185972197","n185972199","n185972201","n185972203","n185964418"]},"w203968016":{"id":"w203968016","tags":{"highway":"residential","name":"New Jersey Court"},"nodes":["n2139795770","n2139795771","n2139795772","n2139795773","n2139795774","n2139795775","n2139795776","n2139795777","n2139795770","n2139795778","n2139795779","n2139795769"]},"w203968017":{"id":"w203968017","tags":{"highway":"residential","name":"Oklahoma Drive"},"nodes":["n2139795780","n2139795781","n2139795782","n2139795783","n2139795769"]},"n1819790528":{"id":"n1819790528","loc":[-85.6184827,41.960025]},"n1819790530":{"id":"n1819790530","loc":[-85.6168626,41.9605834]},"n1819790534":{"id":"n1819790534","loc":[-85.6197379,41.9617163]},"n1819790541":{"id":"n1819790541","loc":[-85.6198881,41.9620833]},"n1819790543":{"id":"n1819790543","loc":[-85.619695,41.9619397]},"n1819790547":{"id":"n1819790547","loc":[-85.6190298,41.9609504]},"n1819790555":{"id":"n1819790555","loc":[-85.6180471,41.9609788]},"n1819790559":{"id":"n1819790559","loc":[-85.6203817,41.9605436]},"n1819790583":{"id":"n1819790583","loc":[-85.6201564,41.9603282]},"n1819790590":{"id":"n1819790590","loc":[-85.617045,41.9598894]},"n1819790609":{"id":"n1819790609","loc":[-85.6177638,41.9598495]},"n1819790618":{"id":"n1819790618","loc":[-85.6195234,41.9610143]},"n1819790642":{"id":"n1819790642","loc":[-85.6181179,41.9627933]},"n1819790659":{"id":"n1819790659","loc":[-85.6174634,41.962897]},"n1819790665":{"id":"n1819790665","loc":[-85.6170343,41.9630885]},"n1819790674":{"id":"n1819790674","loc":[-85.6194697,41.9601925]},"n1819790685":{"id":"n1819790685","loc":[-85.6207722,41.9610665]},"n1819790687":{"id":"n1819790687","loc":[-85.6202315,41.9622109]},"n1819790697":{"id":"n1819790697","loc":[-85.6184505,41.9624662]},"n1819790726":{"id":"n1819790726","loc":[-85.6178926,41.9628492]},"n1819790738":{"id":"n1819790738","loc":[-85.6173347,41.9598016]},"n1819790762":{"id":"n1819790762","loc":[-85.6186221,41.9609105]},"n1819790774":{"id":"n1819790774","loc":[-85.6175922,41.9608308]},"n1819790781":{"id":"n1819790781","loc":[-85.6167768,41.9633198]},"n1819790796":{"id":"n1819790796","loc":[-85.619856,41.961461]},"n1819790811":{"id":"n1819790811","loc":[-85.6208215,41.9620195]},"n1819790833":{"id":"n1819790833","loc":[-85.618311,41.9612536]},"n1819790854":{"id":"n1819790854","loc":[-85.6183646,41.9626417]},"n1819790863":{"id":"n1819790863","loc":[-85.6204997,41.9608547]},"n1819790867":{"id":"n1819790867","loc":[-85.6184934,41.9621391]},"n1819790877":{"id":"n1819790877","loc":[-85.6206928,41.9621152]},"n1819790881":{"id":"n1819790881","loc":[-85.6170879,41.960735]},"n1819790891":{"id":"n1819790891","loc":[-85.6168304,41.9601207]},"n1819790898":{"id":"n1819790898","loc":[-85.619813,41.9612297]},"n1819790909":{"id":"n1819790909","loc":[-85.6167982,41.960376]},"n1819790912":{"id":"n1819790912","loc":[-85.6205855,41.9610462]},"n1819790544":{"id":"n1819790544","loc":[-85.612968,41.9707781]},"n1819790549":{"id":"n1819790549","loc":[-85.614395,41.9697172]},"n1819790552":{"id":"n1819790552","loc":[-85.6180535,41.9655536]},"n1819790554":{"id":"n1819790554","loc":[-85.6111227,41.9703713]},"n1819790560":{"id":"n1819790560","loc":[-85.6112729,41.9701958]},"n1819790563":{"id":"n1819790563","loc":[-85.6137512,41.9689917]},"n1819790564":{"id":"n1819790564","loc":[-85.6181072,41.9659205]},"n1819790595":{"id":"n1819790595","loc":[-85.6170021,41.9666863]},"n1819790605":{"id":"n1819790605","loc":[-85.6168948,41.9644527]},"n1819790606":{"id":"n1819790606","loc":[-85.6128071,41.9701081]},"n1819790607":{"id":"n1819790607","loc":[-85.6129251,41.9704032]},"n1819790612":{"id":"n1819790612","loc":[-85.6177638,41.9663912]},"n1819790615":{"id":"n1819790615","loc":[-85.6152533,41.9670373]},"n1819790622":{"id":"n1819790622","loc":[-85.6146739,41.9673804]},"n1819790623":{"id":"n1819790623","loc":[-85.6180428,41.9661838]},"n1819790625":{"id":"n1819790625","loc":[-85.6172918,41.9646202]},"n1819790645":{"id":"n1819790645","loc":[-85.6178067,41.965043]},"n1819790647":{"id":"n1819790647","loc":[-85.6143306,41.9712488]},"n1819790649":{"id":"n1819790649","loc":[-85.6147383,41.9707702]},"n1819790654":{"id":"n1819790654","loc":[-85.6157361,41.9668459]},"n1819790657":{"id":"n1819790657","loc":[-85.6145666,41.9710733]},"n1819790668":{"id":"n1819790668","loc":[-85.6166909,41.9642692]},"n1819790671":{"id":"n1819790671","loc":[-85.6141482,41.9696538]},"n1819790679":{"id":"n1819790679","loc":[-85.6148349,41.9705388]},"n1819790686":{"id":"n1819790686","loc":[-85.6139551,41.9695501]},"n1819790696":{"id":"n1819790696","loc":[-85.6119703,41.9699087]},"n1819790704":{"id":"n1819790704","loc":[-85.6140731,41.9684174]},"n1819790706":{"id":"n1819790706","loc":[-85.6124745,41.9699246]},"n1819790718":{"id":"n1819790718","loc":[-85.6165407,41.9636868]},"n1819790720":{"id":"n1819790720","loc":[-85.61388,41.9687365]},"n1819790731":{"id":"n1819790731","loc":[-85.6165193,41.9639421]},"n1819790739":{"id":"n1819790739","loc":[-85.6146739,41.9699964]},"n1819790753":{"id":"n1819790753","loc":[-85.6173883,41.9665747]},"n1819790760":{"id":"n1819790760","loc":[-85.6133221,41.9712089]},"n1819790767":{"id":"n1819790767","loc":[-85.6116698,41.9699246]},"n1819790779":{"id":"n1819790779","loc":[-85.6130753,41.9710573]},"n1819790791":{"id":"n1819790791","loc":[-85.6137083,41.9692869]},"n1819790795":{"id":"n1819790795","loc":[-85.6141482,41.9679627]},"n1819790798":{"id":"n1819790798","loc":[-85.6137727,41.9694305]},"n1819790836":{"id":"n1819790836","loc":[-85.6143842,41.9676037]},"n1819790915":{"id":"n1819790915","loc":[-85.6148456,41.9702756]},"n1819790926":{"id":"n1819790926","loc":[-85.6138371,41.9713525]},"n1819790927":{"id":"n1819790927","loc":[-85.6141053,41.9713525]},"n1819790931":{"id":"n1819790931","loc":[-85.6162832,41.966814]},"n1821014625":{"id":"n1821014625","loc":[-85.5960611,41.9808498]},"n1821014627":{"id":"n1821014627","loc":[-85.5565843,42.010982]},"n1821014629":{"id":"n1821014629","loc":[-85.5971541,41.9805808]},"n1821014632":{"id":"n1821014632","loc":[-85.6061837,41.9725907]},"n1821014633":{"id":"n1821014633","loc":[-85.5247773,42.025766]},"n1821014635":{"id":"n1821014635","loc":[-85.5908938,41.9902384]},"n1821014636":{"id":"n1821014636","loc":[-85.5917682,41.9860637]},"n1821014637":{"id":"n1821014637","loc":[-85.5456556,42.0166797]},"n1821014638":{"id":"n1821014638","loc":[-85.5795749,42.0032352]},"n1821014639":{"id":"n1821014639","loc":[-85.6103988,41.9723456]},"n1821014642":{"id":"n1821014642","loc":[-85.5818816,42.0022466]},"n1821014643":{"id":"n1821014643","loc":[-85.5570604,42.0091586]},"n1821014644":{"id":"n1821014644","loc":[-85.5952886,41.9803792]},"n1821014645":{"id":"n1821014645","loc":[-85.5780366,42.0040343]},"n1821014646":{"id":"n1821014646","loc":[-85.6050505,41.9751971]},"n1821014647":{"id":"n1821014647","loc":[-85.5854435,41.9946162]},"n1821014648":{"id":"n1821014648","loc":[-85.5452278,42.0168768]},"n1821014649":{"id":"n1821014649","loc":[-85.6023254,41.9780166]},"n1821014651":{"id":"n1821014651","loc":[-85.5761899,42.0046783]},"n1821014653":{"id":"n1821014653","loc":[-85.5897351,41.9876707]},"n1821014657":{"id":"n1821014657","loc":[-85.5963601,41.9808998]},"n1821014658":{"id":"n1821014658","loc":[-85.5892952,41.9951983]},"n1821014660":{"id":"n1821014660","loc":[-85.5778328,42.0037194]},"n1821014661":{"id":"n1821014661","loc":[-85.5541475,42.0125705]},"n1821014663":{"id":"n1821014663","loc":[-85.5914047,41.9856469]},"n1821014664":{"id":"n1821014664","loc":[-85.6101681,41.9727723]},"n1821014665":{"id":"n1821014665","loc":[-85.5910172,41.9854696]},"n1821014666":{"id":"n1821014666","loc":[-85.5398688,42.0187699]},"n1821014667":{"id":"n1821014667","loc":[-85.5218752,42.0282884]},"n1821014668":{"id":"n1821014668","loc":[-85.5159582,42.0329384]},"n1821014669":{"id":"n1821014669","loc":[-85.5898102,41.9847319]},"n1821014670":{"id":"n1821014670","loc":[-85.5734809,42.0066235]},"n1821014671":{"id":"n1821014671","loc":[-85.5922939,41.980852]},"n1821014672":{"id":"n1821014672","loc":[-85.6023629,41.9781163]},"n1821014674":{"id":"n1821014674","loc":[-85.5409953,42.0191724]},"n1821014676":{"id":"n1821014676","loc":[-85.584435,41.9949909]},"n1821014677":{"id":"n1821014677","loc":[-85.5972399,41.9783835]},"n1821014678":{"id":"n1821014678","loc":[-85.5616738,42.0071337]},"n1821014681":{"id":"n1821014681","loc":[-85.5202994,42.0310755]},"n1821014682":{"id":"n1821014682","loc":[-85.5915912,41.9857767]},"n1821014684":{"id":"n1821014684","loc":[-85.6022288,41.977897]},"n1821014687":{"id":"n1821014687","loc":[-85.5933024,41.9846362]},"n1821014688":{"id":"n1821014688","loc":[-85.5846871,41.9956169]},"n1821014689":{"id":"n1821014689","loc":[-85.5898209,41.99037]},"n1821014691":{"id":"n1821014691","loc":[-85.5448939,42.0149261]},"n1821014692":{"id":"n1821014692","loc":[-85.5977763,41.9786348]},"n1821014694":{"id":"n1821014694","loc":[-85.5767706,42.0034523]},"n1821014695":{"id":"n1821014695","loc":[-85.6103559,41.9726766]},"n1821014697":{"id":"n1821014697","loc":[-85.5922134,41.9809876]},"n1821014698":{"id":"n1821014698","loc":[-85.5935277,41.9831728]},"n1821014700":{"id":"n1821014700","loc":[-85.5674674,42.0078273]},"n1821014703":{"id":"n1821014703","loc":[-85.6021,41.9778053]},"n1821014704":{"id":"n1821014704","loc":[-85.5756763,42.0053737]},"n1821014705":{"id":"n1821014705","loc":[-85.5887695,41.9895207]},"n1821014707":{"id":"n1821014707","loc":[-85.6061073,41.9746866]},"n1821014708":{"id":"n1821014708","loc":[-85.6033446,41.9751692]},"n1821014710":{"id":"n1821014710","loc":[-85.5180986,42.0322332]},"n1821014711":{"id":"n1821014711","loc":[-85.543365,42.0163569]},"n1821014712":{"id":"n1821014712","loc":[-85.6030656,41.9753646]},"n1821014713":{"id":"n1821014713","loc":[-85.6104417,41.9704792]},"n1821014714":{"id":"n1821014714","loc":[-85.5205716,42.030998]},"n1821014716":{"id":"n1821014716","loc":[-85.516382,42.032536]},"n1821014717":{"id":"n1821014717","loc":[-85.5932863,41.9820882]},"n1821014718":{"id":"n1821014718","loc":[-85.5361928,42.0194974]},"n1821014720":{"id":"n1821014720","loc":[-85.6011613,41.9773586]},"n1821014721":{"id":"n1821014721","loc":[-85.554287,42.0109124]},"n1821014722":{"id":"n1821014722","loc":[-85.5577524,42.0103425]},"n1821014725":{"id":"n1821014725","loc":[-85.5867256,41.9921004]},"n1821014726":{"id":"n1821014726","loc":[-85.5856045,41.9968807]},"n1821014727":{"id":"n1821014727","loc":[-85.5545445,42.0106454]},"n1821014728":{"id":"n1821014728","loc":[-85.5923797,41.9842534]},"n1821014729":{"id":"n1821014729","loc":[-85.5696346,42.0081462]},"n1821014730":{"id":"n1821014730","loc":[-85.5998322,41.9786884]},"n1821014735":{"id":"n1821014735","loc":[-85.5337426,42.0218266]},"n1821014736":{"id":"n1821014736","loc":[-85.5847944,41.994672]},"n1821014740":{"id":"n1821014740","loc":[-85.5315271,42.0238669]},"n1821014741":{"id":"n1821014741","loc":[-85.5248846,42.027085]},"n1821014742":{"id":"n1821014742","loc":[-85.5853376,41.997018]},"n1821014743":{"id":"n1821014743","loc":[-85.5894883,41.988811]},"n1821014745":{"id":"n1821014745","loc":[-85.6095311,41.9726226]},"n1821014746":{"id":"n1821014746","loc":[-85.5531511,42.0133416]},"n1821014747":{"id":"n1821014747","loc":[-85.5735882,42.007058]},"n1821014749":{"id":"n1821014749","loc":[-85.5428554,42.0164366]},"n1821014751":{"id":"n1821014751","loc":[-85.5395255,42.0186304]},"n1821014752":{"id":"n1821014752","loc":[-85.571378,42.0083176]},"n1821014754":{"id":"n1821014754","loc":[-85.5541918,42.0113925]},"n1821014755":{"id":"n1821014755","loc":[-85.5278029,42.0250806]},"n1821014756":{"id":"n1821014756","loc":[-85.5936725,41.9827102]},"n1821014757":{"id":"n1821014757","loc":[-85.5176266,42.0346677]},"n1821014758":{"id":"n1821014758","loc":[-85.6096692,41.9714245]},"n1821014759":{"id":"n1821014759","loc":[-85.5770321,42.0034266]},"n1821014761":{"id":"n1821014761","loc":[-85.5988921,41.9779369]},"n1821014762":{"id":"n1821014762","loc":[-85.5811788,42.0024499]},"n1821014763":{"id":"n1821014763","loc":[-85.5154003,42.0381101]},"n1821014764":{"id":"n1821014764","loc":[-85.5155827,42.0374089]},"n1821014765":{"id":"n1821014765","loc":[-85.5891249,41.9884978]},"n1821014766":{"id":"n1821014766","loc":[-85.5313863,42.0238293]},"n1821014768":{"id":"n1821014768","loc":[-85.593297,41.9833363]},"n1821014769":{"id":"n1821014769","loc":[-85.5849446,41.9957245]},"n1821014770":{"id":"n1821014770","loc":[-85.5537774,42.0130847]},"n1821014771":{"id":"n1821014771","loc":[-85.6111766,41.9706069]},"n1821014772":{"id":"n1821014772","loc":[-85.5585477,42.008989]},"n1821014774":{"id":"n1821014774","loc":[-85.5928142,41.9852623]},"n1821014777":{"id":"n1821014777","loc":[-85.5891933,41.9882608]},"n1821014778":{"id":"n1821014778","loc":[-85.5926909,41.9817532]},"n1821014779":{"id":"n1821014779","loc":[-85.5260272,42.0252201]},"n1821014781":{"id":"n1821014781","loc":[-85.5894615,41.9950468]},"n1821014782":{"id":"n1821014782","loc":[-85.5461063,42.0143242]},"n1821014783":{"id":"n1821014783","loc":[-85.5711527,42.0085886]},"n1821014784":{"id":"n1821014784","loc":[-85.5329379,42.0218624]},"n1821014786":{"id":"n1821014786","loc":[-85.583047,42.0020252]},"n1821014787":{"id":"n1821014787","loc":[-85.5758962,42.0054095]},"n1821014788":{"id":"n1821014788","loc":[-85.5626354,42.0077733]},"n1821014789":{"id":"n1821014789","loc":[-85.6029852,41.9755999]},"n1821014790":{"id":"n1821014790","loc":[-85.5892362,41.9886755]},"n1821014791":{"id":"n1821014791","loc":[-85.5157597,42.0372017]},"n1821014793":{"id":"n1821014793","loc":[-85.6054582,41.9751094]},"n1821014794":{"id":"n1821014794","loc":[-85.5986936,41.9778412]},"n1821014795":{"id":"n1821014795","loc":[-85.5880775,41.98976]},"n1821014796":{"id":"n1821014796","loc":[-85.5858727,41.9963624]},"n1821014798":{"id":"n1821014798","loc":[-85.5792543,42.0035958]},"n1821014799":{"id":"n1821014799","loc":[-85.5921665,41.9838326]},"n1821014801":{"id":"n1821014801","loc":[-85.599214,41.9782599]},"n1821014802":{"id":"n1821014802","loc":[-85.5571905,42.0090967]},"n1821014803":{"id":"n1821014803","loc":[-85.5426891,42.0173612]},"n1821014804":{"id":"n1821014804","loc":[-85.5889626,41.9896404]},"n1821014805":{"id":"n1821014805","loc":[-85.5491264,42.0141648]},"n1821014806":{"id":"n1821014806","loc":[-85.5618897,42.0072631]},"n1821014808":{"id":"n1821014808","loc":[-85.5573501,42.0109802]},"n1821014809":{"id":"n1821014809","loc":[-85.5983463,41.9778031]},"n1821014810":{"id":"n1821014810","loc":[-85.5885173,41.9895128]},"n1821014811":{"id":"n1821014811","loc":[-85.6084998,41.9721143]},"n1821014812":{"id":"n1821014812","loc":[-85.5737598,42.0056389]},"n1821014814":{"id":"n1821014814","loc":[-85.5542173,42.0118132]},"n1821014816":{"id":"n1821014816","loc":[-85.5277868,42.024451]},"n1821014817":{"id":"n1821014817","loc":[-85.5403999,42.0191724]},"n1821014819":{"id":"n1821014819","loc":[-85.5983879,41.9791452]},"n1821014820":{"id":"n1821014820","loc":[-85.5891302,41.9897578]},"n1821014822":{"id":"n1821014822","loc":[-85.5930731,41.9805108]},"n1821014824":{"id":"n1821014824","loc":[-85.515395,42.0378471]},"n1821014825":{"id":"n1821014825","loc":[-85.5352755,42.0205136]},"n1821014826":{"id":"n1821014826","loc":[-85.5502744,42.0133398]},"n1821014828":{"id":"n1821014828","loc":[-85.5701295,42.0088256]},"n1821014830":{"id":"n1821014830","loc":[-85.5888929,41.9953099]},"n1821014832":{"id":"n1821014832","loc":[-85.5880077,41.9901547]},"n1821014833":{"id":"n1821014833","loc":[-85.5451192,42.0157072]},"n1821014834":{"id":"n1821014834","loc":[-85.6096478,41.9711932]},"n1821014835":{"id":"n1821014835","loc":[-85.5806424,42.0026532]},"n1821014836":{"id":"n1821014836","loc":[-85.5911674,41.9868732]},"n1821014838":{"id":"n1821014838","loc":[-85.5930302,41.9836571]},"n1821014839":{"id":"n1821014839","loc":[-85.588925,41.9938148]},"n1821014840":{"id":"n1821014840","loc":[-85.6111874,41.9705311]},"n1821014841":{"id":"n1821014841","loc":[-85.5680843,42.0075842]},"n1821014842":{"id":"n1821014842","loc":[-85.6012793,41.9775062]},"n1821014843":{"id":"n1821014843","loc":[-85.5855562,41.9989777]},"n1821014844":{"id":"n1821014844","loc":[-85.5506137,42.0131662]},"n1821014845":{"id":"n1821014845","loc":[-85.5270049,42.025457]},"n1821014846":{"id":"n1821014846","loc":[-85.5257054,42.025244]},"n1821014847":{"id":"n1821014847","loc":[-85.6011184,41.9771832]},"n1821014848":{"id":"n1821014848","loc":[-85.515534,42.0389234]},"n1821014850":{"id":"n1821014850","loc":[-85.5847032,42.0010347]},"n1821014853":{"id":"n1821014853","loc":[-85.5361499,42.019063]},"n1821014854":{"id":"n1821014854","loc":[-85.5439176,42.0165721]},"n1821014855":{"id":"n1821014855","loc":[-85.5838825,42.0017284]},"n1821014857":{"id":"n1821014857","loc":[-85.5542173,42.0122317]},"n1821014859":{"id":"n1821014859","loc":[-85.5708201,42.0089195]},"n1821014860":{"id":"n1821014860","loc":[-85.5844833,41.9954415]},"n1821014862":{"id":"n1821014862","loc":[-85.5223204,42.0295396]},"n1821014863":{"id":"n1821014863","loc":[-85.5777898,42.0035918]},"n1821014864":{"id":"n1821014864","loc":[-85.591044,41.9898078]},"n1821014865":{"id":"n1821014865","loc":[-85.5973204,41.980182]},"n1821014866":{"id":"n1821014866","loc":[-85.5699578,42.0085825]},"n1821014867":{"id":"n1821014867","loc":[-85.5210598,42.0305278]},"n1821014868":{"id":"n1821014868","loc":[-85.5929108,41.9819008]},"n1821014869":{"id":"n1821014869","loc":[-85.5279799,42.0242995]},"n1821014870":{"id":"n1821014870","loc":[-85.5196114,42.0320539]},"n1821014871":{"id":"n1821014871","loc":[-85.5785449,42.0040883]},"n1821014872":{"id":"n1821014872","loc":[-85.588292,41.9895766]},"n1821014873":{"id":"n1821014873","loc":[-85.5160172,42.0331775]},"n1821014874":{"id":"n1821014874","loc":[-85.5688849,42.0077016]},"n1821014876":{"id":"n1821014876","loc":[-85.5857976,41.9996036]},"n1821014879":{"id":"n1821014879","loc":[-85.5990906,41.9780765]},"n1821014881":{"id":"n1821014881","loc":[-85.5483647,42.0144279]},"n1821014883":{"id":"n1821014883","loc":[-85.5691209,42.0077972]},"n1821014885":{"id":"n1821014885","loc":[-85.6076844,41.9721103]},"n1821014886":{"id":"n1821014886","loc":[-85.6015489,41.9766147]},"n1821014887":{"id":"n1821014887","loc":[-85.574822,42.0052802]},"n1821014888":{"id":"n1821014888","loc":[-85.5880024,41.9899593]},"n1821014890":{"id":"n1821014890","loc":[-85.5909421,41.9893772]},"n1821014892":{"id":"n1821014892","loc":[-85.5497326,42.0138141]},"n1821014893":{"id":"n1821014893","loc":[-85.5167106,42.0357811]},"n1821014895":{"id":"n1821014895","loc":[-85.5844404,41.9952501]},"n1821014896":{"id":"n1821014896","loc":[-85.5362465,42.0192662]},"n1821014898":{"id":"n1821014898","loc":[-85.5906095,41.9889147]},"n1821014899":{"id":"n1821014899","loc":[-85.5590667,42.0089354]},"n1821014900":{"id":"n1821014900","loc":[-85.5921598,41.9844209]},"n1821014902":{"id":"n1821014902","loc":[-85.5778971,42.0039266]},"n1821014903":{"id":"n1821014903","loc":[-85.603012,41.9761981]},"n1821014904":{"id":"n1821014904","loc":[-85.6108977,41.9706787]},"n1821014905":{"id":"n1821014905","loc":[-85.5685738,42.0076139]},"n1821014906":{"id":"n1821014906","loc":[-85.5392787,42.0186304]},"n1821014907":{"id":"n1821014907","loc":[-85.5227885,42.0274972]},"n1821014908":{"id":"n1821014908","loc":[-85.5857547,41.9961431]},"n1821014910":{"id":"n1821014910","loc":[-85.5610354,42.0072812]},"n1821014911":{"id":"n1821014911","loc":[-85.5209632,42.0308705]},"n1821014912":{"id":"n1821014912","loc":[-85.5709757,42.0087959]},"n1821014913":{"id":"n1821014913","loc":[-85.59231,41.9839344]},"n1821014914":{"id":"n1821014914","loc":[-85.5375245,42.0185865]},"n1821014916":{"id":"n1821014916","loc":[-85.5901548,41.9839841]},"n1821014917":{"id":"n1821014917","loc":[-85.5611213,42.0086405]},"n1821014918":{"id":"n1821014918","loc":[-85.5360426,42.0198122]},"n1821014919":{"id":"n1821014919","loc":[-85.5862817,41.9948691]},"n1821014921":{"id":"n1821014921","loc":[-85.5469807,42.0144438]},"n1821014922":{"id":"n1821014922","loc":[-85.5761309,42.0053838]},"n1821014924":{"id":"n1821014924","loc":[-85.516264,42.0332971]},"n1821014925":{"id":"n1821014925","loc":[-85.5277224,42.0246661]},"n1821014926":{"id":"n1821014926","loc":[-85.5980016,41.9798231]},"n1821014928":{"id":"n1821014928","loc":[-85.5924548,41.9806965]},"n1821014930":{"id":"n1821014930","loc":[-85.5899121,41.985023]},"n1821014931":{"id":"n1821014931","loc":[-85.5706015,42.0089492]},"n1821014932":{"id":"n1821014932","loc":[-85.515926,42.033046]},"n1821014933":{"id":"n1821014933","loc":[-85.5982377,41.9796796]},"n1821014936":{"id":"n1821014936","loc":[-85.5475721,42.0145253]},"n1821014938":{"id":"n1821014938","loc":[-85.5895701,41.9902323]},"n1821014939":{"id":"n1821014939","loc":[-85.6030495,41.9759947]},"n1821014942":{"id":"n1821014942","loc":[-85.6094721,41.9724989]},"n1821014944":{"id":"n1821014944","loc":[-85.5921973,41.9811112]},"n1821014945":{"id":"n1821014945","loc":[-85.5223526,42.0291332]},"n1821014946":{"id":"n1821014946","loc":[-85.5965103,41.9808998]},"n1821014948":{"id":"n1821014948","loc":[-85.517766,42.0349227]},"n1821014950":{"id":"n1821014950","loc":[-85.5889894,41.990996]},"n1821014951":{"id":"n1821014951","loc":[-85.5601932,42.0092902]},"n1821014954":{"id":"n1821014954","loc":[-85.6028135,41.9764055]},"n1821014955":{"id":"n1821014955","loc":[-85.5520621,42.0130666]},"n1821014956":{"id":"n1821014956","loc":[-85.593002,41.9839344]},"n1821014957":{"id":"n1821014957","loc":[-85.515926,42.0369666]},"n1821014960":{"id":"n1821014960","loc":[-85.5761255,42.003877]},"n1821014961":{"id":"n1821014961","loc":[-85.5716355,42.007911]},"n1821014962":{"id":"n1821014962","loc":[-85.5575378,42.0109045]},"n1821014963":{"id":"n1821014963","loc":[-85.5735667,42.0068188]},"n1821014964":{"id":"n1821014964","loc":[-85.5915214,41.9865861]},"n1821014965":{"id":"n1821014965","loc":[-85.5866344,41.9923157]},"n1821014967":{"id":"n1821014967","loc":[-85.5283138,42.0242256]},"n1821014968":{"id":"n1821014968","loc":[-85.5177875,42.0355801]},"n1821014969":{"id":"n1821014969","loc":[-85.548071,42.0144934]},"n1821014972":{"id":"n1821014972","loc":[-85.5611159,42.0088557]},"n1821014973":{"id":"n1821014973","loc":[-85.541686,42.0188757]},"n1821014974":{"id":"n1821014974","loc":[-85.5917628,41.9862631]},"n1821014975":{"id":"n1821014975","loc":[-85.5854864,41.9959478]},"n1821014977":{"id":"n1821014977","loc":[-85.609102,41.9722317]},"n1821014980":{"id":"n1821014980","loc":[-85.5761202,42.0042438]},"n1821014982":{"id":"n1821014982","loc":[-85.5465944,42.0143601]},"n1821014983":{"id":"n1821014983","loc":[-85.5173261,42.0342732]},"n1821014984":{"id":"n1821014984","loc":[-85.5897297,41.9888509]},"n1821014985":{"id":"n1821014985","loc":[-85.5856688,41.999181]},"n1821014986":{"id":"n1821014986","loc":[-85.5344011,42.0217251]},"n1821014987":{"id":"n1821014987","loc":[-85.601467,41.9768203]},"n1821014988":{"id":"n1821014988","loc":[-85.5457254,42.0165123]},"n1821014989":{"id":"n1821014989","loc":[-85.6023482,41.9784332]},"n1821014991":{"id":"n1821014991","loc":[-85.5361606,42.01823]},"n1821014992":{"id":"n1821014992","loc":[-85.5178465,42.0351139]},"n1821014995":{"id":"n1821014995","loc":[-85.5634293,42.0078092]},"n1821014996":{"id":"n1821014996","loc":[-85.573497,42.0072015]},"n1821014997":{"id":"n1821014997","loc":[-85.5976328,41.9799725]},"n1821014998":{"id":"n1821014998","loc":[-85.5210651,42.0303166]},"n1821015003":{"id":"n1821015003","loc":[-85.5222131,42.0288064]},"n1821015004":{"id":"n1821015004","loc":[-85.5897941,41.984405]},"n1821015005":{"id":"n1821015005","loc":[-85.5975725,41.9776099]},"n1821015006":{"id":"n1821015006","loc":[-85.5765708,42.0034903]},"n1821015007":{"id":"n1821015007","loc":[-85.5250187,42.026559]},"n1821015009":{"id":"n1821015009","loc":[-85.5426998,42.0166279]},"n1821015010":{"id":"n1821015010","loc":[-85.5957606,41.9806584]},"n1821015011":{"id":"n1821015011","loc":[-85.5262753,42.0252497]},"n1821015012":{"id":"n1821015012","loc":[-85.5266455,42.0253374]},"n1821015014":{"id":"n1821015014","loc":[-85.5515632,42.0130187]},"n1821015015":{"id":"n1821015015","loc":[-85.6024058,41.9765212]},"n1821015017":{"id":"n1821015017","loc":[-85.5175032,42.0357156]},"n1821015018":{"id":"n1821015018","loc":[-85.5302718,42.0236039]},"n1821015019":{"id":"n1821015019","loc":[-85.6024005,41.9782759]},"n1821015020":{"id":"n1821015020","loc":[-85.5907758,41.9890821]},"n1821015021":{"id":"n1821015021","loc":[-85.6019445,41.9777215]},"n1821015022":{"id":"n1821015022","loc":[-85.5942854,41.9800881]},"n1821015024":{"id":"n1821015024","loc":[-85.5325826,42.0222711]},"n1821015029":{"id":"n1821015029","loc":[-85.555093,42.0105316]},"n1821015033":{"id":"n1821015033","loc":[-85.5249704,42.0270372]},"n1821015034":{"id":"n1821015034","loc":[-85.5243965,42.0272205]},"n1821015038":{"id":"n1821015038","loc":[-85.5413426,42.0190749]},"n1821015039":{"id":"n1821015039","loc":[-85.5920431,41.9848175]},"n1821015041":{"id":"n1821015041","loc":[-85.5577685,42.0106015]},"n1821015042":{"id":"n1821015042","loc":[-85.5453606,42.0158866]},"n1821015045":{"id":"n1821015045","loc":[-85.5333228,42.0217889]},"n1821015046":{"id":"n1821015046","loc":[-85.5426891,42.0175924]},"n1821015048":{"id":"n1821015048","loc":[-85.5886836,41.9936474]},"n1821015050":{"id":"n1821015050","loc":[-85.6001152,41.9786467]},"n1821015051":{"id":"n1821015051","loc":[-85.6094064,41.9723655]},"n1821015053":{"id":"n1821015053","loc":[-85.605721,41.9749738]},"n1821015055":{"id":"n1821015055","loc":[-85.6106791,41.9705048]},"n1821015057":{"id":"n1821015057","loc":[-85.5210437,42.0307071]},"n1821015059":{"id":"n1821015059","loc":[-85.5995694,41.9786725]},"n1821015060":{"id":"n1821015060","loc":[-85.5371638,42.0182938]},"n1821015062":{"id":"n1821015062","loc":[-85.6111766,41.9704593]},"n1821015065":{"id":"n1821015065","loc":[-85.577704,42.0034921]},"n1821015067":{"id":"n1821015067","loc":[-85.5570067,42.0093699]},"n1821015068":{"id":"n1821015068","loc":[-85.5920364,41.9845525]},"n1821015069":{"id":"n1821015069","loc":[-85.5252065,42.0253954]},"n1821015072":{"id":"n1821015072","loc":[-85.5664159,42.0088517]},"n1821015073":{"id":"n1821015073","loc":[-85.5880399,41.991905]},"n1821015075":{"id":"n1821015075","loc":[-85.6099871,41.9727861]},"n1821015076":{"id":"n1821015076","loc":[-85.5319603,42.0231478]},"n1821015078":{"id":"n1821015078","loc":[-85.6036088,41.9751112]},"n1821015080":{"id":"n1821015080","loc":[-85.5983128,41.9789179]},"n1821015082":{"id":"n1821015082","loc":[-85.5614069,42.0071395]},"n1821015083":{"id":"n1821015083","loc":[-85.60968,41.9709738]},"n1821015086":{"id":"n1821015086","loc":[-85.5914195,41.9837351]},"n1821015087":{"id":"n1821015087","loc":[-85.5895473,41.9948036]},"n1821015090":{"id":"n1821015090","loc":[-85.5929913,41.9851905]},"n1821015093":{"id":"n1821015093","loc":[-85.5907396,41.9838485]},"n1821015095":{"id":"n1821015095","loc":[-85.5893864,41.9880176]},"n1821015096":{"id":"n1821015096","loc":[-85.5788024,42.0039807]},"n1821015097":{"id":"n1821015097","loc":[-85.5630592,42.0078411]},"n1821015098":{"id":"n1821015098","loc":[-85.5350609,42.0211274]},"n1821015099":{"id":"n1821015099","loc":[-85.5967195,41.9808679]},"n1821015100":{"id":"n1821015100","loc":[-85.5666734,42.0088119]},"n1821015101":{"id":"n1821015101","loc":[-85.564694,42.0077675]},"n1821015103":{"id":"n1821015103","loc":[-85.6066544,41.9726527]},"n1821015104":{"id":"n1821015104","loc":[-85.6011827,41.9769838]},"n1821015105":{"id":"n1821015105","loc":[-85.5972131,41.9776697]},"n1821015106":{"id":"n1821015106","loc":[-85.5880828,41.9903341]},"n1821015107":{"id":"n1821015107","loc":[-85.5510268,42.0130626]},"n1821015108":{"id":"n1821015108","loc":[-85.6102164,41.970543]},"n1821015109":{"id":"n1821015109","loc":[-85.5905344,41.9853899]},"n1821015111":{"id":"n1821015111","loc":[-85.5888821,41.9913429]},"n1821015112":{"id":"n1821015112","loc":[-85.606295,41.9741921]},"n1821015114":{"id":"n1821015114","loc":[-85.5969556,41.9807443]},"n1821015115":{"id":"n1821015115","loc":[-85.5882223,41.9934081]},"n1821015116":{"id":"n1821015116","loc":[-85.6104471,41.9724971]},"n1821015118":{"id":"n1821015118","loc":[-85.5406091,42.0192162]},"n1821015120":{"id":"n1821015120","loc":[-85.589955,41.9888429]},"n1821015121":{"id":"n1821015121","loc":[-85.5598821,42.0092304]},"n1821015122":{"id":"n1821015122","loc":[-85.545598,42.0144097]},"n1821015123":{"id":"n1821015123","loc":[-85.5649528,42.0079965]},"n1821015125":{"id":"n1821015125","loc":[-85.5883993,41.9917814]},"n1821015126":{"id":"n1821015126","loc":[-85.5295785,42.0239967]},"n1821015129":{"id":"n1821015129","loc":[-85.5648723,42.0078809]},"n1821015132":{"id":"n1821015132","loc":[-85.564989,42.0081103]},"n1821015133":{"id":"n1821015133","loc":[-85.5946127,41.9800841]},"n1821015134":{"id":"n1821015134","loc":[-85.583448,42.0019078]},"n1821015135":{"id":"n1821015135","loc":[-85.5905934,41.9871842]},"n1821015137":{"id":"n1821015137","loc":[-85.610608,41.9704752]},"n1821015138":{"id":"n1821015138","loc":[-85.5752257,42.0052939]},"n1821015139":{"id":"n1821015139","loc":[-85.5893864,41.9943491]},"n1821015140":{"id":"n1821015140","loc":[-85.5426247,42.0169866]},"n1821015141":{"id":"n1821015141","loc":[-85.562001,42.0074526]},"n1821015142":{"id":"n1821015142","loc":[-85.5212046,42.0301094]},"n1821015143":{"id":"n1821015143","loc":[-85.602214,41.9784531]},"n1821015144":{"id":"n1821015144","loc":[-85.5858687,41.9948293]},"n1821015145":{"id":"n1821015145","loc":[-85.5608477,42.0074805]},"n1821015146":{"id":"n1821015146","loc":[-85.5651607,42.0083614]},"n1821015147":{"id":"n1821015147","loc":[-85.5288288,42.0242495]},"n1821015149":{"id":"n1821015149","loc":[-85.5450334,42.0146989]},"n1821015151":{"id":"n1821015151","loc":[-85.5578275,42.0092304]},"n1821015154":{"id":"n1821015154","loc":[-85.6056634,41.9724511]},"n1821015155":{"id":"n1821015155","loc":[-85.5902179,41.9852742]},"n1821015156":{"id":"n1821015156","loc":[-85.5156256,42.0387157]},"n1821015157":{"id":"n1821015157","loc":[-85.5734433,42.0059459]},"n1821015158":{"id":"n1821015158","loc":[-85.6050773,41.9731273]},"n1821015160":{"id":"n1821015160","loc":[-85.5223419,42.0275233]},"n1821015163":{"id":"n1821015163","loc":[-85.6053562,41.972525]},"n1821015164":{"id":"n1821015164","loc":[-85.5850412,41.9946082]},"n1821015165":{"id":"n1821015165","loc":[-85.5359031,42.0186326]},"n1821015166":{"id":"n1821015166","loc":[-85.5608745,42.0077635]},"n1821015169":{"id":"n1821015169","loc":[-85.572876,42.0073189]},"n1821015171":{"id":"n1821015171","loc":[-85.5875424,41.9919188]},"n1821015172":{"id":"n1821015172","loc":[-85.5240116,42.0272581]},"n1821015173":{"id":"n1821015173","loc":[-85.5318369,42.0236818]},"n1821015174":{"id":"n1821015174","loc":[-85.566888,42.0086923]},"n1821015175":{"id":"n1821015175","loc":[-85.5931522,41.9850669]},"n1821015176":{"id":"n1821015176","loc":[-85.5604842,42.0093199]},"n1821015177":{"id":"n1821015177","loc":[-85.5868168,41.9927543]},"n1821015178":{"id":"n1821015178","loc":[-85.6052275,41.9732549]},"n1821015179":{"id":"n1821015179","loc":[-85.5910118,41.9900431]},"n1821015182":{"id":"n1821015182","loc":[-85.5610032,42.0082897]},"n1821015183":{"id":"n1821015183","loc":[-85.5425443,42.0179431]},"n1821015184":{"id":"n1821015184","loc":[-85.5843277,42.0014055]},"n1821015186":{"id":"n1821015186","loc":[-85.5733307,42.0063564]},"n1821015188":{"id":"n1821015188","loc":[-85.5277385,42.0248694]},"n1821015189":{"id":"n1821015189","loc":[-85.5558427,42.0108168]},"n1821015190":{"id":"n1821015190","loc":[-85.5650587,42.0082618]},"n1821015191":{"id":"n1821015191","loc":[-85.5660351,42.0088278]},"n1821015192":{"id":"n1821015192","loc":[-85.5849768,41.9980049]},"n1821015194":{"id":"n1821015194","loc":[-85.5359139,42.0188199]},"n1821015195":{"id":"n1821015195","loc":[-85.593238,41.9849194]},"n1821015197":{"id":"n1821015197","loc":[-85.5850841,41.9983239]},"n1821015199":{"id":"n1821015199","loc":[-85.5983396,41.9794283]},"n1821015204":{"id":"n1821015204","loc":[-85.5452801,42.0145355]},"n1821015205":{"id":"n1821015205","loc":[-85.5340685,42.0218407]},"n1821015207":{"id":"n1821015207","loc":[-85.5773272,42.0034186]},"n1821015209":{"id":"n1821015209","loc":[-85.5535212,42.0132419]},"n1821015211":{"id":"n1821015211","loc":[-85.6107703,41.9706045]},"n1821015212":{"id":"n1821015212","loc":[-85.6030066,41.9758193]},"n1821015213":{"id":"n1821015213","loc":[-85.5359943,42.0184213]},"n1821015214":{"id":"n1821015214","loc":[-85.5922993,41.9813305]},"n1821015215":{"id":"n1821015215","loc":[-85.5672689,42.0080465]},"n1821015217":{"id":"n1821015217","loc":[-85.5160494,42.0365682]},"n1821015218":{"id":"n1821015218","loc":[-85.5401142,42.0190351]},"n1821015219":{"id":"n1821015219","loc":[-85.5607632,42.0092282]},"n1821015220":{"id":"n1821015220","loc":[-85.5866197,41.9947894]},"n1821015221":{"id":"n1821015221","loc":[-85.6017889,41.9765132]},"n1821015222":{"id":"n1821015222","loc":[-85.5595978,42.009059]},"n1821015226":{"id":"n1821015226","loc":[-85.5871494,41.9929018]},"n1821015227":{"id":"n1821015227","loc":[-85.5857708,41.9998866]},"n1821015228":{"id":"n1821015228","loc":[-85.5317135,42.0238094]},"n1821015231":{"id":"n1821015231","loc":[-85.5733521,42.0061372]},"n1821015233":{"id":"n1821015233","loc":[-85.5855991,42.0001936]},"n1821015234":{"id":"n1821015234","loc":[-85.5213924,42.029962]},"n1821015235":{"id":"n1821015235","loc":[-85.6052221,41.9726567]},"n1821015236":{"id":"n1821015236","loc":[-85.5763723,42.0035422]},"n1821015237":{"id":"n1821015237","loc":[-85.5858512,41.9966215]},"n1821015238":{"id":"n1821015238","loc":[-85.567061,42.008439]},"n1821015239":{"id":"n1821015239","loc":[-85.5250563,42.0269057]},"n1821015240":{"id":"n1821015240","loc":[-85.5347551,42.0214263]},"n1821015241":{"id":"n1821015241","loc":[-85.6098463,41.9707066]},"n1821015242":{"id":"n1821015242","loc":[-85.5676927,42.0076519]},"n1821015243":{"id":"n1821015243","loc":[-85.516775,42.0322669]},"n1821015244":{"id":"n1821015244","loc":[-85.5762275,42.0036538]},"n1821015245":{"id":"n1821015245","loc":[-85.5583639,42.0090949]},"n1821015246":{"id":"n1821015246","loc":[-85.5554041,42.0106432]},"n1821015247":{"id":"n1821015247","loc":[-85.5973364,41.9776099]},"n1821015248":{"id":"n1821015248","loc":[-85.6098945,41.9717513]},"n1821015249":{"id":"n1821015249","loc":[-85.6045315,41.9751511]},"n1821015250":{"id":"n1821015250","loc":[-85.5579938,42.0092264]},"n1821015253":{"id":"n1821015253","loc":[-85.6058873,41.9724652]},"n1821015254":{"id":"n1821015254","loc":[-85.5869456,41.9947517]},"n1821015255":{"id":"n1821015255","loc":[-85.5936565,41.9823713]},"n1821015256":{"id":"n1821015256","loc":[-85.5218269,42.0278102]},"n1821015258":{"id":"n1821015258","loc":[-85.5887802,41.9905534]},"n1821015259":{"id":"n1821015259","loc":[-85.5901924,41.9904515]},"n1821015263":{"id":"n1821015263","loc":[-85.5249222,42.0255787]},"n1821015265":{"id":"n1821015265","loc":[-85.5175206,42.0321672]},"n1821015266":{"id":"n1821015266","loc":[-85.5275722,42.0254034]},"n1821015267":{"id":"n1821015267","loc":[-85.6016226,41.9765451]},"n1821015269":{"id":"n1821015269","loc":[-85.5569316,42.011032]},"n1821015271":{"id":"n1821015271","loc":[-85.6010714,41.9785209]},"n1821015272":{"id":"n1821015272","loc":[-85.6050666,41.9729917]},"n1821015273":{"id":"n1821015273","loc":[-85.5891235,41.99529]},"n1821015274":{"id":"n1821015274","loc":[-85.515454,42.0376439]},"n1821015276":{"id":"n1821015276","loc":[-85.5776021,42.0034443]},"n1821015277":{"id":"n1821015277","loc":[-85.6041707,41.9751453]},"n1821015278":{"id":"n1821015278","loc":[-85.5444701,42.0167435]},"n1821015280":{"id":"n1821015280","loc":[-85.5923274,41.9852202]},"n1821015283":{"id":"n1821015283","loc":[-85.5893649,41.9900271]},"n1821015284":{"id":"n1821015284","loc":[-85.5933453,41.9804412]},"n1821015285":{"id":"n1821015285","loc":[-85.5247237,42.026017]},"n1821015286":{"id":"n1821015286","loc":[-85.5286182,42.0242477]},"n1821015287":{"id":"n1821015287","loc":[-85.5904003,41.9888549]},"n1821015288":{"id":"n1821015288","loc":[-85.6062146,41.9739369]},"n1821015290":{"id":"n1821015290","loc":[-85.5762596,42.0052602]},"n1821015292":{"id":"n1821015292","loc":[-85.5849715,41.9975465]},"n1821015293":{"id":"n1821015293","loc":[-85.585229,42.0006241]},"n1821015294":{"id":"n1821015294","loc":[-85.5926922,41.9805946]},"n1821015295":{"id":"n1821015295","loc":[-85.5703387,42.0089133]},"n1821015299":{"id":"n1821015299","loc":[-85.5789955,42.0038611]},"n1821015301":{"id":"n1821015301","loc":[-85.6072888,41.9721918]},"n1821015302":{"id":"n1821015302","loc":[-85.5356349,42.0200992]},"n1821015304":{"id":"n1821015304","loc":[-85.5891772,41.994066]},"n1821015306":{"id":"n1821015306","loc":[-85.606295,41.9744952]},"n1821015307":{"id":"n1821015307","loc":[-85.538871,42.0186583]},"n1821015308":{"id":"n1821015308","loc":[-85.587997,41.994971]},"n1821015311":{"id":"n1821015311","loc":[-85.606869,41.9725809]},"n1821015312":{"id":"n1821015312","loc":[-85.5171974,42.0339943]},"n1821015314":{"id":"n1821015314","loc":[-85.5327435,42.0220479]},"n1821015315":{"id":"n1821015315","loc":[-85.5383439,42.0187282]},"n1821015316":{"id":"n1821015316","loc":[-85.5248095,42.0263119]},"n1821015318":{"id":"n1821015318","loc":[-85.5732502,42.0073051]},"n1821015319":{"id":"n1821015319","loc":[-85.5924226,41.9852663]},"n1821015321":{"id":"n1821015321","loc":[-85.5179001,42.0353052]},"n1821015322":{"id":"n1821015322","loc":[-85.5456771,42.0162413]},"n1821015323":{"id":"n1821015323","loc":[-85.5936618,41.9829096]},"n1821015325":{"id":"n1821015325","loc":[-85.5656931,42.0086582]},"n1821015326":{"id":"n1821015326","loc":[-85.5448456,42.0150975]},"n1821015327":{"id":"n1821015327","loc":[-85.5220039,42.027615]},"n1821015329":{"id":"n1821015329","loc":[-85.517884,42.0354885]},"n1821015330":{"id":"n1821015330","loc":[-85.5576666,42.0101671]},"n1821015332":{"id":"n1821015332","loc":[-85.5368754,42.0181402]},"n1821015333":{"id":"n1821015333","loc":[-85.5367078,42.0181145]},"n1821015334":{"id":"n1821015334","loc":[-85.5903909,41.9904316]},"n1821015335":{"id":"n1821015335","loc":[-85.5430767,42.0163587]},"n1821015336":{"id":"n1821015336","loc":[-85.5277492,42.0252878]},"n1821015337":{"id":"n1821015337","loc":[-85.5312146,42.0236898]},"n1821015338":{"id":"n1821015338","loc":[-85.5886568,41.991614]},"n1821015339":{"id":"n1821015339","loc":[-85.5782498,42.0040883]},"n1821015341":{"id":"n1821015341","loc":[-85.562233,42.0076457]},"n1821015342":{"id":"n1821015342","loc":[-85.588626,41.9952479]},"n1821015343":{"id":"n1821015343","loc":[-85.5762865,42.005033]},"n1821015344":{"id":"n1821015344","loc":[-85.5850841,41.9971478]},"n1821015346":{"id":"n1821015346","loc":[-85.5643144,42.0076936]},"n1821015347":{"id":"n1821015347","loc":[-85.5164893,42.0359467]},"n1821015348":{"id":"n1821015348","loc":[-85.5906846,41.9903541]},"n1821015349":{"id":"n1821015349","loc":[-85.557688,42.0107769]},"n1821015350":{"id":"n1821015350","loc":[-85.5363698,42.0181424]},"n1821015351":{"id":"n1821015351","loc":[-85.5939636,41.9801918]},"n1821015352":{"id":"n1821015352","loc":[-85.5524041,42.0131644]},"n1821015354":{"id":"n1821015354","loc":[-85.5308606,42.0236221]},"n1821015355":{"id":"n1821015355","loc":[-85.5877449,41.9932367]},"n1821015356":{"id":"n1821015356","loc":[-85.519885,42.0318586]},"n1821015357":{"id":"n1821015357","loc":[-85.5454035,42.0168431]},"n1821015358":{"id":"n1821015358","loc":[-85.5970629,41.9781881]},"n1821015359":{"id":"n1821015359","loc":[-85.5932541,41.9844767]},"n1821015360":{"id":"n1821015360","loc":[-85.5970736,41.9778252]},"n1821015361":{"id":"n1821015361","loc":[-85.537031,42.0181601]},"n1821015362":{"id":"n1821015362","loc":[-85.5548355,42.0105156]},"n1821015363":{"id":"n1821015363","loc":[-85.5168648,42.0336158]},"n1821015365":{"id":"n1821015365","loc":[-85.5870435,41.9919507]},"n1821015366":{"id":"n1821015366","loc":[-85.5719681,42.0075443]},"n1821015367":{"id":"n1821015367","loc":[-85.5969985,41.9780446]},"n1821015368":{"id":"n1821015368","loc":[-85.5926761,41.98528]},"n1821015369":{"id":"n1821015369","loc":[-85.5224009,42.0293444]},"n1821015371":{"id":"n1821015371","loc":[-85.518737,42.0322651]},"n1821015372":{"id":"n1821015372","loc":[-85.6064573,41.9726465]},"n1821015373":{"id":"n1821015373","loc":[-85.5201103,42.0313088]},"n1821015375":{"id":"n1821015375","loc":[-85.5378182,42.0186844]},"n1821015376":{"id":"n1821015376","loc":[-85.6109741,41.9706882]},"n1821015377":{"id":"n1821015377","loc":[-85.5993333,41.9785488]},"n1821015378":{"id":"n1821015378","loc":[-85.5889787,41.9907368]},"n1821015380":{"id":"n1821015380","loc":[-85.6060161,41.9737375]},"n1821015381":{"id":"n1821015381","loc":[-85.5743016,42.0053679]},"n1821015382":{"id":"n1821015382","loc":[-85.6014724,41.9776099]},"n1821015383":{"id":"n1821015383","loc":[-85.5574426,42.0091644]},"n1821015385":{"id":"n1821015385","loc":[-85.5208613,42.0309302]},"n1821015386":{"id":"n1821015386","loc":[-85.5919023,41.9837789]},"n1821015387":{"id":"n1821015387","loc":[-85.5455484,42.0160221]},"n1821015392":{"id":"n1821015392","loc":[-85.5801757,42.0028964]},"n1821015395":{"id":"n1821015395","loc":[-85.5493785,42.0139974]},"n1821015396":{"id":"n1821015396","loc":[-85.5449475,42.015488]},"n1821015398":{"id":"n1821015398","loc":[-85.611123,41.9706627]},"n1821015400":{"id":"n1821015400","loc":[-85.5935706,41.9822477]},"n1821015401":{"id":"n1821015401","loc":[-85.5724254,42.0073508]},"n1821015403":{"id":"n1821015403","loc":[-85.5486812,42.0143442]},"n1821015404":{"id":"n1821015404","loc":[-85.5161835,42.0327711]},"n1821015406":{"id":"n1821015406","loc":[-85.5921705,41.9851107]},"n1821015407":{"id":"n1821015407","loc":[-85.531912,42.0234069]},"n1821015410":{"id":"n1821015410","loc":[-85.5292566,42.024176]},"n1821015411":{"id":"n1821015411","loc":[-85.5845316,41.9948315]},"n1821015413":{"id":"n1821015413","loc":[-85.5217947,42.0280413]},"n1821015414":{"id":"n1821015414","loc":[-85.5527367,42.013272]},"n1821015415":{"id":"n1821015415","loc":[-85.5191179,42.0321973]},"n1821015416":{"id":"n1821015416","loc":[-85.5540241,42.0128655]},"n1821015418":{"id":"n1821015418","loc":[-85.5272892,42.0254849]},"n1821015419":{"id":"n1821015419","loc":[-85.5449744,42.016867]},"n1821015420":{"id":"n1821015420","loc":[-85.5852665,41.9986787]},"n1821015421":{"id":"n1821015421","loc":[-85.6102701,41.972186]},"n1821015423":{"id":"n1821015423","loc":[-85.6026365,41.9764972]},"n1821015427":{"id":"n1821015427","loc":[-85.5898692,41.9841498]},"n1821015429":{"id":"n1821015429","loc":[-85.5422546,42.0183855]},"n1821015430":{"id":"n1821015430","loc":[-85.5866505,41.9925549]},"n1821015431":{"id":"n1821015431","loc":[-85.5234376,42.0273577]},"n1821015432":{"id":"n1821015432","loc":[-85.6096746,41.9727284]},"n1821015433":{"id":"n1821015433","loc":[-85.5824891,42.0021567]},"n1821015434":{"id":"n1821015434","loc":[-85.5923905,41.9841139]},"n1821015435":{"id":"n1821015435","loc":[-85.5874565,41.9948014]},"n1821015437":{"id":"n1821015437","loc":[-85.6055279,41.9734423]},"n1821015438":{"id":"n1821015438","loc":[-85.5299379,42.0237376]},"n1821015439":{"id":"n1821015439","loc":[-85.5155022,42.0383651]},"n1821015442":{"id":"n1821015442","loc":[-85.527422,42.0254711]},"n1821015443":{"id":"n1821015443","loc":[-85.5920699,41.9849291]},"n1821015444":{"id":"n1821015444","loc":[-85.5639711,42.0077494]},"n1821015445":{"id":"n1821015445","loc":[-85.5162586,42.0361777]},"n1821015446":{"id":"n1821015446","loc":[-85.5220039,42.029695]},"n1821015448":{"id":"n1821015448","loc":[-85.5176641,42.0356956]},"n1821015449":{"id":"n1821015449","loc":[-85.5930556,41.9841577]},"n1821015451":{"id":"n1821015451","loc":[-85.5320783,42.0228848]},"n1821015452":{"id":"n1821015452","loc":[-85.5170096,42.0357235]},"n1821015453":{"id":"n1821015453","loc":[-85.5571355,42.009613]},"n1821015454":{"id":"n1821015454","loc":[-85.5609979,42.009059]},"n1821015455":{"id":"n1821015455","loc":[-85.6097336,41.9708342]},"n1821015456":{"id":"n1821015456","loc":[-85.5884476,41.9904218]},"w170843846":{"id":"w170843846","tags":{"waterway":"river"},"nodes":["n1819790555","n1819790762","n1819790547","n1819790618","n1819790898","n1819790796","n1819790534","n1819790543","n1819790541","n1819790687","n1819790877","n1819790811","n1819790670"]},"w209083541":{"id":"w209083541","tags":{"name":"Portage River","waterway":"river"},"nodes":["n1821014848","n1821015156","n1821015439","n1821014763","n1821014824","n1821015274","n1821014764","n1821014791","n1821014957","n1821015217","n1821015445","n1821015347","n1821014893","n1821015452","n1821015017","n1821015448","n1821014968","n1821015329","n1821015321","n1821014992","n1821014948","n1821014757","n1821014983","n1821015312","n1821015363","n1821014924","n1821014873","n1821014932","n1821014668","n1821015404","n1821014716","n1821015243","n1821015265","n1821014710","n1821015371","n1821015415","n1821014870","n1821015356","n1821015373","n1821014681","n1821014714","n1821015385","n1821014911","n1821015057","n1821014867","n1821014998","n1821015142","n1821015234","n1821015446","n1821014862","n1821015369","n1821014945","n1821015003","n1821014667","n1821015413","n1821015256","n1821015327","n1821015160","n1821014907","n1821015431","n1821015172","n1821015034","n1821014741","n1821015033","n1821015239","n1821015007","n1821015316","n1821015285","n1821014633","n1821015263","n1821015069","n1821014846","n1821014779","n1821015011","n1821015012","n1821014845","n1821015418","n1821015442","n1821015266","n1821015336","n1821014755","n1821015188","n1821014925","n1821014816","n1821014869","n1821014967","n1821015286","n1821015147","n1821015410","n1821015126","n1821015438","n1821015018","n1821015354","n1821015337","n1821014766","n1821014740","n1821015228","n1821015173","n1821015407","n1821015076","n1821015451","n1821015024","n1821015314","n1821014784","n1821015045","n1821014735","n1821015205","n1821014986","n1821015240","n1821015098","n1821014825","n1821015302","n1821014918","n1821014718","n1821014896","n1821014853","n1821015194","n1821015165","n1821015213","n1821014991","n1821015350","n1821015333","n1821015332","n1821015361","n1821015060","n1821014914","n1821015375","n1821015315","n1821015307","n1821014906","n1821014751","n1821014666","n1821015218","n1821014817","n1821015118","n1821014674","n1821015038","n1821014973","n1821015429","n1821015183","n1821015046","n1821014803","n1821015140","n1821015009","n1821014749","n1821015335","n1821014711","n1821014854","n1821015278","n1821015419","n1821014648","n1821015357","n1821014637","n1821014988","n1821015322","n1821015387","n1821015042","n1821014833","n1821015396","n1821015326","n1821014691","n1821015149","n1821015204","n1821015122","n1821014782","n1821014982","n1821014921","n1821014936","n1821014969","n1821014881","n1821015403","n1821014805","n1821015395","n1821014892","n1821014826","n1821014844","n1821015107","n1821015014","n1821014955","n1821015352","n1821015414","n1821014746","n1821015209","n1821014770","n1821015416","n1821014661","n1821014857","n1821014814","n1821014754","n1821014721","n1821014727","n1821015362","n1821015029","n1821015246","n1821015189","n1821014627","n1821015269","n1821014808","n1821014962","n1821015349","n1821015041","n1821014722","n1821015330","n1821015453","n1821015067","n1821014643","n1821014802","n1821015383","n1821015151","n1821015250","n1821015245","n1821014772","n1821014899","n1821015222","n1821015121","n1821014951","n1821015176","n1821015219","n1821015454","n1821014972","n1821014917","n1821015182","n1821015166","n1821015145","n1821014910","n1821015082","n1821014678","n1821014806","n1821015141","n1821015341","n1821014788","n1821015097","n1821014995","n1821015444","n1821015346","n1821015101","n1821015129","n1821015123","n1821015132","n1821015190","n1821015146","n1821015325","n1821015191","n1821015072","n1821015100","n1821015174","n1821015238","n1821015215","n1821014700","n1821015242","n1821014841","n1821014905","n1821014874","n1821014883","n1821014729","n1821014866","n1821014828","n1821015295","n1821014931","n1821014859","n1821014912","n1821014783","n1821014752","n1821014961","n1821015366","n1821015401","n1821015169","n1821015318","n1821014996","n1821014747","n1821014963","n1821014670","n1821015186","n1821015231","n1821015157","n1821014812","n1821015381","n1821014887","n1821015138","n1821014704","n1821014787","n1821014922","n1821015290","n1821015343","n1821014651","n1821014980","n1821014960","n1821015244","n1821015236","n1821015006","n1821014694","n1821014759","n1821015207","n1821015276","n1821015065","n1821014863","n1821014660","n1821014902","n1821014645","n1821015339","n1821014871","n1821015096","n1821015299","n1821014798","n1821014638","n1821015392","n1821014835","n1821014762","n1821014642","n1821015433","n1821014786","n1821015134","n1821014855","n1821015184","n1821014850","n1821015293","n1821015233","n1821015227","n1821014876","n1821014985","n1821014843","n1821015420","n1821015197","n1821015192","n1821015292","n1821015344","n1821014742","n1821014726","n1821015237","n1821014796","n1821014908","n1821014975","n1821014769","n1821014688","n1821014860","n1821014895","n1821014676","n1821015411","n1821014736","n1821015164","n1821014647","n1821015144","n1821014919","n1821015220","n1821015254","n1821015435","n1821015308","n1821015342","n1821014830","n1821015273","n1821014658","n1821014781","n1821015087","n1821015139","n1821015304","n1821014839","n1821015048","n1821015115","n1821015355","n1821015226","n1821015177","n1821015430","n1821014965","n1821014725","n1821015365","n1821015171","n1821015073","n1821015125","n1821015338","n1821015111","n1821014950","n1821015378","n1821015258","n1821015456","n1821015106","n1821014832","n1821014888","n1821014795","n1821014872","n1821014810","n1821014705","n1821014804","n1821014820","n1821015283","n1821014938","n1821014689","n1821015259","n1821015334","n1821015348","n1821014635","n1821015179","n1821014864","n1821014890","n1821015020","n1821014898","n1821015287","n1821015120","n1821014984","n1821014743","n1821014790","n1821014765","n1821014777","n1821015095","n1821014653","n1821015135","n1821014836","n1821014964","n1821014974","n1821014636","n1821014682","n1821014663","n1821014665","n1821015109","n1821015155","n1821014930","n1821014669","n1821015004","n1821015427","n1821014916","n1821015093","n1821015086","n1821015386","n1821014799","n1821014913","n1821015434","n1821014728","n1821014900","n1821015068","n1821015039","n1821015443","n1821015406","n1821015280","n1821015319","n1821015368","n1821014774","n1821015090","n1821015175","n1821015195","n1821014687","n1821015359","n1821015449","n1821014956","n1821014838","n1821014768","n1821014698","n1821015323","n1821014756","n1821015255","n1821015400","n1821014717","n1821014868","n1821014778","n1821015214","n1821014944","n1821014697","n1821014671","n1821014928","n1821015294","n1821014822","n1821015284","n1821015351","n1821015022","n1821015133","n1821014644","n1821015010","n1821014625","n1821014657","n1821014946","n1821015099","n1821015114","n1821014629","n1821014865","n1821014997","n1821014926","n1821014933","n1821015199","n1821014819","n1821015080","n1821014692","n1821014677","n1821015358","n1821015367","n1821015360","n1821015105","n1821015247","n1821015005","n1821014809","n1821014794","n1821014761","n1821014879","n1821014801","n1821015377","n1821015059","n1821014730","n1821015050","n1821015271","n1821015143","n1821014989","n1821015019","n1821014672","n1821014649","n1821014684","n1821014703","n1821015021","n1821015382","n1821014842","n1821014720","n1821014847","n1821015104","n1821014987","n1821014886","n1821015267","n1821015221","n1821015015","n1821015423","n1821014954","n1821014903","n1821014939","n1821015212","n1821014789","n1821014712","n1821014708","n1821015078","n1821015277","n1821015249","n1821014646","n1821014793","n1821015053","n1821014707","n1821015306","n1821015112","n1821015288","n1821015380","n1821015437","n1821015178","n1821015158","n1821015272","n1821015235","n1821015163","n1821015154","n1821015253","n1821014632","n1821015372","n1821015103","n1821015311","n1821015301","n1821014885","n1821014811","n1821014977","n1821015051","n1821014942","n1821014745","n1821015432","n1821015075","n1821014664","n1821014695","n1821015116","n1821014639","n1821015421","n1821015248","n1821014758","n1821014834","n1821015083","n1821015455","n1821015241","n1821015108","n1821014713","n1821015137","n1821015055","n1821015211","n1821014904","n1821015376","n1821015398","n1821014771","n1821014840","n1821015062","n1819790554","n1819790560","n1819790767","n1819790696","n1819790706","n1819790606","n1819790607","n1819790544","n1819790779","n1819790760","n1819790926","n1819790927","n1819790647","n1819790657","n1819790649","n1819790679","n1819790915","n1819790739","n1819790549","n1819790671","n1819790686","n1819790798","n1819790791","n1819790563","n1819790720","n1819790704","n1819790795","n1819790836","n1819790622","n1819790615","n1819790654","n1819790931","n1819790595","n1819790753","n1819790612","n1819790623","n1819790564","n1819790552","n1819790645","n1819790625","n1819790605","n1819790668","n1819790731","n1819790718","n1819790781","n1819790665","n1819790659","n1819790726","n1819790642","n1819790854","n1819790697","n1819790867","n1819790833","n1819790555","n1819790774","n1819790881","n1819790530","n1819790909","n1819790891","n1819790590","n1819790738","n1819790609","n1819790528","n1819790674","n1819790583","n1819790559","n1819790863","n1819790912","n1819790685","n1819790913"]},"n185955128":{"id":"n185955128","loc":[-85.6189367,41.9519432]},"n185948818":{"id":"n185948818","loc":[-85.616755,41.952231]},"n185978819":{"id":"n185978819","loc":[-85.616773,41.954737]},"n185978821":{"id":"n185978821","loc":[-85.616699,41.954742]},"n2138420714":{"id":"n2138420714","loc":[-85.6176304,41.9515154]},"n2138420715":{"id":"n2138420715","loc":[-85.6177355,41.9515717]},"n2138420716":{"id":"n2138420716","loc":[-85.6192901,41.951573]},"n2138420718":{"id":"n2138420718","loc":[-85.6171481,41.9513579]},"n2138420719":{"id":"n2138420719","loc":[-85.6165981,41.9519199]},"n2138420720":{"id":"n2138420720","loc":[-85.6165719,41.9519922]},"n2138420721":{"id":"n2138420721","loc":[-85.6165832,41.9520757]},"n2138420722":{"id":"n2138420722","loc":[-85.6166355,41.9521453]},"n2138420723":{"id":"n2138420723","loc":[-85.6169161,41.9522788]},"n2138420724":{"id":"n2138420724","loc":[-85.6170882,41.9522538]},"n2138420725":{"id":"n2138420725","loc":[-85.6189204,41.9514674]},"n2138420726":{"id":"n2138420726","loc":[-85.6180346,41.9514735]},"n2138420727":{"id":"n2138420727","loc":[-85.6180362,41.9515719]},"n2138420728":{"id":"n2138420728","loc":[-85.6189204,41.9515727]},"n2138420744":{"id":"n2138420744","loc":[-85.618919,41.9519571]},"n2138420745":{"id":"n2138420745","loc":[-85.6194575,41.9522374]},"n2138420746":{"id":"n2138420746","loc":[-85.6181777,41.9536179]},"n2138420747":{"id":"n2138420747","loc":[-85.6176582,41.9533658]},"n2138420748":{"id":"n2138420748","loc":[-85.6179871,41.9530242]},"n2138420749":{"id":"n2138420749","loc":[-85.618429,41.9532476]},"n2138420750":{"id":"n2138420750","loc":[-85.6185538,41.9531194]},"n2138420751":{"id":"n2138420751","loc":[-85.6180765,41.9528677]},"n2138420752":{"id":"n2138420752","loc":[-85.6180394,41.9528855]},"n2138420753":{"id":"n2138420753","loc":[-85.6193752,41.9521695]},"n2138420754":{"id":"n2138420754","loc":[-85.6181374,41.9535376]},"n2138420755":{"id":"n2138420755","loc":[-85.6179898,41.9535545]},"n2138420756":{"id":"n2138420756","loc":[-85.6177286,41.9534228]},"n2138420757":{"id":"n2138420757","loc":[-85.6181011,41.9530292]},"n2138420759":{"id":"n2138420759","loc":[-85.6185158,41.9531194]},"n2138420760":{"id":"n2138420760","loc":[-85.6191318,41.9520425]},"n2138420761":{"id":"n2138420761","loc":[-85.6182348,41.9529815]},"n2138420762":{"id":"n2138420762","loc":[-85.6184853,41.9524248]},"n2138420763":{"id":"n2138420763","loc":[-85.6186764,41.9525193]},"n2138420764":{"id":"n2138420764","loc":[-85.6189421,41.9526483]},"n2138420765":{"id":"n2138420765","loc":[-85.6182875,41.9531222]},"n2138420766":{"id":"n2138420766","loc":[-85.6179141,41.9535163]},"n2138420767":{"id":"n2138420767","loc":[-85.6178363,41.9535735]},"n185948824":{"id":"n185948824","loc":[-85.6165667,41.9529715]},"n2138420758":{"id":"n2138420758","loc":[-85.6184408,41.953201]},"n2138422349":{"id":"n2138422349","loc":[-85.6175136,41.9533346]},"n2138422350":{"id":"n2138422350","loc":[-85.6171867,41.9531679]},"n2138422351":{"id":"n2138422351","loc":[-85.61722,41.9531305]},"n2138422352":{"id":"n2138422352","loc":[-85.6171889,41.9531158]},"n2138422353":{"id":"n2138422353","loc":[-85.6171733,41.9531284]},"n2138422354":{"id":"n2138422354","loc":[-85.616765,41.9529207]},"n2138422355":{"id":"n2138422355","loc":[-85.6167565,41.9529355]},"n2138422356":{"id":"n2138422356","loc":[-85.6164772,41.9527911]},"n2138422357":{"id":"n2138422357","loc":[-85.6168227,41.9524261]},"n2138422358":{"id":"n2138422358","loc":[-85.6171913,41.9526158]},"n2138422359":{"id":"n2138422359","loc":[-85.6172403,41.9525589]},"n2138422360":{"id":"n2138422360","loc":[-85.6172097,41.952542]},"n2138422361":{"id":"n2138422361","loc":[-85.6173948,41.9523512]},"n2138422362":{"id":"n2138422362","loc":[-85.6174256,41.9523678]},"n2138422363":{"id":"n2138422363","loc":[-85.6174831,41.9523086]},"n2138422364":{"id":"n2138422364","loc":[-85.6173316,41.9522289]},"n2138422365":{"id":"n2138422365","loc":[-85.6174507,41.9521024]},"n2138422366":{"id":"n2138422366","loc":[-85.6174773,41.9521155]},"n2138422367":{"id":"n2138422367","loc":[-85.6176577,41.9519232]},"n2138422368":{"id":"n2138422368","loc":[-85.6176336,41.9519105]},"n2138422369":{"id":"n2138422369","loc":[-85.617747,41.9517861]},"n2138422370":{"id":"n2138422370","loc":[-85.6182675,41.9520559]},"n2138422371":{"id":"n2138422371","loc":[-85.6182105,41.9521219]},"n2138422372":{"id":"n2138422372","loc":[-85.6183863,41.9522203]},"n2138422373":{"id":"n2138422373","loc":[-85.6180984,41.9525266]},"n2138422374":{"id":"n2138422374","loc":[-85.6179159,41.9524295]},"n2138422375":{"id":"n2138422375","loc":[-85.617854,41.9524979]},"n2138422376":{"id":"n2138422376","loc":[-85.6177686,41.9524531]},"n2138422377":{"id":"n2138422377","loc":[-85.6174716,41.9527765]},"n2138422378":{"id":"n2138422378","loc":[-85.6178545,41.9529756]},"n2138425424":{"id":"n2138425424","loc":[-85.6171736,41.9536385]},"n2138425425":{"id":"n2138425425","loc":[-85.6180159,41.9535782]},"n2138425426":{"id":"n2138425426","loc":[-85.6181068,41.9536282]},"n2138425427":{"id":"n2138425427","loc":[-85.6180673,41.9542678]},"n2138425428":{"id":"n2138425428","loc":[-85.6178636,41.9542634]},"n2138425429":{"id":"n2138425429","loc":[-85.6176204,41.9542046]},"n2138425430":{"id":"n2138425430","loc":[-85.6174366,41.9541031]},"n2138425431":{"id":"n2138425431","loc":[-85.6172942,41.9539781]},"n2138425432":{"id":"n2138425432","loc":[-85.6172171,41.9538399]},"n2138425433":{"id":"n2138425433","loc":[-85.6168138,41.9543266]},"n2138425434":{"id":"n2138425434","loc":[-85.6167779,41.9538098]},"n2138425435":{"id":"n2138425435","loc":[-85.6165849,41.9537073]},"n2138425441":{"id":"n2138425441","loc":[-85.616458,41.9543184]},"n2138425442":{"id":"n2138425442","loc":[-85.6166428,41.954345]},"n2138425445":{"id":"n2138425445","loc":[-85.6181332,41.9514117]},"n2138425446":{"id":"n2138425446","loc":[-85.6183263,41.9514111]},"n2138425447":{"id":"n2138425447","loc":[-85.6185033,41.9514102]},"n2138425449":{"id":"n2138425449","loc":[-85.6186809,41.9514093]},"n2138425451":{"id":"n2138425451","loc":[-85.6188681,41.9514082]},"n2138436008":{"id":"n2138436008","loc":[-85.6170474,41.9513604]},"n2138436009":{"id":"n2138436009","loc":[-85.6164937,41.9519586]},"n2138436010":{"id":"n2138436010","loc":[-85.616497,41.9520725]},"n2138436011":{"id":"n2138436011","loc":[-85.6165654,41.9521645]},"n2138436012":{"id":"n2138436012","loc":[-85.6166631,41.9522178]},"n2138436013":{"id":"n2138436013","loc":[-85.6167327,41.9522554]},"n2138436014":{"id":"n2138436014","loc":[-85.6172383,41.9525125]},"n2138439319":{"id":"n2138439319","loc":[-85.6170432,41.9524057]},"n2138439320":{"id":"n2138439320","loc":[-85.617691,41.9517107]},"n2138439321":{"id":"n2138439321","loc":[-85.6177727,41.9516794]},"n2138439322":{"id":"n2138439322","loc":[-85.619085,41.9516811]},"n2138439323":{"id":"n2138439323","loc":[-85.6179432,41.952895]},"n2138439324":{"id":"n2138439324","loc":[-85.6180389,41.9529384]},"n2138439325":{"id":"n2138439325","loc":[-85.6176303,41.9533604]},"n2138439326":{"id":"n2138439326","loc":[-85.6175538,41.9534396]},"n2138439327":{"id":"n2138439327","loc":[-85.6173806,41.9523658]},"n2138439328":{"id":"n2138439328","loc":[-85.6171841,41.9522542]},"n2138439329":{"id":"n2138439329","loc":[-85.6172077,41.9524958]},"n2138439330":{"id":"n2138439330","loc":[-85.6171235,41.9525809]},"n2138439331":{"id":"n2138439331","loc":[-85.6180938,41.9527349]},"n2138439332":{"id":"n2138439332","loc":[-85.6177023,41.9525253]},"n2138439333":{"id":"n2138439333","loc":[-85.6175543,41.9526865]},"n2138439334":{"id":"n2138439334","loc":[-85.6179589,41.9528783]},"n185948820":{"id":"n185948820","loc":[-85.6163249,41.952701]},"n185948822":{"id":"n185948822","loc":[-85.6163757,41.952855]},"n185955123":{"id":"n185955123","loc":[-85.6198103,41.9510408]},"n185958839":{"id":"n185958839","loc":[-85.611651,41.954761]},"n185965033":{"id":"n185965033","loc":[-85.614195,41.954754]},"n185976502":{"id":"n185976502","loc":[-85.617375,41.947559]},"n185976504":{"id":"n185976504","loc":[-85.6174164,41.9510804]},"n185978828":{"id":"n185978828","loc":[-85.613542,41.954756]},"n185978830":{"id":"n185978830","loc":[-85.610373,41.954774]},"n2138420713":{"id":"n2138420713","loc":[-85.6174641,41.9506942]},"n2138420717":{"id":"n2138420717","loc":[-85.6173027,41.9512895]},"n2138420768":{"id":"n2138420768","loc":[-85.61745,41.9501974]},"n2138420773":{"id":"n2138420773","loc":[-85.6174135,41.9489136]},"n2138425436":{"id":"n2138425436","loc":[-85.6159148,41.9538036]},"n2138425437":{"id":"n2138425437","loc":[-85.6159534,41.9539677]},"n2138425438":{"id":"n2138425438","loc":[-85.6160306,41.9540846]},"n2138425439":{"id":"n2138425439","loc":[-85.6161354,41.954181]},"n2138425440":{"id":"n2138425440","loc":[-85.6162733,41.954263]},"n2138425443":{"id":"n2138425443","loc":[-85.6183273,41.9510826]},"n2138425444":{"id":"n2138425444","loc":[-85.6181354,41.9510835]},"n2138425448":{"id":"n2138425448","loc":[-85.6185033,41.9510816]},"n2138425450":{"id":"n2138425450","loc":[-85.6186816,41.9510808]},"n2138425452":{"id":"n2138425452","loc":[-85.6188641,41.9510818]},"n2138435984":{"id":"n2138435984","loc":[-85.6167607,41.9501009]},"n2138436000":{"id":"n2138436000","loc":[-85.6173169,41.947558]},"n2138436001":{"id":"n2138436001","loc":[-85.6173362,41.948883]},"n2138436002":{"id":"n2138436002","loc":[-85.6167791,41.9492952]},"n2138436003":{"id":"n2138436003","loc":[-85.6167543,41.949349]},"n2138436004":{"id":"n2138436004","loc":[-85.6167648,41.9509125]},"n2138436005":{"id":"n2138436005","loc":[-85.6168832,41.9510412]},"n2138436006":{"id":"n2138436006","loc":[-85.6170045,41.9511417]},"n2138436007":{"id":"n2138436007","loc":[-85.6170624,41.9512483]},"n2138436017":{"id":"n2138436017","loc":[-85.6168094,41.9492729]},"n2138436021":{"id":"n2138436021","loc":[-85.6167553,41.9494886]},"n2138436023":{"id":"n2138436023","loc":[-85.6167585,41.9499707]},"n2138436025":{"id":"n2138436025","loc":[-85.6167567,41.9497018]},"w203838284":{"id":"w203838284","tags":{"area":"yes","leisure":"pitch","sport":"baseball"},"nodes":["n2138425424","n2138425425","n2138425426","n2138425427","n2138425428","n2138425429","n2138425430","n2138425431","n2138425432","n2138425424"]},"w203837928":{"id":"w203837928","tags":{"highway":"service"},"nodes":["n2138420717","n2138420718","n2138420719","n2138420720","n2138420721","n2138420722","n185948818","n2138420723","n2138420724","n2138420715"]},"w203839364":{"id":"w203839364","tags":{"highway":"footway"},"nodes":["n2138439331","n2138439332"]},"w203837932":{"id":"w203837932","tags":{"amenity":"parking","area":"yes"},"nodes":["n2138420744","n2138420745","n2138420746","n2138420747","n2138420748","n2138420749","n2138420750","n2138420751","n2138420744"]},"w203839362":{"id":"w203839362","tags":{"highway":"footway"},"nodes":["n2138439327","n2138439328"]},"w203839363":{"id":"w203839363","tags":{"highway":"footway"},"nodes":["n2138439329","n2138439330"]},"w203837933":{"id":"w203837933","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n185955128","n2138420760","n2138420753","n2138420764","n2138420759","n2138420758","n2138420754","n2138420755","n2138420766","n2138420756"]},"w203837936":{"id":"w203837936","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420765","n2138420766"]},"w17966364":{"id":"w17966364","tags":{"access":"private","highway":"service","name":"Collins Dr"},"nodes":["n185961362","n185976502","n2138420773","n2138420768","n2138420713","n185976504","n2138420717","n2138420714","n2138420715","n2138420727","n2138420728","n2138420716"]},"w203838040":{"id":"w203838040","tags":{"amenity":"school","area":"yes","building":"yes","name":"Three Rivers Middle School"},"nodes":["n2138422349","n2138422350","n2138422351","n2138422352","n2138422353","n2138422354","n2138422355","n2138422356","n2138422357","n2138439330","n2138422358","n2138422359","n2138422360","n2138436014","n2138439327","n2138422361","n2138422362","n2138422363","n2138422364","n2138422365","n2138422366","n2138422367","n2138422368","n2138422369","n2138422370","n2138422371","n2138422372","n2138422373","n2138422374","n2138422375","n2138422376","n2138439332","n2138439333","n2138422377","n2138422378","n2138422349"]},"w17964049":{"id":"w17964049","tags":{"highway":"service"},"nodes":["n185955120","n185955123","n2138420716","n185955128","n2138420762","n2138420752","n2138420761","n2138420759"]},"w41074899":{"id":"w41074899","tags":{"highway":"secondary","name":"E Hoffman St","ref":"M 60"},"nodes":["n185978817","n185978819","n185978821","n185965033","n185978828","n185958839","n185978830"]},"w203839365":{"id":"w203839365","tags":{"highway":"footway"},"nodes":["n2138439333","n2138439334"]},"w203837935":{"id":"w203837935","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420762","n2138420763","n2138420764"]},"w203838287":{"id":"w203838287","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425446","n2138425447","n2138425448","n2138425443","n2138425446"]},"w203837934":{"id":"w203837934","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420760","n2138420763","n2138420761"]},"w203838289":{"id":"w203838289","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425449","n2138425451","n2138425452","n2138425450","n2138425449"]},"w17963047":{"id":"w17963047","tags":{"highway":"service"},"nodes":["n185948818","n2138436013","n185948820","n185948822","n185948824","n2138439326","n2138420767","n2138420766"]},"w203839091":{"id":"w203839091","tags":{"highway":"footway"},"nodes":["n185976502","n2138436000","n2138436001","n2138436017","n2138436002","n2138436003","n2138436021","n2138436025","n2138436023","n2138435984","n2138436004","n2138436005","n2138436006","n2138436007","n2138436008","n2138436009","n2138436010","n2138436011","n2138436012","n2138436013","n2138439319","n2138439329","n2138436014"]},"w204830797":{"id":"w204830797","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420756","n2138420757","n2138420765","n2138420758"]},"w203838288":{"id":"w203838288","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425447","n2138425449","n2138425450","n2138425448","n2138425447"]},"w203838285":{"id":"w203838285","tags":{"area":"yes","leisure":"pitch","sport":"baseball"},"nodes":["n2138425433","n2138425434","n2138425435","n2138425436","n2138425437","n2138425438","n2138425439","n2138425440","n2138425441","n2138425442","n2138425433"]},"w203838286":{"id":"w203838286","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425443","n2138425444","n2138425445","n2138425446","n2138425443"]},"w203837929":{"id":"w203837929","tags":{"amenity":"parking","area":"yes"},"nodes":["n2138420725","n2138420726","n2138420727","n2138420728","n2138420725"]},"w203839361":{"id":"w203839361","tags":{"highway":"footway"},"nodes":["n2138439319","n2138439328","n2138439320","n2138439321","n2138439322","n2138439331","n2138439334","n2138439323","n2138439324","n2138439325","n2138439326"]},"n394381698":{"id":"n394381698","loc":[-85.614471,41.954755]},"n394381699":{"id":"n394381699","loc":[-85.6152,41.954744]},"n394381700":{"id":"n394381700","loc":[-85.615201,41.954081]},"n394381701":{"id":"n394381701","loc":[-85.614426,41.954042]},"n394381702":{"id":"n394381702","loc":[-85.616319,41.954749]},"n394381704":{"id":"n394381704","loc":[-85.616152,41.954752]},"n394381706":{"id":"n394381706","loc":[-85.615201,41.95483]},"n394490775":{"id":"n394490775","loc":[-85.613971,41.954839]},"n394490782":{"id":"n394490782","loc":[-85.614372,41.954841]},"n185958835":{"id":"n185958835","loc":[-85.611615,41.953704]},"n185958837":{"id":"n185958837","loc":[-85.611636,41.953938]},"n185958842":{"id":"n185958842","loc":[-85.611187,41.951686]},"n185958844":{"id":"n185958844","loc":[-85.611087,41.951741]},"n185958845":{"id":"n185958845","loc":[-85.611034,41.951852]},"n185958847":{"id":"n185958847","loc":[-85.611016,41.95196]},"n185958849":{"id":"n185958849","loc":[-85.610989,41.95328]},"n185958851":{"id":"n185958851","loc":[-85.611021,41.953484]},"n185958852":{"id":"n185958852","loc":[-85.611091,41.953603]},"n185958853":{"id":"n185958853","loc":[-85.6112,41.953661]},"n185958855":{"id":"n185958855","loc":[-85.611364,41.953686]},"n185965031":{"id":"n185965031","loc":[-85.614204,41.953696]},"n185965032":{"id":"n185965032","loc":[-85.6142,41.953978]},"n185965062":{"id":"n185965062","loc":[-85.614617,41.951639]},"n185965064":{"id":"n185965064","loc":[-85.61463,41.951852]},"n185965066":{"id":"n185965066","loc":[-85.614642,41.953436]},"n185965068":{"id":"n185965068","loc":[-85.6146,41.953551]},"n185965071":{"id":"n185965071","loc":[-85.614487,41.95363]},"n185965073":{"id":"n185965073","loc":[-85.614354,41.953672]},"n185966288":{"id":"n185966288","loc":[-85.61179,41.953695]},"n185966290":{"id":"n185966290","loc":[-85.612232,41.953685]},"n185966293":{"id":"n185966293","loc":[-85.613438,41.953677]},"n185966349":{"id":"n185966349","loc":[-85.611323,41.951653]},"n185966351":{"id":"n185966351","loc":[-85.611892,41.951642]},"n185966352":{"id":"n185966352","loc":[-85.612216,41.951641]},"n185966353":{"id":"n185966353","loc":[-85.613111,41.951639]},"n185966354":{"id":"n185966354","loc":[-85.613396,41.95164]},"n185966355":{"id":"n185966355","loc":[-85.614221,41.95164]},"n185973839":{"id":"n185973839","loc":[-85.61341,41.951919]},"n185973840":{"id":"n185973840","loc":[-85.613438,41.953308]},"n185980222":{"id":"n185980222","loc":[-85.613781,41.955164]},"n185980223":{"id":"n185980223","loc":[-85.613815,41.955237]},"n185980225":{"id":"n185980225","loc":[-85.613837,41.955316]},"n185990345":{"id":"n185990345","loc":[-85.612211,41.951977]},"n185955743":{"id":"n185955743","loc":[-85.613873,41.95635]},"n185980227":{"id":"n185980227","loc":[-85.613851,41.955415]},"n185980229":{"id":"n185980229","loc":[-85.613918,41.957134]},"n394381703":{"id":"n394381703","loc":[-85.616287,41.955674]},"n394381705":{"id":"n394381705","loc":[-85.615164,41.955676]},"n394490777":{"id":"n394490777","loc":[-85.613973,41.955979]},"n394490780":{"id":"n394490780","loc":[-85.614364,41.955987]},"w17965307":{"id":"w17965307","tags":{"highway":"residential","name":"Bates Ave"},"nodes":["n185958842","n185966349","n185966351","n185966352","n185966353","n185966354","n185966355","n185965062"]},"w17967957":{"id":"w17967957","tags":{"highway":"residential","name":"Krum Ave"},"nodes":["n185966352","n185990345","n185966290"]},"w17964508":{"id":"w17964508","tags":{"highway":"residential","name":"Blossom Dr"},"nodes":["n185958842","n185958844","n185958845","n185958847","n185958849","n185958851","n185958852","n185958853","n185958855","n185958835"]},"w17964507":{"id":"w17964507","tags":{"highway":"residential","name":"Blossom Dr"},"nodes":["n185958835","n185958837","n185958839"]},"w34367080":{"id":"w34367080","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394381699","n394381706","n394381705","n394381703","n394381702","n394381704","n394381699"]},"w17965302":{"id":"w17965302","tags":{"highway":"residential","name":"Clausen Ave"},"nodes":["n185958835","n185966288","n185966290","n185966293","n185965031"]},"w17965156":{"id":"w17965156","tags":{"highway":"residential","name":"Orchard Dr"},"nodes":["n185965062","n185965064","n185965066","n185965068","n185965071","n185965073","n185965031"]},"w34369812":{"id":"w34369812","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490775","n394490777","n394490780","n394490782","n394490775"]},"w17965151":{"id":"w17965151","tags":{"highway":"residential","name":"Orchard Dr"},"nodes":["n185965031","n185965032","n185965033"]},"w17966756":{"id":"w17966756","tags":{"access":"private","highway":"service","name":"Lockport Dr"},"nodes":["n185978828","n185980222","n185980223","n185980225","n185980227","n185955743","n185980229"]},"w17966056":{"id":"w17966056","tags":{"highway":"residential","name":"Angell Ave"},"nodes":["n185966354","n185973839","n185973840","n185966293"]},"w34367079":{"id":"w34367079","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394381700","n394381701","n394381698","n394381699","n394381700"]},"n185955744":{"id":"n185955744","loc":[-85.611753,41.956208]},"n185988932":{"id":"n185988932","loc":[-85.6159,41.956336]},"n185988934":{"id":"n185988934","loc":[-85.6159158,41.9590646]},"n185988935":{"id":"n185988935","loc":[-85.6157358,41.959364],"tags":{"highway":"turning_circle"}},"n2138447007":{"id":"n2138447007","loc":[-85.6130784,41.9590689]},"n2138447008":{"id":"n2138447008","loc":[-85.6133328,41.9593805]},"n2138447003":{"id":"n2138447003","loc":[-85.610238,41.9547745]},"n2138447004":{"id":"n2138447004","loc":[-85.6102652,41.9566041]},"n2138447005":{"id":"n2138447005","loc":[-85.610325,41.9568823]},"n2138447006":{"id":"n2138447006","loc":[-85.6105644,41.9571383]},"n2138447009":{"id":"n2138447009","loc":[-85.6135946,41.959948]},"n2138447010":{"id":"n2138447010","loc":[-85.6136071,41.9629372]},"n2138447011":{"id":"n2138447011","loc":[-85.6134392,41.9633182]},"n2138447012":{"id":"n2138447012","loc":[-85.6130151,41.9636073]},"n2138447013":{"id":"n2138447013","loc":[-85.6122729,41.9637125]},"n2138447014":{"id":"n2138447014","loc":[-85.6056682,41.963752]},"w17964174":{"id":"w17964174","tags":{"access":"private","highway":"service"},"nodes":["n185955743","n185955744"]},"w17967743":{"id":"w17967743","tags":{"access":"private","highway":"service","name":"Manistee River Rd"},"nodes":["n185971574","n185988932","n185971407","n185981301","n185967987","n185988934","n185988935"]},"w203839666":{"id":"w203839666","tags":{"highway":"residential","name":"Hov Aire Drive"},"nodes":["n2138447003","n2138447004","n2138447005","n2138447006","n2138447007","n2138447008","n2138447009","n2138447010","n2138447011","n2138447012","n2138447013","n2138447014"]}}';iD.data = {"deprecated":[{"old":{"amenity":"firepit"},"replace":{"leisure":"firepit"}},{"old":{"barrier":"wire_fence"},"replace":{"barrier":"fence","fence_type":"chain"}},{"old":{"barrier":"wood_fence"},"replace":{"barrier":"fence","fence_type":"wood"}},{"old":{"highway":"ford"},"replace":{"ford":"yes"}},{"old":{"highway":"stile"},"replace":{"barrier":"stile"}},{"old":{"highway":"incline"},"replace":{"highway":"road","incline":"up"}},{"old":{"highway":"incline_steep"},"replace":{"highway":"road","incline":"up"}},{"old":{"highway":"unsurfaced"},"replace":{"highway":"road","incline":"unpaved"}},{"old":{"landuse":"wood"},"replace":{"landuse":"forest","natural":"wood"}},{"old":{"natural":"marsh"},"replace":{"natural":"wetland","wetland":"marsh"}},{"old":{"power_source":"*"},"replace":{"generator:source":"$1"}},{"old":{"power_rating":"*"},"replace":{"generator:output":"$1"}},{"old":{"shop":"organic"},"replace":{"shop":"supermarket","organic":"only"}}],"discarded":["created_by","odbl","odbl:note","tiger:upload_uuid","tiger:tlid","tiger:source","tiger:separated","geobase:datasetName","geobase:uuid","sub_sea:type","KSJ2:ADS","KSJ2:ARE","KSJ2:AdminArea","KSJ2:COP_label","KSJ2:DFD","KSJ2:INT","KSJ2:INT_label","KSJ2:LOC","KSJ2:LPN","KSJ2:OPC","KSJ2:PubFacAdmin","KSJ2:RAC","KSJ2:RAC_label","KSJ2:RIC","KSJ2:RIN","KSJ2:WSC","KSJ2:coordinate","KSJ2:curve_id","KSJ2:curve_type","KSJ2:filename","KSJ2:lake_id","KSJ2:lat","KSJ2:long","KSJ2:river_id","yh:LINE_NAME","yh:LINE_NUM","yh:STRUCTURE","yh:TOTYUMONO","yh:TYPE","yh:WIDTH","yh:WIDTH_RANK","SK53_bulk:load"],"wikipedia":[["Abkhazian","Аҧсшәа","ab"],["Achinese","Acèh","ace"],["Afrikaans","Afrikaans","af"],["Akan","Akan","ak"],["Alemannisch","Alemannisch","als"],["Amharic","አማርኛ","am"],["Aragonese","aragonés","an"],["Old English","Ænglisc","ang"],["Arabic","العربية","ar"],["Aramaic","ܐܪܡܝܐ","arc"],["Egyptian Arabic","مصرى","arz"],["Assamese","অসমীয়া","as"],["Asturian","asturianu","ast"],["Avaric","авар","av"],["Aymara","Aymar aru","ay"],["Azerbaijani","azərbaycanca","az"],["South Azerbaijani","تۆرکجه","azb"],["Bashkir","башҡортса","ba"],["Bavarian","Boarisch","bar"],["Samogitian","žemaitėška","bat-smg"],["Bikol Central","Bikol Central","bcl"],["Belarusian","беларуская","be"],["беларуская (тарашкевіца)","беларуская (тарашкевіца)","be-x-old"],["Bulgarian","български","bg"],["भोजपुरी","भोजपुरी","bh"],["Bislama","Bislama","bi"],["Banjar","Bahasa Banjar","bjn"],["Bambara","bamanankan","bm"],["Bengali","বাংলা","bn"],["Tibetan","བོད་ཡིག","bo"],["Bishnupriya","বিষ্ণুপ্রিয়া মণিপুরী","bpy"],["Breton","brezhoneg","br"],["Bosnian","bosanski","bs"],["Buginese","ᨅᨔ ᨕᨘᨁᨗ","bug"],["буряад","буряад","bxr"],["Catalan","català","ca"],["Chavacano de Zamboanga","Chavacano de Zamboanga","cbk-zam"],["Min Dong Chinese","Mìng-dĕ̤ng-ngṳ̄","cdo"],["Chechen","нохчийн","ce"],["Cebuano","Cebuano","ceb"],["Chamorro","Chamoru","ch"],["Cherokee","ᏣᎳᎩ","chr"],["Cheyenne","Tsetsêhestâhese","chy"],["Central Kurdish","کوردیی ناوەندی","ckb"],["Corsican","corsu","co"],["Cree","Nēhiyawēwin / ᓀᐦᐃᔭᐍᐏᐣ","cr"],["Crimean Turkish","qırımtatarca","crh"],["Czech","čeština","cs"],["Kashubian","kaszëbsczi","csb"],["Church Slavic","словѣньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ","cu"],["Chuvash","Чӑвашла","cv"],["Welsh","Cymraeg","cy"],["Danish","dansk","da"],["German","Deutsch","de"],["Zazaki","Zazaki","diq"],["Lower Sorbian","dolnoserbski","dsb"],["Divehi","ދިވެހިބަސް","dv"],["Dzongkha","ཇོང་ཁ","dz"],["Ewe","eʋegbe","ee"],["Greek","Ελληνικά","el"],["Emiliano-Romagnolo","emiliàn e rumagnòl","eml"],["English","English","en"],["Esperanto","Esperanto","eo"],["Spanish","español","es"],["Estonian","eesti","et"],["Basque","euskara","eu"],["Extremaduran","estremeñu","ext"],["Persian","فارسی","fa"],["Fulah","Fulfulde","ff"],["Finnish","suomi","fi"],["Võro","Võro","fiu-vro"],["Fijian","Na Vosa Vakaviti","fj"],["Faroese","føroyskt","fo"],["French","français","fr"],["Arpitan","arpetan","frp"],["Northern Frisian","Nordfriisk","frr"],["Friulian","furlan","fur"],["Western Frisian","Frysk","fy"],["Irish","Gaeilge","ga"],["Gagauz","Gagauz","gag"],["Gan Chinese","贛語","gan"],["Scottish Gaelic","Gàidhlig","gd"],["Galician","galego","gl"],["Gilaki","گیلکی","glk"],["Guarani","Avañe'ẽ","gn"],["Goan Konkani","गोवा कोंकणी / Gova Konknni","gom"],["Gothic","𐌲𐌿𐍄𐌹𐍃𐌺","got"],["Gujarati","ગુજરાતી","gu"],["Manx","Gaelg","gv"],["Hausa","Hausa","ha"],["Hakka Chinese","客家語/Hak-kâ-ngî","hak"],["Hawaiian","Hawai`i","haw"],["Hebrew","עברית","he"],["Hindi","हिन्दी","hi"],["Fiji Hindi","Fiji Hindi","hif"],["Croatian","hrvatski","hr"],["Upper Sorbian","hornjoserbsce","hsb"],["Haitian","Kreyòl ayisyen","ht"],["Hungarian","magyar","hu"],["Armenian","Հայերեն","hy"],["Interlingua","interlingua","ia"],["Indonesian","Bahasa Indonesia","id"],["Interlingue","Interlingue","ie"],["Igbo","Igbo","ig"],["Inupiaq","Iñupiak","ik"],["Iloko","Ilokano","ilo"],["Ido","Ido","io"],["Icelandic","íslenska","is"],["Italian","italiano","it"],["Inuktitut","ᐃᓄᒃᑎᑐᑦ/inuktitut","iu"],["Japanese","日本語","ja"],["Lojban","Lojban","jbo"],["Javanese","Basa Jawa","jv"],["Georgian","ქართული","ka"],["Kara-Kalpak","Qaraqalpaqsha","kaa"],["Kabyle","Taqbaylit","kab"],["Kabardian","Адыгэбзэ","kbd"],["Kongo","Kongo","kg"],["Kikuyu","Gĩkũyũ","ki"],["Kazakh","қазақша","kk"],["Kalaallisut","kalaallisut","kl"],["Khmer","ភាសាខ្មែរ","km"],["Kannada","ಕನ್ನಡ","kn"],["Korean","한국어","ko"],["Komi-Permyak","Перем Коми","koi"],["Karachay-Balkar","къарачай-малкъар","krc"],["Kashmiri","कॉशुर / کٲشُر","ks"],["Colognian","Ripoarisch","ksh"],["Kurdish","Kurdî","ku"],["Komi","коми","kv"],["Cornish","kernowek","kw"],["Kyrgyz","Кыргызча","ky"],["Latin","Latina","la"],["Ladino","Ladino","lad"],["Luxembourgish","Lëtzebuergesch","lb"],["лакку","лакку","lbe"],["Lezghian","лезги","lez"],["Ganda","Luganda","lg"],["Limburgish","Limburgs","li"],["Ligurian","Ligure","lij"],["Lombard","lumbaart","lmo"],["Lingala","lingála","ln"],["Lao","ລາວ","lo"],["Northern Luri","لۊری شومالی","lrc"],["Lithuanian","lietuvių","lt"],["Latgalian","latgaļu","ltg"],["Latvian","latviešu","lv"],["Maithili","मैथिली","mai"],["Basa Banyumasan","Basa Banyumasan","map-bms"],["Moksha","мокшень","mdf"],["Malagasy","Malagasy","mg"],["Eastern Mari","олык марий","mhr"],["Maori","Māori","mi"],["Minangkabau","Baso Minangkabau","min"],["Macedonian","македонски","mk"],["Malayalam","മലയാളം","ml"],["Mongolian","монгол","mn"],["Marathi","मराठी","mr"],["Western Mari","кырык мары","mrj"],["Malay","Bahasa Melayu","ms"],["Maltese","Malti","mt"],["Mirandese","Mirandés","mwl"],["Burmese","မြန်မာဘာသာ","my"],["Erzya","эрзянь","myv"],["Mazanderani","مازِرونی","mzn"],["Nauru","Dorerin Naoero","na"],["Nāhuatl","Nāhuatl","nah"],["Neapolitan","Napulitano","nap"],["Low German","Plattdüütsch","nds"],["Low Saxon (Netherlands)","Nedersaksies","nds-nl"],["Nepali","नेपाली","ne"],["Newari","नेपाल भाषा","new"],["Dutch","Nederlands","nl"],["Norwegian Nynorsk","norsk nynorsk","nn"],["Norwegian","norsk bokmål","no"],["Novial","Novial","nov"],["Nouormand","Nouormand","nrm"],["Northern Sotho","Sesotho sa Leboa","nso"],["Navajo","Diné bizaad","nv"],["Nyanja","Chi-Chewa","ny"],["Occitan","occitan","oc"],["Oromo","Oromoo","om"],["Oriya","ଓଡ଼ିଆ","or"],["Ossetic","Ирон","os"],["Punjabi","ਪੰਜਾਬੀ","pa"],["Pangasinan","Pangasinan","pag"],["Pampanga","Kapampangan","pam"],["Papiamento","Papiamentu","pap"],["Picard","Picard","pcd"],["Pennsylvania German","Deitsch","pdc"],["Palatine German","Pälzisch","pfl"],["Pali","पालि","pi"],["Norfuk / Pitkern","Norfuk / Pitkern","pih"],["Polish","polski","pl"],["Piedmontese","Piemontèis","pms"],["Western Punjabi","پنجابی","pnb"],["Pontic","Ποντιακά","pnt"],["Pashto","پښتو","ps"],["Portuguese","português","pt"],["Quechua","Runa Simi","qu"],["Romansh","rumantsch","rm"],["Romani","Romani","rmy"],["Rundi","Kirundi","rn"],["Romanian","română","ro"],["Aromanian","armãneashti","roa-rup"],["tarandíne","tarandíne","roa-tara"],["Russian","русский","ru"],["Rusyn","русиньскый","rue"],["Kinyarwanda","Kinyarwanda","rw"],["Sanskrit","संस्कृतम्","sa"],["Sakha","саха тыла","sah"],["Sardinian","sardu","sc"],["Sicilian","sicilianu","scn"],["Scots","Scots","sco"],["Sindhi","سنڌي","sd"],["Northern Sami","sámegiella","se"],["Sango","Sängö","sg"],["Serbo-Croatian","srpskohrvatski / српскохрватски","sh"],["Sinhala","සිංහල","si"],["Simple English","Simple English","simple"],["Slovak","slovenčina","sk"],["Slovenian","slovenščina","sl"],["Samoan","Gagana Samoa","sm"],["Shona","chiShona","sn"],["Somali","Soomaaliga","so"],["Albanian","shqip","sq"],["Serbian","српски / srpski","sr"],["Sranan Tongo","Sranantongo","srn"],["Swati","SiSwati","ss"],["Southern Sotho","Sesotho","st"],["Saterland Frisian","Seeltersk","stq"],["Sundanese","Basa Sunda","su"],["Swedish","svenska","sv"],["Swahili","Kiswahili","sw"],["Silesian","ślůnski","szl"],["Tamil","தமிழ்","ta"],["Telugu","తెలుగు","te"],["Tetum","tetun","tet"],["Tajik","тоҷикӣ","tg"],["Thai","ไทย","th"],["Tigrinya","ትግርኛ","ti"],["Turkmen","Türkmençe","tk"],["Tagalog","Tagalog","tl"],["Tswana","Setswana","tn"],["Tongan","lea faka-Tonga","to"],["Tok Pisin","Tok Pisin","tpi"],["Turkish","Türkçe","tr"],["Tsonga","Xitsonga","ts"],["Tatar","татарча/tatarça","tt"],["Tumbuka","chiTumbuka","tum"],["Twi","Twi","tw"],["Tahitian","reo tahiti","ty"],["Tuvinian","тыва дыл","tyv"],["Udmurt","удмурт","udm"],["Uyghur","ئۇيغۇرچە / Uyghurche","ug"],["Ukrainian","українська","uk"],["Urdu","اردو","ur"],["Uzbek","oʻzbekcha/ўзбекча","uz"],["Venda","Tshivenda","ve"],["Venetian","vèneto","vec"],["Veps","vepsän kel’","vep"],["Vietnamese","Tiếng Việt","vi"],["West Flemish","West-Vlams","vls"],["Volapük","Volapük","vo"],["Walloon","walon","wa"],["Waray","Winaray","war"],["Wolof","Wolof","wo"],["Wu Chinese","吴语","wuu"],["Kalmyk","хальмг","xal"],["Xhosa","isiXhosa","xh"],["Mingrelian","მარგალური","xmf"],["Yiddish","ייִדיש","yi"],["Yoruba","Yorùbá","yo"],["Zhuang","Vahcuengh","za"],["Zeelandic","Zeêuws","zea"],["Chinese","中文","zh"],["Classical Chinese","文言","zh-classical"],["Chinese (Min Nan)","Bân-lâm-gú","zh-min-nan"],["Cantonese","粵語","zh-yue"],["Zulu","isiZulu","zu"]],"imperial":{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"id":0},"geometry":{"type":"MultiPolygon","coordinates":[[[[-1.426496,50.639342],[-1.445953,50.648139],[-1.452789,50.654283],[-1.485951,50.669338],[-1.497426,50.672309],[-1.535146,50.669379],[-1.551503,50.665107],[-1.569488,50.658026],[-1.545318,50.686103],[-1.50593,50.707709],[-1.418691,50.733791],[-1.420888,50.730455],[-1.423451,50.7237],[-1.425364,50.72012],[-1.400868,50.721991],[-1.377553,50.734198],[-1.343495,50.761054],[-1.318512,50.772162],[-1.295766,50.773179],[-1.144276,50.733791],[-1.119537,50.734198],[-1.10912,50.732856],[-1.097035,50.726955],[-1.096425,50.724433],[-1.097646,50.71601],[-1.097035,50.713324],[-1.094228,50.712633],[-1.085561,50.714016],[-1.082753,50.713324],[-1.062327,50.692816],[-1.062327,50.685289],[-1.066965,50.685248],[-1.069651,50.683498],[-1.071889,50.680976],[-1.075307,50.678534],[-1.112701,50.671454],[-1.128651,50.666449],[-1.156361,50.650784],[-1.162221,50.645982],[-1.164703,50.640937],[-1.164666,50.639543],[-1.426496,50.639342]]],[[[-7.240314,55.050389],[-7.013736,55.1615],[-6.958913,55.20349],[-6.571562,55.268366],[-6.509633,55.31398],[-6.226158,55.344406],[-6.07105,55.25001],[-5.712696,55.017635],[-5.242021,54.415204],[-5.695554,54.14284],[-5.72473,54.07455],[-6.041633,54.006238],[-6.153953,54.054931],[-6.220539,54.098803],[-6.242502,54.099758],[-6.263661,54.104682],[-6.269887,54.097927],[-6.28465,54.105226],[-6.299585,54.104037],[-6.313796,54.099696],[-6.327128,54.097888],[-6.338962,54.102952],[-6.346662,54.109877],[-6.354827,54.110652],[-6.368108,54.097319],[-6.369348,54.091118],[-6.367643,54.083418],[-6.366919,54.075098],[-6.371157,54.066778],[-6.377513,54.063264],[-6.401026,54.060887],[-6.426761,54.05541],[-6.433892,54.055306],[-6.4403,54.057993],[-6.446243,54.062438],[-6.450222,54.066675],[-6.450894,54.068432],[-6.47854,54.067709],[-6.564013,54.04895],[-6.571868,54.049519],[-6.587164,54.053343],[-6.595071,54.052412],[-6.60029,54.04895],[-6.605217,54.044475],[-6.610987,54.039235],[-6.616465,54.037271],[-6.630624,54.041819],[-6.657289,54.061146],[-6.672534,54.068432],[-6.657082,54.091945],[-6.655791,54.103314],[-6.666436,54.114786],[-6.643957,54.131839],[-6.634552,54.150133],[-6.640339,54.168013],[-6.648448,54.173665],[-6.663025,54.183826],[-6.683954,54.194368],[-6.694651,54.197985],[-6.706537,54.198915],[-6.717234,54.195143],[-6.724779,54.188631],[-6.73284,54.183567],[-6.744777,54.184187],[-6.766481,54.192352],[-6.787824,54.202998],[-6.807358,54.21633],[-6.823946,54.23235],[-6.829733,54.242375],[-6.833196,54.25209],[-6.837743,54.260513],[-6.846683,54.266456],[-6.882185,54.277257],[-6.864667,54.282734],[-6.856657,54.292811],[-6.858414,54.307332],[-6.870015,54.326001],[-6.879705,54.341594],[-6.885957,54.345624],[-6.897895,54.346193],[-6.905956,54.349035],[-6.915051,54.365933],[-6.922028,54.372703],[-6.984091,54.403089],[-7.017836,54.413166],[-7.049255,54.411512],[-7.078504,54.394717],[-7.127028,54.349759],[-7.159894,54.335186],[-7.168059,54.335031],[-7.185629,54.336943],[-7.18947,54.335692],[-7.19245,54.334721],[-7.193949,54.329967],[-7.191468,54.323869],[-7.187644,54.318804],[-7.185009,54.317254],[-7.184647,54.316634],[-7.192399,54.307384],[-7.193691,54.307539],[-7.199168,54.303457],[-7.206661,54.304903],[-7.211467,54.30418],[-7.209038,54.293431],[-7.1755,54.283664],[-7.181495,54.269763],[-7.14589,54.25209],[-7.159739,54.24067],[-7.153331,54.224237],[-7.174725,54.216072],[-7.229502,54.207545],[-7.240871,54.202326],[-7.249088,54.197416],[-7.255496,54.190854],[-7.261128,54.18088],[-7.256322,54.176901],[-7.247021,54.17225],[-7.24578,54.166979],[-7.265366,54.16114],[-7.26087,54.151166],[-7.263505,54.140986],[-7.27074,54.132253],[-7.280042,54.126155],[-7.293788,54.122021],[-7.297353,54.125896],[-7.29632,54.134991],[-7.296423,54.146515],[-7.295028,54.155404],[-7.292134,54.162638],[-7.295545,54.165119],[-7.325982,54.154577],[-7.333165,54.149409],[-7.333165,54.142743],[-7.310324,54.114683],[-7.316489,54.11428],[-7.326964,54.113597],[-7.375488,54.123312],[-7.390216,54.121194],[-7.39466,54.121917],[-7.396624,54.126258],[-7.403962,54.135043],[-7.41223,54.136438],[-7.422255,54.135456],[-7.425769,54.136955],[-7.414659,54.145688],[-7.439619,54.146929],[-7.480753,54.127653],[-7.502302,54.125121],[-7.609014,54.139901],[-7.620796,54.144965],[-7.624052,54.153336],[-7.625706,54.162173],[-7.632682,54.168529],[-7.70477,54.200362],[-7.722599,54.202326],[-7.782078,54.2],[-7.836959,54.204341],[-7.856441,54.211421],[-7.86967,54.226872],[-7.873649,54.271055],[-7.880264,54.287023],[-7.894966,54.293586],[-7.93411,54.297049],[-7.942075,54.298873],[-7.950802,54.300873],[-7.96801,54.31219],[-7.981033,54.326556],[-8.002194,54.357923],[-8.03134,54.358027],[-8.05648,54.365882],[-8.079941,54.380196],[-8.122419,54.415233],[-8.146346,54.430736],[-8.156035,54.439055],[-8.158128,54.447117],[-8.161177,54.454817],[-8.173837,54.461741],[-8.168467,54.463477],[-8.15017,54.46939],[-8.097046,54.478588],[-8.072448,54.487063],[-8.060976,54.493316],[-8.05586,54.497553],[-8.043561,54.512229],[-8.023278,54.529696],[-8.002194,54.543442],[-7.926411,54.533055],[-7.887137,54.532125],[-7.848844,54.54091],[-7.749264,54.596152],[-7.707871,54.604162],[-7.707944,54.604708],[-7.707951,54.604763],[-7.710558,54.624264],[-7.721204,54.625866],[-7.736758,54.619251],[-7.753553,54.614497],[-7.769159,54.618011],[-7.801199,54.634806],[-7.814996,54.639457],[-7.822541,54.638113],[-7.838044,54.63124],[-7.846416,54.631447],[-7.85427,54.636408],[-7.864347,54.649069],[-7.872771,54.652221],[-7.890082,54.655063],[-7.906619,54.661316],[-7.914835,54.671651],[-7.907135,54.686689],[-7.913233,54.688653],[-7.929666,54.696714],[-7.880109,54.711029],[-7.845899,54.731027],[-7.832153,54.730614],[-7.803576,54.716145],[-7.770503,54.706016],[-7.736603,54.707463],[-7.70229,54.718883],[-7.667512,54.738779],[-7.649683,54.744877],[-7.61537,54.739347],[-7.585398,54.744722],[-7.566639,54.738675],[-7.556149,54.738365],[-7.543075,54.741673],[-7.543023,54.743791],[-7.548398,54.747202],[-7.551705,54.754695],[-7.549741,54.779603],[-7.543385,54.793091],[-7.470831,54.845284],[-7.45507,54.863009],[-7.444735,54.884455],[-7.444735,54.894893],[-7.448972,54.920318],[-7.445251,54.932152],[-7.436983,54.938301],[-7.417139,54.943056],[-7.415755,54.944372],[-7.408665,54.951117],[-7.407424,54.959437],[-7.413109,54.984965],[-7.409078,54.992045],[-7.403755,54.99313],[-7.40112,54.994836],[-7.405254,55.003569],[-7.376987,55.02889],[-7.366962,55.035557],[-7.355024,55.040931],[-7.291152,55.046615],[-7.282987,55.051835],[-7.275288,55.058863],[-7.266503,55.065167],[-7.247097,55.069328],[-7.2471,55.069322],[-7.256744,55.050686],[-7.240956,55.050279],[-7.240314,55.050389]]],[[[-13.688588,57.596259],[-13.690419,57.596259],[-13.691314,57.596503],[-13.691314,57.597154],[-13.690419,57.597805],[-13.688588,57.597805],[-13.687652,57.597154],[-13.687652,57.596869],[-13.688588,57.596259]]],[[[-4.839121,54.469789],[-4.979941,54.457977],[-5.343644,54.878637],[-5.308469,55.176452],[-6.272566,55.418443],[-8.690528,57.833706],[-6.344705,59.061083],[-4.204785,58.63305],[-2.31566,60.699068],[-1.695335,60.76432],[-1.58092,60.866001],[-0.17022,60.897204],[-0.800508,59.770037],[-1.292368,57.732574],[-1.850077,55.766368],[-1.73054,55.782219],[1.892395,52.815229],[1.742775,51.364209],[1.080173,50.847526],[0.000774,50.664982],[-0.162997,50.752401],[-0.725152,50.731879],[-0.768853,50.741516],[-0.770985,50.736884],[-0.789947,50.730048],[-0.812815,50.734768],[-0.877742,50.761156],[-0.942879,50.758338],[-0.992581,50.737379],[-1.18513,50.766989],[-1.282741,50.792353],[-1.375004,50.772063],[-1.523427,50.719605],[-1.630649,50.695128],[-1.663617,50.670508],[-1.498021,50.40831],[-4.097427,49.735486],[-6.825199,49.700905],[-5.541541,51.446591],[-6.03361,51.732369],[-4.791746,52.635365],[-4.969244,52.637413],[-5.049473,53.131209],[-4.787393,53.409491],[-4.734148,53.424866],[-4.917096,53.508212],[-4.839121,54.469789]]]]}},{"type":"Feature","properties":{"id":0},"geometry":{"type":"MultiPolygon","coordinates":[[[[-157.018938,19.300864],[-179.437336,27.295312],[-179.480084,28.991459],[-168.707465,26.30325],[-163.107414,24.60499],[-153.841679,20.079306],[-154.233846,19.433391],[-153.61725,18.900587],[-154.429471,18.171036],[-156.780638,18.718492],[-157.018938,19.300864]]],[[[-78.91269,43.037032],[-78.964351,42.976393],[-78.981718,42.979043],[-78.998055,42.991111],[-79.01189,43.004358],[-79.022046,43.010539],[-79.023076,43.017015],[-79.00983,43.050867],[-79.011449,43.065291],[-78.993051,43.066174],[-78.975536,43.069707],[-78.958905,43.070884],[-78.943304,43.065291],[-78.917399,43.058521],[-78.908569,43.049396],[-78.91269,43.037032]]],[[[-123.03529,48.992515],[-123.035308,48.992499],[-123.045277,48.984361],[-123.08849,48.972235],[-123.089345,48.987982],[-123.090484,48.992499],[-123.090488,48.992515],[-123.035306,48.992515],[-123.03529,48.992515]]],[[[-103.837038,29.279906],[-103.864121,29.281366],[-103.928122,29.293019],[-104.01915,29.32033],[-104.057313,29.339037],[-104.105424,29.385675],[-104.139789,29.400584],[-104.161648,29.416759],[-104.194514,29.448927],[-104.212291,29.484661],[-104.218698,29.489829],[-104.227148,29.493033],[-104.251022,29.508588],[-104.267171,29.526571],[-104.292751,29.532824],[-104.320604,29.532255],[-104.338484,29.524013],[-104.349026,29.537578],[-104.430443,29.582795],[-104.437832,29.58543],[-104.444008,29.589203],[-104.448555,29.597678],[-104.452069,29.607109],[-104.455222,29.613387],[-104.469381,29.625402],[-104.516639,29.654315],[-104.530824,29.667906],[-104.535036,29.677802],[-104.535191,29.687853],[-104.537103,29.702116],[-104.543666,29.71643],[-104.561391,29.745421],[-104.570279,29.787511],[-104.583586,29.802575],[-104.601207,29.81477],[-104.619682,29.833064],[-104.623764,29.841487],[-104.637588,29.887996],[-104.656346,29.908201],[-104.660635,29.918433],[-104.663478,29.923084],[-104.676526,29.93683],[-104.680479,29.942308],[-104.682469,29.952126],[-104.680117,29.967784],[-104.680479,29.976466],[-104.699108,30.03145],[-104.701589,30.055324],[-104.698592,30.075271],[-104.684639,30.111135],[-104.680479,30.134131],[-104.67867,30.170356],[-104.681564,30.192939],[-104.695853,30.208441],[-104.715231,30.243995],[-104.724585,30.252211],[-104.742155,30.25986],[-104.74939,30.264459],[-104.761689,30.284199],[-104.774143,30.311588],[-104.788767,30.335927],[-104.807732,30.346418],[-104.8129,30.350707],[-104.814967,30.360577],[-104.816001,30.371997],[-104.818274,30.380524],[-104.824269,30.38719],[-104.83755,30.394063],[-104.844939,30.40104],[-104.853259,30.41215],[-104.855016,30.417473],[-104.853621,30.423984],[-104.852432,30.438867],[-104.854655,30.448737],[-104.864473,30.462018],[-104.866695,30.473025],[-104.865248,30.479898],[-104.859615,30.491112],[-104.859254,30.497261],[-104.863026,30.502377],[-104.879718,30.510852],[-104.882146,30.520929],[-104.884007,30.541858],[-104.886591,30.551883],[-104.898166,30.569401],[-104.928242,30.599529],[-104.93434,30.610536],[-104.941057,30.61405],[-104.972735,30.618029],[-104.98276,30.620716],[-104.989117,30.629553],[-104.991649,30.640301],[-104.992941,30.651464],[-104.995783,30.661747],[-105.008495,30.676992],[-105.027977,30.690117],[-105.049475,30.699264],[-105.06813,30.702675],[-105.087043,30.709806],[-105.133604,30.757917],[-105.140425,30.750476],[-105.153241,30.763188],[-105.157788,30.76572],[-105.160889,30.764118],[-105.162698,30.774919],[-105.167297,30.781171],[-105.17479,30.783962],[-105.185125,30.784634],[-105.195306,30.787941],[-105.204917,30.80241],[-105.2121,30.805718],[-105.21825,30.806803],[-105.229257,30.810214],[-105.232874,30.809128],[-105.239851,30.801532],[-105.243985,30.799103],[-105.249049,30.798845],[-105.259488,30.802979],[-105.265844,30.808405],[-105.270753,30.814348],[-105.277006,30.819412],[-105.334315,30.843803],[-105.363771,30.850366],[-105.376173,30.859565],[-105.41555,30.902456],[-105.496682,30.95651],[-105.530789,30.991701],[-105.555955,31.002605],[-105.565722,31.016661],[-105.578641,31.052163],[-105.59094,31.071438],[-105.605875,31.081928],[-105.623496,31.090351],[-105.643805,31.103684],[-105.668042,31.127869],[-105.675225,31.131951],[-105.692278,31.137635],[-105.76819,31.18001],[-105.777854,31.192722],[-105.78483,31.211016],[-105.861983,31.288376],[-105.880147,31.300881],[-105.896994,31.305997],[-105.897149,31.309511],[-105.908802,31.317004],[-105.928052,31.326461],[-105.934563,31.335504],[-105.941772,31.352351],[-105.948515,31.361239],[-105.961202,31.371006],[-106.004739,31.396948],[-106.021147,31.402167],[-106.046261,31.404648],[-106.065304,31.410952],[-106.099385,31.428884],[-106.141113,31.439167],[-106.164316,31.447797],[-106.174471,31.460251],[-106.209249,31.477305],[-106.215424,31.483919],[-106.21744,31.488725],[-106.218731,31.494616],[-106.222891,31.50459],[-106.232658,31.519938],[-106.274749,31.562622],[-106.286298,31.580141],[-106.312292,31.648612],[-106.331309,31.68215],[-106.35849,31.717548],[-106.39177,31.745919],[-106.428951,31.758476],[-106.473135,31.755065],[-106.492797,31.759044],[-106.501425,31.766344],[-106.506052,31.770258],[-106.517189,31.773824],[-106.558969,31.773876],[-106.584859,31.773927],[-106.610697,31.773979],[-106.636587,31.774082],[-106.662477,31.774134],[-106.688315,31.774237],[-106.714205,31.774237],[-106.740095,31.774289],[-106.765933,31.774392],[-106.791823,31.774444],[-106.817713,31.774496],[-106.843603,31.774547],[-106.869441,31.774599],[-106.895331,31.774702],[-106.921221,31.774702],[-106.947111,31.774754],[-106.973001,31.774857],[-106.998891,31.774909],[-107.02478,31.774961],[-107.05067,31.775013],[-107.076509,31.775064],[-107.102398,31.775168],[-107.128288,31.775168],[-107.154127,31.775219],[-107.180016,31.775374],[-107.205906,31.775374],[-107.231796,31.775426],[-107.257634,31.775478],[-107.283524,31.775529],[-107.309414,31.775633],[-107.335252,31.775684],[-107.361142,31.775788],[-107.387032,31.775788],[-107.412896,31.775839],[-107.438786,31.775943],[-107.464676,31.775994],[-107.490566,31.776098],[-107.516404,31.776149],[-107.542294,31.776201],[-107.568184,31.776253],[-107.594074,31.776304],[-107.619964,31.776408],[-107.645854,31.776459],[-107.671744,31.776459],[-107.697633,31.776563],[-107.723472,31.776614],[-107.749362,31.776666],[-107.775251,31.776718],[-107.801141,31.77677],[-107.82698,31.776873],[-107.852869,31.776925],[-107.878759,31.776925],[-107.904598,31.777028],[-107.930487,31.77708],[-107.956377,31.777131],[-107.982216,31.777183],[-108.008105,31.777235],[-108.033995,31.777338],[-108.059885,31.77739],[-108.085723,31.77739],[-108.111613,31.777545],[-108.137503,31.777545],[-108.163341,31.777648],[-108.189283,31.7777],[-108.215121,31.777751],[-108.215121,31.770723],[-108.215121,31.763695],[-108.215121,31.756667],[-108.215121,31.749639],[-108.215121,31.74256],[-108.215121,31.735583],[-108.215121,31.728555],[-108.215121,31.721476],[-108.215121,31.714396],[-108.215121,31.70742],[-108.215121,31.700392],[-108.215121,31.693312],[-108.215121,31.686284],[-108.215121,31.679256],[-108.215121,31.672176],[-108.21507,31.665148],[-108.215018,31.658172],[-108.215018,31.651092],[-108.215018,31.644064],[-108.215018,31.637036],[-108.215018,31.630008],[-108.215018,31.62298],[-108.215018,31.615952],[-108.215018,31.608873],[-108.215018,31.601845],[-108.215018,31.594817],[-108.215018,31.587789],[-108.215018,31.580761],[-108.215018,31.573733],[-108.215018,31.566653],[-108.215018,31.559625],[-108.214966,31.552597],[-108.214966,31.545569],[-108.214966,31.538489],[-108.214966,31.531461],[-108.214966,31.524485],[-108.214966,31.517405],[-108.214966,31.510378],[-108.214966,31.503401],[-108.214966,31.496322],[-108.214966,31.489242],[-108.214966,31.482214],[-108.214966,31.475238],[-108.214966,31.468158],[-108.214966,31.46113],[-108.214966,31.454102],[-108.214966,31.447074],[-108.214915,31.440046],[-108.214863,31.432966],[-108.214863,31.425938],[-108.214863,31.41891],[-108.214863,31.411882],[-108.214863,31.404803],[-108.214863,31.397826],[-108.214863,31.390798],[-108.214863,31.383719],[-108.214863,31.376639],[-108.214863,31.369663],[-108.214863,31.362635],[-108.214863,31.355555],[-108.214863,31.348527],[-108.214863,31.341551],[-108.214863,31.334471],[-108.214811,31.327443],[-108.257573,31.327391],[-108.300336,31.327391],[-108.34302,31.327391],[-108.385731,31.327391],[-108.428442,31.327391],[-108.471152,31.327391],[-108.513837,31.327391],[-108.556547,31.327391],[-108.59931,31.327391],[-108.64202,31.327391],[-108.684757,31.327391],[-108.727467,31.327391],[-108.770178,31.327391],[-108.812914,31.327391],[-108.855625,31.327391],[-108.898335,31.327391],[-108.941046,31.327391],[-108.968282,31.327391],[-108.983731,31.327391],[-109.026493,31.327391],[-109.04743,31.327391],[-109.069203,31.327391],[-109.111914,31.327391],[-109.154599,31.327391],[-109.197361,31.327391],[-109.240072,31.32734],[-109.282782,31.32734],[-109.325519,31.32734],[-109.368229,31.32734],[-109.410914,31.32734],[-109.45365,31.32734],[-109.496387,31.32734],[-109.539071,31.32734],[-109.581808,31.32734],[-109.624493,31.32734],[-109.667177,31.32734],[-109.709965,31.32734],[-109.75265,31.32734],[-109.795335,31.32734],[-109.838123,31.32734],[-109.880808,31.32734],[-109.923596,31.327288],[-109.96628,31.327236],[-110.008965,31.327236],[-110.051702,31.327236],[-110.094386,31.327236],[-110.137071,31.327236],[-110.179807,31.327236],[-110.222544,31.327236],[-110.265229,31.327236],[-110.308017,31.327236],[-110.350753,31.327236],[-110.39349,31.327236],[-110.436174,31.327236],[-110.478859,31.327236],[-110.521595,31.327236],[-110.56428,31.327236],[-110.606965,31.327236],[-110.649727,31.327236],[-110.692438,31.327236],[-110.7352,31.327236],[-110.777885,31.327236],[-110.820595,31.327236],[-110.863358,31.327236],[-110.906068,31.327236],[-110.948753,31.327185],[-111.006269,31.327185],[-111.067118,31.333644],[-111.094455,31.342532],[-111.145924,31.359069],[-111.197446,31.375554],[-111.248864,31.392142],[-111.300333,31.40873],[-111.351803,31.425318],[-111.403299,31.441855],[-111.454768,31.458339],[-111.506238,31.474979],[-111.915464,31.601431],[-112.324715,31.727987],[-112.733967,31.854543],[-113.143218,31.981046],[-113.552444,32.107602],[-113.961696,32.234132],[-114.370921,32.360687],[-114.780147,32.487243],[-114.816785,32.498534],[-114.819373,32.499363],[-114.822108,32.50024],[-114.809447,32.511324],[-114.795546,32.552226],[-114.794203,32.574111],[-114.802678,32.594497],[-114.786813,32.621033],[-114.781542,32.628061],[-114.758804,32.64483],[-114.751156,32.65222],[-114.739477,32.669066],[-114.731209,32.686636],[-114.723871,32.711519],[-114.724284,32.712835],[-114.724285,32.712836],[-114.764541,32.709839],[-114.838076,32.704206],[-114.911612,32.698703],[-114.985199,32.693122],[-115.058734,32.687567],[-115.13227,32.681986],[-115.205806,32.676456],[-115.27929,32.670823],[-115.352851,32.665346],[-115.426386,32.659765],[-115.499922,32.654209],[-115.573535,32.648654],[-115.647019,32.643073],[-115.720529,32.637518],[-115.794064,32.631963],[-115.8676,32.626408],[-115.941213,32.620827],[-116.014748,32.615271],[-116.088232,32.609664],[-116.161742,32.604161],[-116.235329,32.598554],[-116.308891,32.593025],[-116.382426,32.587469],[-116.455962,32.581888],[-116.529472,32.576333],[-116.603007,32.570804],[-116.676543,32.565223],[-116.750104,32.559667],[-116.82364,32.554086],[-116.897201,32.548531],[-116.970737,32.542976],[-117.044221,32.537421],[-117.125121,32.531669],[-117.125969,32.538258],[-117.239623,32.531308],[-120.274098,32.884264],[-121.652736,34.467248],[-124.367265,37.662798],[-126.739806,41.37928],[-126.996297,45.773888],[-124.770704,48.44258],[-123.734053,48.241906],[-123.1663,48.27837],[-123.193018,48.501035],[-123.176987,48.65482],[-122.912481,48.753561],[-122.899122,48.897797],[-122.837671,48.97502],[-122.743986,48.980582],[-122.753,48.992499],[-122.753012,48.992515],[-122.653258,48.992515],[-122.433375,48.992515],[-122.213517,48.992515],[-121.993763,48.992515],[-121.773958,48.992515],[-121.554152,48.992515],[-121.33432,48.992515],[-121.114515,48.992515],[-95.396937,48.99267],[-95.177106,48.99267],[-95.168527,48.995047],[-95.161887,49.001145],[-95.159329,49.01179],[-95.159665,49.10951],[-95.160027,49.223353],[-95.160337,49.313012],[-95.160569,49.369494],[-95.102821,49.35394],[-94.982518,49.356162],[-94.926087,49.345568],[-94.856195,49.318283],[-94.839142,49.308878],[-94.827256,49.292858],[-94.819892,49.252034],[-94.810358,49.229606],[-94.806121,49.210899],[-94.811185,49.166561],[-94.803743,49.146407],[-94.792039,49.12646],[-94.753772,49.026156],[-94.711217,48.914586],[-94.711734,48.862755],[-94.712147,48.842446],[-94.713284,48.823843],[-94.710907,48.807513],[-94.701786,48.790098],[-94.688893,48.778832],[-94.592852,48.726433],[-94.519161,48.70447],[-94.4795,48.700698],[-94.311577,48.713927],[-94.292586,48.711912],[-94.284034,48.709069],[-94.274499,48.704108],[-94.265482,48.697752],[-94.258454,48.690828],[-94.255767,48.683541],[-94.252459,48.662405],[-94.251038,48.65729],[-94.23215,48.652019],[-94.03485,48.643311],[-93.874885,48.636206],[-93.835741,48.617137],[-93.809386,48.543576],[-93.778664,48.519468],[-93.756779,48.516549],[-93.616297,48.531302],[-93.599889,48.526341],[-93.566584,48.538279],[-93.491756,48.542309],[-93.459924,48.557399],[-93.45225,48.572721],[-93.453774,48.586958],[-93.451475,48.597422],[-93.417316,48.604114],[-93.385716,48.614863],[-93.25774,48.630314],[-93.131701,48.62463],[-92.97972,48.61768],[-92.955588,48.612228],[-92.884197,48.579878],[-92.72555,48.548692],[-92.648604,48.536263],[-92.630181,48.519468],[-92.627468,48.502777],[-92.646743,48.497428],[-92.691366,48.489858],[-92.710641,48.482882],[-92.718909,48.459782],[-92.704052,48.445158],[-92.677129,48.441747],[-92.657053,48.438233],[-92.570521,48.446656],[-92.526932,48.445623],[-92.490629,48.433117],[-92.474532,48.410483],[-92.467581,48.394282],[-92.467064,48.353225],[-92.462465,48.329299],[-92.451381,48.312685],[-92.41823,48.282041],[-92.38464,48.232406],[-92.371851,48.222587],[-92.353815,48.222897],[-92.327874,48.229435],[-92.303663,48.239279],[-92.291029,48.249562],[-92.292062,48.270336],[-92.301416,48.290645],[-92.303095,48.310928],[-92.281598,48.33178],[-92.259118,48.339635],[-92.154732,48.350125],[-92.070499,48.346714],[-92.043421,48.334596],[-92.030114,48.313176],[-92.021355,48.287441],[-92.007997,48.262482],[-91.992158,48.247909],[-91.975492,48.236566],[-91.957302,48.228323],[-91.852244,48.195974],[-91.764988,48.187344],[-91.744137,48.179593],[-91.727575,48.168327],[-91.695509,48.13758],[-91.716438,48.112051],[-91.692512,48.097866],[-91.618615,48.089572],[-91.597479,48.090399],[-91.589676,48.088332],[-91.581098,48.080942],[-91.579806,48.070969],[-91.585129,48.06084],[-91.586989,48.052572],[-91.574845,48.048205],[-91.487098,48.053476],[-91.464722,48.048955],[-91.446274,48.040738],[-91.427929,48.036449],[-91.3654,48.057843],[-91.276362,48.064768],[-91.23807,48.082648],[-91.203963,48.107659],[-91.071103,48.170859],[-91.02816,48.184838],[-91.008109,48.194372],[-90.923153,48.227109],[-90.873802,48.234344],[-90.840678,48.220107],[-90.837939,48.210547],[-90.848843,48.198713],[-90.849721,48.189566],[-90.843003,48.176983],[-90.83427,48.171789],[-90.823883,48.168327],[-90.812307,48.160989],[-90.803057,48.147166],[-90.796701,48.117064],[-90.786469,48.10045],[-90.750347,48.083991],[-90.701307,48.08456],[-90.611079,48.103499],[-90.586843,48.104817],[-90.573872,48.097892],[-90.562194,48.088849],[-90.542014,48.083733],[-90.531601,48.08456],[-90.501887,48.094275],[-90.490493,48.096239],[-90.483465,48.094482],[-90.477858,48.091536],[-90.470623,48.089882],[-90.178625,48.116444],[-90.120386,48.115359],[-90.073257,48.101199],[-90.061036,48.091019],[-90.008222,48.029731],[-89.995329,48.018595],[-89.980317,48.010094],[-89.92045,47.98746],[-89.902441,47.985909],[-89.803454,48.013763],[-89.780975,48.017199],[-89.763302,48.017303],[-89.745964,48.013763],[-89.724596,48.005908],[-89.712788,48.003376],[-89.678656,48.008699],[-89.65659,48.007975],[-89.593105,47.996503],[-89.581753,47.996333],[-89.586724,47.992938],[-89.310872,47.981097],[-89.072861,48.046842],[-88.49789,48.212841],[-88.286621,48.156675],[-85.939935,47.280501],[-84.784644,46.770068],[-84.516909,46.435083],[-84.489712,46.446652],[-84.491052,46.457658],[-84.478301,46.466467],[-84.465408,46.478172],[-84.448096,46.489722],[-84.42324,46.511581],[-84.389702,46.520262],[-84.352469,46.522743],[-84.30534,46.501607],[-84.242011,46.526464],[-84.197285,46.546359],[-84.147676,46.541346],[-84.110443,46.526464],[-84.158812,46.433343],[-84.147676,46.399882],[-84.129046,46.375026],[-84.10543,46.347741],[-84.105944,46.346374],[-84.117195,46.347157],[-84.117489,46.338326],[-84.122361,46.331922],[-84.112061,46.287102],[-84.092672,46.227469],[-84.111983,46.20337],[-84.015118,46.149712],[-83.957038,46.045736],[-83.676821,46.15388],[-83.429449,46.086221],[-83.523049,45.892052],[-83.574563,45.890259],[-82.551615,44.857931],[-82.655591,43.968545],[-82.440632,43.096285],[-82.460131,43.084392],[-82.458894,43.083247],[-82.431813,43.039387],[-82.424748,43.02408],[-82.417242,43.01731],[-82.416369,43.01742],[-82.416412,43.017143],[-82.414603,42.983243],[-82.430442,42.951307],[-82.453179,42.918983],[-82.464781,42.883637],[-82.468036,42.863974],[-82.482325,42.835113],[-82.485271,42.818524],[-82.473618,42.798164],[-82.470982,42.790568],[-82.471344,42.779845],[-82.476951,42.761474],[-82.48341,42.719254],[-82.511264,42.646675],[-82.526224,42.619906],[-82.549246,42.590941],[-82.575833,42.571795],[-82.608467,42.561098],[-82.644331,42.557817],[-82.644698,42.557533],[-82.644932,42.561634],[-82.637132,42.568405],[-82.60902,42.579296],[-82.616673,42.582828],[-82.636985,42.599607],[-82.625357,42.616092],[-82.629331,42.626394],[-82.638751,42.633459],[-82.644344,42.640524],[-82.644166,42.641056],[-82.716083,42.617461],[-82.777592,42.408506],[-82.888693,42.406093],[-82.889991,42.403266],[-82.905739,42.387665],[-82.923842,42.374419],[-82.937972,42.366176],[-82.947686,42.363527],[-82.979624,42.359406],[-83.042618,42.340861],[-83.061899,42.32732],[-83.081622,42.30907],[-83.11342,42.279619],[-83.145306,42.066968],[-83.177398,41.960666],[-83.21512,41.794493],[-82.219051,41.516445],[-80.345329,42.13344],[-80.316455,42.123137],[-79.270266,42.591872],[-79.221058,42.582892],[-78.871842,42.860012],[-78.875011,42.867184],[-78.896205,42.897209],[-78.901651,42.908101],[-78.90901,42.952255],[-78.913426,42.957848],[-78.932118,42.9708],[-78.936386,42.979631],[-78.927997,43.002003],[-78.893114,43.029379],[-78.887963,43.051456],[-78.914897,43.076477],[-79.026167,43.086485],[-79.065231,43.10573],[-79.065273,43.105897],[-79.065738,43.120237],[-79.061423,43.130288],[-79.055583,43.138427],[-79.051604,43.146851],[-79.04933,43.159847],[-79.048607,43.170622],[-79.053775,43.260358],[-79.058425,43.277799],[-79.058631,43.2782],[-78.990696,43.286947],[-78.862059,43.324332],[-78.767813,43.336418],[-78.516117,43.50645],[-76.363317,43.943219],[-76.396746,44.106667],[-76.364697,44.111631],[-76.366146,44.117349],[-76.357462,44.131478],[-76.183493,44.223025],[-76.162644,44.229888],[-76.176117,44.30795],[-76.046414,44.354817],[-75.928746,44.391137],[-75.852508,44.381639],[-75.849095,44.386103],[-75.847623,44.392579],[-75.84674,44.398172],[-75.845415,44.40141],[-75.780803,44.432318],[-75.770205,44.446153],[-75.772266,44.463815],[-75.779184,44.48236],[-75.791496,44.496513],[-75.791183,44.496768],[-75.754622,44.527567],[-75.69969,44.581673],[-75.578199,44.661513],[-75.455958,44.741766],[-75.341831,44.816749],[-75.270233,44.863774],[-75.129647,44.925166],[-75.075594,44.935501],[-75.058721,44.941031],[-75.0149,44.96599],[-74.998647,44.972398],[-74.940201,44.987746],[-74.903744,45.005213],[-74.88651,45.009398],[-74.868474,45.010122],[-74.741557,44.998857],[-74.712961,44.999254],[-74.695875,44.99803],[-74.596114,44.998495],[-74.496352,44.999012],[-74.197146,45.000458],[-71.703551,45.012757],[-71.603816,45.013274],[-71.505848,45.013731],[-71.50408,45.013739],[-71.506613,45.037045],[-71.504752,45.052962],[-71.497259,45.066553],[-71.45659,45.110994],[-71.451215,45.121691],[-71.445996,45.140295],[-71.441604,45.150682],[-71.413026,45.186184],[-71.406567,45.204942],[-71.42269,45.217189],[-71.449045,45.226905],[-71.438813,45.233468],[-71.394888,45.241529],[-71.381245,45.250779],[-71.3521,45.278323],[-71.334323,45.28871],[-71.311534,45.294136],[-71.293396,45.292327],[-71.20937,45.254758],[-71.185133,45.248557],[-71.160329,45.245767],[-71.141725,45.252329],[-71.111029,45.287108],[-71.095242,45.300905],[-71.085553,45.304213],[-71.084952,45.304293],[-71.064211,45.307055],[-71.054418,45.310362],[-71.036667,45.323385],[-71.027598,45.33465],[-71.016539,45.343125],[-70.993155,45.347827],[-70.968118,45.34452],[-70.951608,45.332014],[-70.906908,45.246232],[-70.892412,45.234604],[-70.874351,45.245663],[-70.870605,45.255275],[-70.872491,45.274189],[-70.870243,45.283129],[-70.862621,45.290363],[-70.842389,45.301215],[-70.835258,45.309794],[-70.83208,45.328552],[-70.835465,45.373097],[-70.833837,45.393096],[-70.825982,45.410459],[-70.812986,45.42343],[-70.794873,45.430406],[-70.771877,45.430045],[-70.75255,45.422345],[-70.718004,45.397282],[-70.696739,45.388652],[-70.675785,45.388704],[-70.65359,45.395473],[-70.641316,45.408496],[-70.650257,45.427461],[-70.668162,45.439036],[-70.707385,45.4564],[-70.722836,45.470921],[-70.732009,45.491591],[-70.730329,45.507973],[-70.686792,45.572723],[-70.589614,45.651788],[-70.572406,45.662279],[-70.514735,45.681709],[-70.484763,45.699641],[-70.4728,45.703568],[-70.450424,45.703723],[-70.439132,45.705893],[-70.419315,45.716901],[-70.407351,45.731525],[-70.402442,45.749663],[-70.403941,45.771161],[-70.408282,45.781651],[-70.413682,45.787697],[-70.41717,45.793795],[-70.415232,45.804389],[-70.409935,45.810745],[-70.389807,45.825059],[-70.312654,45.867641],[-70.283173,45.890482],[-70.262528,45.923038],[-70.255939,45.948876],[-70.263148,45.956834],[-70.280434,45.959315],[-70.303947,45.968616],[-70.316298,45.982982],[-70.316892,45.999002],[-70.306143,46.035331],[-70.303637,46.038483],[-70.294309,46.044943],[-70.29201,46.048663],[-70.293017,46.054038],[-70.296092,46.057862],[-70.300795,46.061737],[-70.304774,46.065975],[-70.311362,46.071866],[-70.312629,46.079566],[-70.30033,46.089281],[-70.26444,46.106593],[-70.24948,46.120597],[-70.244002,46.141009],[-70.249247,46.162765],[-70.263329,46.183229],[-70.284801,46.191859],[-70.280899,46.211857],[-70.253407,46.251493],[-70.236173,46.288339],[-70.223693,46.300793],[-70.201886,46.305495],[-70.199509,46.315262],[-70.197028,46.336863],[-70.188398,46.358412],[-70.167418,46.368179],[-70.153052,46.372829],[-70.074323,46.419545],[-70.061817,46.445409],[-70.050086,46.511271],[-70.032723,46.609766],[-70.023628,46.661287],[-70.007763,46.704075],[-69.989961,46.721697],[-69.899708,46.811562],[-69.809403,46.901299],[-69.719099,46.991086],[-69.628794,47.080797],[-69.538464,47.17061],[-69.448159,47.260346],[-69.357906,47.350134],[-69.267628,47.439844],[-69.25091,47.452919],[-69.237268,47.45881],[-69.221972,47.459688],[-69.069655,47.431886],[-69.054023,47.418399],[-69.054333,47.389253],[-69.066193,47.32967],[-69.065134,47.296339],[-69.06356,47.290809],[-69.057486,47.269467],[-69.0402,47.249055],[-68.906229,47.190221],[-68.889718,47.190609],[-68.761819,47.23704],[-68.71779,47.245231],[-68.668801,47.243422],[-68.644203,47.245283],[-68.6256,47.255205],[-68.607926,47.269829],[-68.58524,47.28249],[-68.539662,47.299853],[-68.518009,47.304762],[-68.492016,47.307553],[-68.466746,47.305692],[-68.435327,47.291275],[-68.422563,47.293109],[-68.410212,47.297424],[-68.385614,47.301713],[-68.383392,47.307139],[-68.384839,47.315873],[-68.382049,47.32781],[-68.347839,47.358506],[-68.299728,47.367833],[-68.24645,47.360573],[-68.197047,47.341401],[-68.184335,47.333133],[-68.156068,47.306674],[-68.145061,47.301455],[-68.115398,47.292282],[-68.101446,47.286185],[-68.039382,47.245231],[-67.993184,47.223217],[-67.962436,47.197689],[-67.953703,47.18663],[-67.949982,47.172936],[-67.943419,47.164538],[-67.899132,47.138778],[-67.870607,47.107358],[-67.854742,47.09785],[-67.813556,47.081908],[-67.808699,47.075138],[-67.805185,47.035631],[-67.802549,46.901247],[-67.800017,46.766785],[-67.797433,46.632297],[-67.794849,46.497861],[-67.792317,46.363476],[-67.789733,46.229014],[-67.78715,46.094552],[-67.784566,45.960142],[-67.782757,45.95053],[-67.776556,45.942933],[-67.767461,45.935957],[-67.759658,45.928567],[-67.757849,45.919472],[-67.769425,45.903969],[-67.787356,45.890017],[-67.799242,45.875651],[-67.792627,45.858907],[-67.776091,45.840821],[-67.772835,45.828057],[-67.779863,45.815706],[-67.794126,45.799169],[-67.80627,45.781754],[-67.811127,45.76651],[-67.810816,45.762414],[-67.817811,45.754896],[-67.821785,45.740767],[-67.827673,45.739001],[-67.868884,45.744593],[-67.856815,45.723694],[-67.835768,45.703971],[-67.793821,45.676301],[-67.733034,45.651869],[-67.723173,45.645393],[-67.711546,45.642155],[-67.697564,45.64922],[-67.66695,45.620077],[-67.649435,45.611247],[-67.603073,45.605948],[-67.561862,45.596234],[-67.54052,45.593879],[-67.442056,45.603593],[-67.440939,45.604586],[-67.431306,45.597941],[-67.422107,45.568796],[-67.42619,45.533449],[-67.443036,45.522184],[-67.467531,45.508283],[-67.493214,45.493142],[-67.48231,45.455521],[-67.428825,45.38705],[-67.434561,45.350308],[-67.459056,45.318424],[-67.468668,45.301835],[-67.475024,45.282353],[-67.471303,45.266282],[-67.427585,45.236568],[-67.390533,45.193108],[-67.356272,45.165926],[-67.31922,45.153886],[-67.284648,45.169699],[-67.279584,45.179052],[-67.279222,45.187372],[-67.277207,45.195072],[-67.267336,45.202513],[-67.254986,45.205045],[-67.242428,45.202565],[-67.219071,45.192126],[-67.206166,45.189401],[-67.176015,45.178656],[-67.191274,45.180365],[-67.204376,45.178209],[-67.204724,45.177791],[-67.152423,45.148932],[-67.048033,45.043407],[-66.962727,45.047088],[-66.857192,44.968696],[-66.897268,44.817275],[-67.2159,44.593511],[-67.122366,44.423624],[-67.68447,44.192544],[-67.459678,40.781645],[-76.607854,32.495823],[-76.798479,32.713735],[-78.561892,29.037718],[-78.892446,29.039659],[-79.762295,26.719312],[-80.026352,24.932961],[-82.368794,23.994833],[-83.806281,29.068506],[-87.460772,29.089961],[-87.922646,28.666131],[-90.461001,28.246758],[-91.787336,29.11536],[-93.311871,29.12431],[-96.423449,26.057857],[-97.129057,25.991017],[-97.129509,25.966833],[-97.139358,25.965876],[-97.202171,25.960893],[-97.202176,25.960857],[-97.204941,25.960639],[-97.253051,25.963481],[-97.266358,25.960639],[-97.2692,25.944361],[-97.287649,25.928651],[-97.310981,25.922088],[-97.328447,25.933302],[-97.351107,25.918419],[-97.355112,25.912786],[-97.35227,25.894493],[-97.345165,25.871704],[-97.345733,25.852222],[-97.36599,25.843902],[-97.376015,25.846744],[-97.380124,25.853203],[-97.383121,25.860541],[-97.389891,25.865657],[-97.397823,25.865812],[-97.399476,25.861162],[-97.39989,25.855115],[-97.404179,25.851395],[-97.425418,25.854857],[-97.435727,25.869275],[-97.441309,25.884933],[-97.448259,25.892322],[-97.469421,25.892943],[-97.486319,25.895733],[-97.502209,25.901883],[-97.52027,25.912786],[-97.565177,25.954748],[-97.594322,25.966375],[-97.604787,25.979966],[-97.613055,25.995985],[-97.622641,26.00906],[-97.641451,26.022495],[-97.659874,26.03066],[-97.679614,26.034639],[-97.766948,26.039652],[-97.780306,26.043218],[-97.782321,26.058617],[-97.80201,26.063733],[-97.878181,26.063733],[-97.941666,26.056809],[-97.999233,26.064302],[-98.013057,26.063682],[-98.044166,26.048799],[-98.065457,26.042184],[-98.075146,26.046628],[-98.083311,26.070916],[-98.103103,26.074947],[-98.150232,26.063682],[-98.185062,26.065232],[-98.222656,26.075412],[-98.300429,26.111431],[-98.309809,26.121094],[-98.333037,26.15303],[-98.339264,26.159851],[-98.365774,26.160161],[-98.377272,26.163572],[-98.377272,26.173649],[-98.36934,26.19401],[-98.397193,26.201141],[-98.428845,26.217729],[-98.456544,26.225946],[-98.472383,26.207652],[-98.49295,26.230596],[-98.521527,26.240932],[-98.552791,26.248321],[-98.581627,26.262274],[-98.640564,26.24181],[-98.653663,26.244291],[-98.664696,26.250647],[-98.685289,26.268475],[-98.693325,26.270542],[-98.702239,26.271628],[-98.704255,26.27664],[-98.691465,26.290231],[-98.701413,26.299119],[-98.713169,26.303357],[-98.726217,26.30439],[-98.739911,26.303253],[-98.735932,26.320048],[-98.746397,26.332141],[-98.780839,26.351674],[-98.795851,26.368314],[-98.801329,26.372138],[-98.810295,26.372448],[-98.817323,26.368521],[-98.825023,26.366454],[-98.836081,26.372138],[-98.842334,26.365834],[-98.850835,26.364077],[-98.860524,26.366299],[-98.870214,26.372138],[-98.893029,26.367849],[-98.9299,26.39224],[-98.945377,26.378288],[-98.954136,26.393946],[-98.962844,26.399527],[-98.986951,26.400095],[-99.004056,26.393842],[-99.010515,26.392602],[-99.016432,26.394462],[-99.022995,26.403351],[-99.027878,26.406245],[-99.047645,26.406968],[-99.066351,26.404746],[-99.085498,26.40764],[-99.106427,26.423039],[-99.108907,26.434253],[-99.102525,26.446966],[-99.09374,26.459781],[-99.089373,26.47115],[-99.091492,26.484018],[-99.10299,26.512078],[-99.115108,26.525617],[-99.140946,26.531405],[-99.164873,26.540448],[-99.17128,26.563961],[-99.171548,26.56583],[-99.213953,26.568537],[-99.242801,26.579723],[-99.254575,26.6018],[-99.258844,26.614752],[-99.277683,26.638007],[-99.281951,26.649781],[-99.277389,26.657729],[-99.26635,26.653314],[-99.252662,26.644483],[-99.240299,26.639184],[-99.244861,26.652431],[-99.240299,26.697763],[-99.242507,26.713658],[-99.252368,26.743683],[-99.254575,26.75899],[-99.252368,26.799024],[-99.254575,26.810504],[-99.257666,26.813153],[-99.262229,26.814036],[-99.266497,26.817863],[-99.268263,26.827872],[-99.271649,26.832876],[-99.289458,26.84465],[-99.308444,26.830521],[-99.316539,26.822279],[-99.323457,26.810504],[-99.328166,26.797258],[-99.329197,26.789016],[-99.331699,26.78254],[-99.340383,26.77312],[-99.366728,26.761345],[-99.380269,26.777241],[-99.391896,26.796963],[-99.412207,26.796963],[-99.410883,26.808149],[-99.405437,26.818452],[-99.396606,26.824928],[-99.384979,26.824928],[-99.377178,26.816686],[-99.374823,26.804028],[-99.374234,26.791076],[-99.371291,26.783128],[-99.360694,26.780479],[-99.359369,26.790487],[-99.36452,26.810504],[-99.357897,26.822279],[-99.351274,26.83111],[-99.346123,26.840824],[-99.344062,26.855247],[-99.348772,26.899696],[-99.355101,26.920302],[-99.36452,26.934726],[-99.403377,26.952093],[-99.413974,26.964162],[-99.401758,26.985651],[-99.399991,26.999192],[-99.418831,27.007728],[-99.441938,27.013615],[-99.453271,27.019797],[-99.455332,27.025979],[-99.464751,27.039225],[-99.466959,27.047467],[-99.462544,27.057181],[-99.461635,27.056839],[-99.461728,27.056954],[-99.442039,27.089614],[-99.439404,27.098347],[-99.441419,27.107494],[-99.445734,27.114728],[-99.450178,27.120465],[-99.452452,27.125012],[-99.450333,27.145166],[-99.435786,27.188419],[-99.431988,27.207591],[-99.434029,27.22697],[-99.440902,27.244798],[-99.451832,27.26118],[-99.46612,27.276527],[-99.468963,27.278233],[-99.480409,27.283297],[-99.482941,27.286708],[-99.484879,27.294821],[-99.486584,27.297611],[-99.493199,27.30128],[-99.521362,27.311254],[-99.5148,27.321796],[-99.497591,27.338798],[-99.494026,27.348203],[-99.492889,27.358848],[-99.487721,27.37187],[-99.484621,27.391766],[-99.475706,27.414762],[-99.472916,27.426647],[-99.473639,27.463803],[-99.472916,27.468299],[-99.47643,27.48251],[-99.480409,27.490778],[-99.48829,27.494654],[-99.503689,27.495584],[-99.509503,27.500028],[-99.510071,27.510518],[-99.507074,27.533437],[-99.507203,27.57377],[-99.515006,27.588601],[-99.535031,27.604828],[-99.55503,27.613509],[-99.572264,27.61847],[-99.578232,27.622811],[-99.590247,27.642061],[-99.600169,27.646427],[-99.612442,27.643637],[-99.633526,27.633069],[-99.644869,27.632733],[-99.648642,27.636919],[-99.658693,27.654024],[-99.664739,27.659398],[-99.70037,27.659191],[-99.705692,27.66317],[-99.710674,27.670116],[-99.723056,27.687381],[-99.730652,27.691825],[-99.734037,27.702031],[-99.736311,27.713607],[-99.740445,27.722159],[-99.747344,27.726009],[-99.765198,27.731177],[-99.774577,27.735828],[-99.78685,27.748488],[-99.795428,27.761924],[-99.806963,27.771423],[-99.808167,27.772414],[-99.83292,27.776755],[-99.832971,27.782181],[-99.844779,27.793576],[-99.858241,27.803524],[-99.863357,27.804661],[-99.864727,27.814324],[-99.861858,27.83608],[-99.863357,27.845666],[-99.870928,27.854477],[-99.880204,27.859231],[-99.888007,27.864812],[-99.891288,27.876026],[-99.882684,27.89158],[-99.878808,27.901838],[-99.88134,27.906463],[-99.896766,27.912923],[-99.914336,27.928245],[-99.929916,27.946331],[-99.939683,27.961085],[-99.928289,27.975761],[-99.940717,27.983254],[-99.961852,27.987492],[-99.976606,27.992453],[-99.991127,28.007801],[-100.000584,28.02041],[-100.007457,28.033561],[-100.014123,28.050459],[-100.013503,28.056971],[-100.010506,28.063611],[-100.010196,28.068882],[-100.017585,28.070949],[-100.031538,28.081801],[-100.045077,28.095289],[-100.048023,28.102523],[-100.048901,28.115959],[-100.056498,28.137922],[-100.074895,28.154407],[-100.172873,28.198538],[-100.189203,28.201329],[-100.197626,28.207168],[-100.201192,28.220346],[-100.202949,28.234428],[-100.205946,28.242877],[-100.212819,28.245073],[-100.240724,28.249698],[-100.257932,28.260524],[-100.275089,28.277242],[-100.284339,28.296517],[-100.277931,28.314888],[-100.278551,28.331088],[-100.293899,28.353413],[-100.322631,28.386899],[-100.331675,28.422013],[-100.336326,28.458574],[-100.340201,28.464259],[-100.348315,28.470253],[-100.355549,28.478185],[-100.35679,28.489322],[-100.351622,28.496711],[-100.322631,28.510406],[-100.364024,28.524797],[-100.38423,28.537174],[-100.397769,28.557586],[-100.398751,28.568645],[-100.397097,28.592726],[-100.401438,28.60226],[-100.411463,28.609314],[-100.434821,28.619133],[-100.44619,28.626497],[-100.444898,28.643782],[-100.481381,28.686054],[-100.493939,28.708378],[-100.519054,28.804961],[-100.524996,28.814831],[-100.529285,28.819947],[-100.534453,28.830231],[-100.538639,28.835631],[-100.54515,28.83899],[-100.559671,28.839378],[-100.566234,28.842504],[-100.569696,28.84961],[-100.56334,28.86209],[-100.566234,28.869789],[-100.571763,28.8732],[-100.586543,28.879789],[-100.58954,28.883458],[-100.594966,28.899322],[-100.606955,28.910123],[-100.618841,28.917926],[-100.624318,28.924721],[-100.624783,28.93777],[-100.626696,28.948338],[-100.630778,28.956683],[-100.637909,28.962884],[-100.628918,28.98433],[-100.632793,29.005156],[-100.652224,29.044817],[-100.660854,29.102669],[-100.668967,29.116208],[-100.678165,29.119412],[-100.690826,29.121014],[-100.70204,29.12365],[-100.706846,29.130187],[-100.70974,29.135561],[-100.762501,29.173776],[-100.770098,29.187289],[-100.762088,29.208658],[-100.783172,29.243074],[-100.796143,29.257673],[-100.81609,29.270773],[-100.86389,29.290616],[-100.871797,29.296456],[-100.891227,29.318547],[-100.91474,29.337048],[-100.987397,29.366322],[-100.998301,29.372472],[-101.008068,29.380585],[-101.016232,29.390068],[-101.022175,29.40048],[-101.025948,29.414356],[-101.029617,29.442984],[-101.037782,29.460063],[-101.039026,29.460452],[-101.040188,29.457132],[-101.045487,29.451245],[-101.060205,29.449184],[-101.067711,29.45095],[-101.076101,29.453894],[-101.085962,29.454483],[-101.098031,29.449184],[-101.113043,29.466552],[-101.142774,29.475383],[-101.174124,29.475971],[-101.193699,29.469495],[-101.198703,29.473911],[-101.198851,29.476854],[-101.184132,29.497754],[-101.184868,29.512767],[-101.195171,29.521892],[-101.214157,29.518065],[-101.245213,29.493044],[-101.265818,29.487157],[-101.290545,29.49746],[-101.297315,29.503936],[-101.300995,29.512767],[-101.294372,29.520715],[-101.273177,29.524247],[-101.259195,29.533372],[-101.243888,29.554861],[-101.231966,29.580176],[-101.227845,29.599899],[-101.239178,29.616677],[-101.26052,29.613439],[-101.281272,29.597249],[-101.290545,29.575761],[-101.295255,29.570168],[-101.306146,29.574583],[-101.317626,29.584003],[-101.323955,29.592539],[-101.323661,29.603137],[-101.318804,29.616383],[-101.311445,29.628158],[-101.303497,29.634045],[-101.303669,29.631411],[-101.302727,29.633851],[-101.301073,29.649509],[-101.30978,29.654548],[-101.336239,29.654315],[-101.349029,29.660103],[-101.357684,29.667441],[-101.364351,29.676665],[-101.376624,29.700643],[-101.383368,29.718497],[-101.39962,29.740718],[-101.406545,29.752888],[-101.409309,29.765781],[-101.405098,29.778442],[-101.414012,29.774411],[-101.424218,29.771414],[-101.435096,29.770122],[-101.446103,29.771052],[-101.455689,29.77591],[-101.462433,29.788932],[-101.470908,29.791516],[-101.490286,29.785547],[-101.505763,29.773894],[-101.521809,29.765936],[-101.542893,29.771052],[-101.539689,29.779191],[-101.530516,29.796477],[-101.528604,29.801438],[-101.531912,29.811101],[-101.539172,29.817974],[-101.546458,29.820145],[-101.549766,29.815701],[-101.553977,29.796684],[-101.564907,29.786478],[-101.580281,29.781568],[-101.632216,29.775651],[-101.794531,29.795857],[-101.80298,29.801438],[-101.805978,29.811928],[-101.812695,29.812032],[-101.82409,29.805184],[-101.857602,29.805184],[-101.877524,29.810843],[-101.88742,29.81229],[-101.895455,29.808621],[-101.90238,29.803247],[-101.910881,29.799888],[-101.920157,29.798182],[-101.929613,29.797717],[-101.942662,29.803608],[-101.957054,29.814047],[-101.972246,29.818181],[-101.98793,29.805184],[-102.014595,29.810998],[-102.109344,29.80211],[-102.145647,29.815701],[-102.157248,29.824537],[-102.203679,29.846138],[-102.239775,29.849135],[-102.253444,29.855285],[-102.258276,29.873475],[-102.276181,29.869547],[-102.289023,29.878126],[-102.302175,29.889391],[-102.321011,29.893939],[-102.330235,29.888926],[-102.339769,29.870633],[-102.351061,29.866602],[-102.36323,29.864276],[-102.370723,29.857765],[-102.374547,29.848102],[-102.376589,29.821488],[-102.380051,29.811386],[-102.404132,29.780793],[-102.406096,29.777279],[-102.515288,29.784721],[-102.523066,29.782318],[-102.531127,29.769915],[-102.54154,29.762474],[-102.543349,29.760123],[-102.546578,29.757875],[-102.553141,29.756738],[-102.558309,29.759089],[-102.562882,29.769347],[-102.566758,29.771052],[-102.58531,29.764696],[-102.621225,29.747281],[-102.638743,29.743715],[-102.676054,29.74449],[-102.683469,29.743715],[-102.69104,29.736817],[-102.693624,29.729401],[-102.694709,29.720616],[-102.697758,29.709557],[-102.726748,29.664495],[-102.73127,29.650594],[-102.735507,29.649509],[-102.751656,29.622457],[-102.75176,29.620157],[-102.761346,29.603414],[-102.767598,29.59729],[-102.779665,29.592303],[-102.774084,29.579617],[-102.776461,29.575948],[-102.785892,29.571814],[-102.78075,29.558249],[-102.786512,29.550497],[-102.795478,29.54427],[-102.827311,29.470502],[-102.833951,29.461355],[-102.839067,29.45195],[-102.841134,29.438308],[-102.838705,29.426939],[-102.834984,29.415699],[-102.835191,29.403839],[-102.844545,29.390533],[-102.845578,29.384719],[-102.838033,29.370534],[-102.837672,29.366322],[-102.84656,29.361749],[-102.853872,29.361],[-102.859867,29.361155],[-102.864957,29.359527],[-102.876972,29.350871],[-102.883069,29.343766],[-102.885188,29.333379],[-102.885498,29.314801],[-102.899399,29.276095],[-102.899709,29.2639],[-102.892139,29.254391],[-102.867954,29.240387],[-102.858781,29.229147],[-102.869866,29.224781],[-102.896893,29.220285],[-102.942265,29.190209],[-102.947536,29.182018],[-102.969757,29.192845],[-102.988386,29.177135],[-103.015826,29.126776],[-103.024275,29.116157],[-103.032621,29.110214],[-103.072541,29.091404],[-103.080758,29.085203],[-103.085589,29.07572],[-103.091532,29.057866],[-103.095356,29.060294],[-103.104684,29.057866],[-103.109205,29.023372],[-103.122771,28.996474],[-103.147989,28.985105],[-103.187108,28.990221],[-103.241756,29.003502],[-103.301545,29.002365],[-103.316247,29.010065],[-103.311514,29.026043],[-103.309994,29.031175],[-103.3248,29.026808],[-103.330484,29.023733],[-103.342602,29.041226],[-103.351671,29.039417],[-103.360534,29.029831],[-103.372083,29.023733],[-103.38663,29.028798],[-103.414639,29.052414],[-103.423605,29.057866],[-103.435697,29.061121],[-103.478537,29.08205],[-103.529748,29.126776],[-103.535588,29.135122],[-103.538223,29.142408],[-103.541711,29.148816],[-103.550238,29.154656],[-103.558015,29.156206],[-103.58499,29.154656],[-103.673125,29.173569],[-103.702477,29.187858],[-103.749476,29.222972],[-103.759062,29.226848],[-103.770767,29.229845],[-103.777718,29.235297],[-103.769424,29.257543],[-103.774229,29.267517],[-103.78366,29.274803],[-103.794177,29.277594],[-103.837038,29.279906]]],[[[178.301106,52.056551],[179.595462,52.142083],[179.825447,51.992849],[179.661729,51.485763],[179.723231,51.459963],[179.408066,51.209841],[178.411463,51.523605],[177.698335,51.877899],[177.16784,51.581866],[176.487008,52.175325],[174.484678,52.08716],[172.866263,52.207379],[172.825506,52.716846],[172.747012,52.654022],[172.08261,52.952695],[172.942925,53.183013],[173.029416,52.993628],[173.127208,52.99494],[173.143321,52.990383],[173.175059,52.971747],[173.182932,52.968373],[176.45233,52.628178],[176.468135,52.488358],[177.900385,52.488358],[178.007601,52.179677],[178.301106,52.056551]]],[[[-168.899607,65.747626],[-168.909861,65.739569],[-168.926218,65.739895],[-168.942128,65.74372],[-168.951731,65.75316],[-168.942983,65.764716],[-168.920115,65.768866],[-168.907908,65.768297],[-168.902781,65.761542],[-168.899607,65.747626]]],[[[-131.160718,54.787192],[-132.853508,54.482536],[-134.77719,54.717786],[-142.6966,55.845503],[-142.861997,49.948308],[-155.675916,51.109976],[-164.492732,50.603976],[-164.691217,50.997975],[-171.246993,49.948308],[-171.215436,50.576636],[-173.341669,50.968826],[-173.362022,51.082198],[-177.799603,51.272899],[-179.155463,50.982285],[-179.476076,52.072632],[-177.11459,52.248701],[-177.146284,52.789384],[-174.777218,52.443779],[-174.773743,52.685853],[-173.653194,52.704099],[-173.790528,53.469081],[-171.063371,53.604473],[-170.777733,59.291898],[-174.324884,60.332184],[-171.736408,62.68026],[-172.315705,62.725352],[-171.995091,63.999658],[-168.501424,65.565173],[-168.714145,65.546708],[-168.853077,68.370871],[-161.115601,72.416214],[-146.132257,70.607941],[-140.692512,69.955349],[-141.145395,69.671641],[-141.015207,69.654202],[-141.006459,69.651272],[-141.005564,69.650946],[-141.005549,69.650941],[-141.005471,69.505164],[-141.001208,60.466879],[-141.001156,60.321074],[-140.994929,60.304382],[-140.979555,60.295804],[-140.909146,60.28366],[-140.768457,60.259269],[-140.660505,60.24051],[-140.533743,60.218548],[-140.518705,60.22387],[-140.506664,60.236324],[-140.475323,60.276477],[-140.462791,60.289138],[-140.447805,60.29446],[-140.424111,60.293168],[-140.32497,60.267537],[-140.169243,60.227229],[-140.01579,60.187387],[-139.967757,60.188369],[-139.916933,60.207851],[-139.826318,60.256478],[-139.728417,60.309033],[-139.679816,60.32681],[-139.628346,60.334096],[-139.517965,60.336732],[-139.413992,60.339212],[-139.262193,60.342778],[-139.101608,60.346602],[-139.079465,60.341021],[-139.06869,60.322056],[-139.073186,60.299835],[-139.113468,60.226816],[-139.149615,60.161187],[-139.183231,60.100157],[-139.182146,60.073389],[-139.112305,60.031376],[-139.060207,60.000059],[-139.051611,59.994892],[-139.003759,59.977219],[-138.842425,59.937686],[-138.742586,59.913192],[-138.704888,59.898464],[-138.697188,59.89371],[-138.692098,59.886888],[-138.654349,59.805498],[-138.63745,59.784052],[-138.59921,59.753822],[-138.488881,59.696357],[-138.363617,59.631142],[-138.219543,59.556004],[-138.067614,59.476991],[-137.91057,59.395187],[-137.758305,59.315915],[-137.611363,59.239331],[-137.594181,59.225275],[-137.582088,59.206568],[-137.5493,59.134531],[-137.521007,59.072364],[-137.484394,58.991904],[-137.507752,58.939969],[-137.50876,58.914906],[-137.486875,58.900075],[-137.453466,58.899145],[-137.423106,58.907723],[-137.338098,58.955472],[-137.2819,58.98715],[-137.172346,59.027148],[-137.062367,59.067572],[-137.047109,59.07331],[-136.942282,59.11107],[-136.840816,59.148174],[-136.785496,59.157217],[-136.671911,59.150809],[-136.613491,59.15422],[-136.569489,59.172152],[-136.484791,59.2538],[-136.483551,59.257469],[-136.466549,59.287803],[-136.467092,59.38449],[-136.467557,59.461643],[-136.415958,59.452238],[-136.36684,59.449551],[-136.319995,59.459059],[-136.275036,59.486448],[-136.244728,59.528202],[-136.258474,59.556107],[-136.29935,59.575745],[-136.350329,59.592384],[-136.2585,59.621582],[-136.145406,59.636826],[-136.02686,59.652846],[-135.923818,59.666747],[-135.830955,59.693257],[-135.641251,59.747362],[-135.482759,59.792475],[-135.465137,59.789685],[-135.404392,59.753305],[-135.345791,59.731032],[-135.259879,59.698218],[-135.221897,59.675273],[-135.192028,59.64711],[-135.157792,59.623287],[-135.106684,59.613158],[-135.087874,59.606544],[-135.032942,59.573109],[-135.018524,59.559363],[-135.016198,59.543447],[-135.01948,59.493166],[-135.023252,59.477146],[-135.037489,59.461591],[-135.078598,59.438337],[-135.095754,59.418855],[-134.993254,59.381906],[-135.00483,59.367127],[-135.014441,59.35152],[-135.016198,59.336173],[-134.979973,59.297415],[-134.95783,59.280982],[-134.932431,59.270647],[-134.839465,59.258141],[-134.74345,59.245119],[-134.70552,59.240106],[-134.692084,59.235249],[-134.68286,59.223001],[-134.671439,59.193752],[-134.66038,59.181298],[-134.610771,59.144556],[-134.582788,59.128847],[-134.556717,59.123059],[-134.509072,59.122801],[-134.477575,59.114946],[-134.451013,59.097893],[-134.398019,59.051952],[-134.387167,59.036863],[-134.385591,59.018828],[-134.399389,58.974954],[-134.343423,58.968857],[-134.329651,58.963017],[-134.320039,58.952682],[-134.32314,58.949168],[-134.330323,58.945344],[-134.333036,58.93413],[-134.327403,58.916457],[-134.316939,58.903796],[-134.22219,58.842714],[-134.108838,58.808246],[-133.983109,58.769902],[-133.87123,58.735899],[-133.831129,58.718019],[-133.796402,58.693421],[-133.700077,58.59937],[-133.626283,58.546402],[-133.547063,58.505577],[-133.463089,58.462221],[-133.392241,58.403878],[-133.43012,58.372097],[-133.41503,58.330549],[-133.374567,58.290965],[-133.257262,58.210298],[-133.165588,58.147305],[-133.142127,58.120588],[-133.094843,58.0331],[-133.075154,58.007882],[-132.99335,57.941917],[-132.917153,57.880499],[-132.83212,57.791564],[-132.70944,57.663303],[-132.629057,57.579277],[-132.552447,57.499075],[-132.455735,57.420992],[-132.362304,57.3457],[-132.304684,57.280355],[-132.230994,57.19682],[-132.276366,57.14889],[-132.34122,57.080393],[-132.16229,57.050317],[-132.031859,57.028406],[-132.107384,56.858753],[-131.871558,56.79346],[-131.865874,56.785708],[-131.872411,56.77297],[-131.882617,56.759146],[-131.887966,56.747958],[-131.886028,56.737055],[-131.880705,56.728838],[-131.864789,56.71349],[-131.838976,56.682278],[-131.830424,56.664759],[-131.826574,56.644606],[-131.832103,56.603368],[-131.825592,56.593343],[-131.799108,56.587658],[-131.692293,56.585074],[-131.585891,56.595048],[-131.560363,56.594066],[-131.536437,56.585229],[-131.491659,56.560166],[-131.345699,56.503271],[-131.215604,56.45255],[-131.100546,56.407669],[-131.016934,56.38705],[-130.839089,56.372452],[-130.760334,56.345192],[-130.645768,56.261942],[-130.602256,56.247059],[-130.495518,56.232434],[-130.47229,56.22489],[-130.458053,56.210653],[-130.427926,56.143964],[-130.418159,56.129702],[-130.403974,56.121898],[-130.290311,56.10097],[-130.243156,56.092391],[-130.211246,56.089962],[-130.116756,56.105646],[-130.094328,56.101486],[-130.071539,56.084123],[-130.039319,56.045521],[-130.026632,56.024101],[-130.01901,56.002216],[-130.014695,55.963252],[-130.016788,55.918913],[-130.019612,55.907978],[-130.019618,55.907952],[-130.022817,55.901353],[-130.049387,55.871405],[-130.104726,55.825263],[-130.136627,55.806464],[-130.148834,55.795356],[-130.163482,55.771145],[-130.167307,55.766262],[-130.170806,55.759833],[-130.173655,55.749498],[-130.170806,55.740953],[-130.163808,55.734565],[-130.160064,55.727118],[-130.167388,55.715399],[-130.155914,55.700141],[-130.142893,55.689521],[-130.131825,55.676581],[-130.126454,55.653998],[-130.12857,55.63642],[-130.135121,55.619127],[-130.153147,55.58511],[-130.148671,55.578192],[-130.146881,55.569322],[-130.146962,55.547187],[-130.112172,55.509345],[-130.101674,55.481147],[-130.095082,55.472113],[-130.065419,55.446112],[-130.057525,55.434882],[-130.052561,55.414008],[-130.054311,55.366645],[-130.05012,55.345445],[-130.039296,55.330756],[-129.989247,55.284003],[-130.031239,55.26435],[-130.050038,55.252875],[-130.067494,55.239],[-130.078236,55.233791],[-130.100494,55.230292],[-130.104726,55.225653],[-130.105702,55.211127],[-130.10912,55.200751],[-130.115793,55.191596],[-130.126454,55.180976],[-130.151967,55.163275],[-130.159983,55.153713],[-130.167592,55.129584],[-130.173695,55.117743],[-130.200266,55.104153],[-130.211781,55.084133],[-130.228871,55.04385],[-130.238678,55.03441],[-130.261342,55.022895],[-130.269846,55.016547],[-130.275706,55.006985],[-130.286366,54.983222],[-130.294342,54.971869],[-130.326568,54.952094],[-130.335561,54.938707],[-130.365387,54.907294],[-130.385243,54.896552],[-130.430816,54.881252],[-130.488759,54.844184],[-130.580312,54.806383],[-130.597485,54.803391],[-130.71074,54.733215],[-131.160718,54.787192]]]]}}]},"featureIcons":{"circle-stroked-24":{"x":0,"y":0,"width":24,"height":24},"circle-stroked-18":{"x":24,"y":0,"width":18,"height":18},"circle-stroked-12":{"x":42,"y":0,"width":12,"height":12},"circle-24":{"x":54,"y":0,"width":24,"height":24},"circle-18":{"x":78,"y":0,"width":18,"height":18},"circle-12":{"x":96,"y":0,"width":12,"height":12},"square-stroked-24":{"x":108,"y":0,"width":24,"height":24},"square-stroked-18":{"x":132,"y":0,"width":18,"height":18},"square-stroked-12":{"x":150,"y":0,"width":12,"height":12},"square-24":{"x":162,"y":0,"width":24,"height":24},"square-18":{"x":186,"y":0,"width":18,"height":18},"square-12":{"x":204,"y":0,"width":12,"height":12},"triangle-stroked-24":{"x":216,"y":0,"width":24,"height":24},"triangle-stroked-18":{"x":240,"y":0,"width":18,"height":18},"triangle-stroked-12":{"x":258,"y":0,"width":12,"height":12},"triangle-24":{"x":0,"y":24,"width":24,"height":24},"triangle-18":{"x":24,"y":24,"width":18,"height":18},"triangle-12":{"x":42,"y":24,"width":12,"height":12},"star-stroked-24":{"x":54,"y":24,"width":24,"height":24},"star-stroked-18":{"x":78,"y":24,"width":18,"height":18},"star-stroked-12":{"x":96,"y":24,"width":12,"height":12},"star-24":{"x":108,"y":24,"width":24,"height":24},"star-18":{"x":132,"y":24,"width":18,"height":18},"star-12":{"x":150,"y":24,"width":12,"height":12},"cross-24":{"x":162,"y":24,"width":24,"height":24},"cross-18":{"x":186,"y":24,"width":18,"height":18},"cross-12":{"x":204,"y":24,"width":12,"height":12},"marker-stroked-24":{"x":216,"y":24,"width":24,"height":24},"marker-stroked-18":{"x":240,"y":24,"width":18,"height":18},"marker-stroked-12":{"x":258,"y":24,"width":12,"height":12},"marker-24":{"x":0,"y":48,"width":24,"height":24},"marker-18":{"x":24,"y":48,"width":18,"height":18},"marker-12":{"x":42,"y":48,"width":12,"height":12},"religious-jewish-24":{"x":54,"y":48,"width":24,"height":24},"religious-jewish-18":{"x":78,"y":48,"width":18,"height":18},"religious-jewish-12":{"x":96,"y":48,"width":12,"height":12},"religious-christian-24":{"x":108,"y":48,"width":24,"height":24},"religious-christian-18":{"x":132,"y":48,"width":18,"height":18},"religious-christian-12":{"x":150,"y":48,"width":12,"height":12},"religious-muslim-24":{"x":162,"y":48,"width":24,"height":24},"religious-muslim-18":{"x":186,"y":48,"width":18,"height":18},"religious-muslim-12":{"x":204,"y":48,"width":12,"height":12},"cemetery-24":{"x":216,"y":48,"width":24,"height":24},"cemetery-18":{"x":240,"y":48,"width":18,"height":18},"cemetery-12":{"x":258,"y":48,"width":12,"height":12},"rocket-24":{"x":0,"y":72,"width":24,"height":24},"rocket-18":{"x":24,"y":72,"width":18,"height":18},"rocket-12":{"x":42,"y":72,"width":12,"height":12},"airport-24":{"x":54,"y":72,"width":24,"height":24},"airport-18":{"x":78,"y":72,"width":18,"height":18},"airport-12":{"x":96,"y":72,"width":12,"height":12},"heliport-24":{"x":108,"y":72,"width":24,"height":24},"heliport-18":{"x":132,"y":72,"width":18,"height":18},"heliport-12":{"x":150,"y":72,"width":12,"height":12},"rail-24":{"x":162,"y":72,"width":24,"height":24},"rail-18":{"x":186,"y":72,"width":18,"height":18},"rail-12":{"x":204,"y":72,"width":12,"height":12},"rail-metro-24":{"x":216,"y":72,"width":24,"height":24},"rail-metro-18":{"x":240,"y":72,"width":18,"height":18},"rail-metro-12":{"x":258,"y":72,"width":12,"height":12},"rail-light-24":{"x":0,"y":96,"width":24,"height":24},"rail-light-18":{"x":24,"y":96,"width":18,"height":18},"rail-light-12":{"x":42,"y":96,"width":12,"height":12},"bus-24":{"x":54,"y":96,"width":24,"height":24},"bus-18":{"x":78,"y":96,"width":18,"height":18},"bus-12":{"x":96,"y":96,"width":12,"height":12},"fuel-24":{"x":108,"y":96,"width":24,"height":24},"fuel-18":{"x":132,"y":96,"width":18,"height":18},"fuel-12":{"x":150,"y":96,"width":12,"height":12},"parking-24":{"x":162,"y":96,"width":24,"height":24},"parking-18":{"x":186,"y":96,"width":18,"height":18},"parking-12":{"x":204,"y":96,"width":12,"height":12},"parking-garage-24":{"x":216,"y":96,"width":24,"height":24},"parking-garage-18":{"x":240,"y":96,"width":18,"height":18},"parking-garage-12":{"x":258,"y":96,"width":12,"height":12},"airfield-24":{"x":0,"y":120,"width":24,"height":24},"airfield-18":{"x":24,"y":120,"width":18,"height":18},"airfield-12":{"x":42,"y":120,"width":12,"height":12},"roadblock-24":{"x":54,"y":120,"width":24,"height":24},"roadblock-18":{"x":78,"y":120,"width":18,"height":18},"roadblock-12":{"x":96,"y":120,"width":12,"height":12},"ferry-24":{"x":108,"y":120,"width":24,"height":24},"ferry-18":{"x":132,"y":120,"width":18,"height":18},"ferry-12":{"x":150,"y":120,"width":12,"height":12},"harbor-24":{"x":162,"y":120,"width":24,"height":24},"harbor-18":{"x":186,"y":120,"width":18,"height":18},"harbor-12":{"x":204,"y":120,"width":12,"height":12},"bicycle-24":{"x":216,"y":120,"width":24,"height":24},"bicycle-18":{"x":240,"y":120,"width":18,"height":18},"bicycle-12":{"x":258,"y":120,"width":12,"height":12},"park-24":{"x":0,"y":144,"width":24,"height":24},"park-18":{"x":24,"y":144,"width":18,"height":18},"park-12":{"x":42,"y":144,"width":12,"height":12},"park2-24":{"x":54,"y":144,"width":24,"height":24},"park2-18":{"x":78,"y":144,"width":18,"height":18},"park2-12":{"x":96,"y":144,"width":12,"height":12},"museum-24":{"x":108,"y":144,"width":24,"height":24},"museum-18":{"x":132,"y":144,"width":18,"height":18},"museum-12":{"x":150,"y":144,"width":12,"height":12},"lodging-24":{"x":162,"y":144,"width":24,"height":24},"lodging-18":{"x":186,"y":144,"width":18,"height":18},"lodging-12":{"x":204,"y":144,"width":12,"height":12},"monument-24":{"x":216,"y":144,"width":24,"height":24},"monument-18":{"x":240,"y":144,"width":18,"height":18},"monument-12":{"x":258,"y":144,"width":12,"height":12},"zoo-24":{"x":0,"y":168,"width":24,"height":24},"zoo-18":{"x":24,"y":168,"width":18,"height":18},"zoo-12":{"x":42,"y":168,"width":12,"height":12},"garden-24":{"x":54,"y":168,"width":24,"height":24},"garden-18":{"x":78,"y":168,"width":18,"height":18},"garden-12":{"x":96,"y":168,"width":12,"height":12},"campsite-24":{"x":108,"y":168,"width":24,"height":24},"campsite-18":{"x":132,"y":168,"width":18,"height":18},"campsite-12":{"x":150,"y":168,"width":12,"height":12},"theatre-24":{"x":162,"y":168,"width":24,"height":24},"theatre-18":{"x":186,"y":168,"width":18,"height":18},"theatre-12":{"x":204,"y":168,"width":12,"height":12},"art-gallery-24":{"x":216,"y":168,"width":24,"height":24},"art-gallery-18":{"x":240,"y":168,"width":18,"height":18},"art-gallery-12":{"x":258,"y":168,"width":12,"height":12},"pitch-24":{"x":0,"y":192,"width":24,"height":24},"pitch-18":{"x":24,"y":192,"width":18,"height":18},"pitch-12":{"x":42,"y":192,"width":12,"height":12},"soccer-24":{"x":54,"y":192,"width":24,"height":24},"soccer-18":{"x":78,"y":192,"width":18,"height":18},"soccer-12":{"x":96,"y":192,"width":12,"height":12},"america-football-24":{"x":108,"y":192,"width":24,"height":24},"america-football-18":{"x":132,"y":192,"width":18,"height":18},"america-football-12":{"x":150,"y":192,"width":12,"height":12},"tennis-24":{"x":162,"y":192,"width":24,"height":24},"tennis-18":{"x":186,"y":192,"width":18,"height":18},"tennis-12":{"x":204,"y":192,"width":12,"height":12},"basketball-24":{"x":216,"y":192,"width":24,"height":24},"basketball-18":{"x":240,"y":192,"width":18,"height":18},"basketball-12":{"x":258,"y":192,"width":12,"height":12},"baseball-24":{"x":0,"y":216,"width":24,"height":24},"baseball-18":{"x":24,"y":216,"width":18,"height":18},"baseball-12":{"x":42,"y":216,"width":12,"height":12},"golf-24":{"x":54,"y":216,"width":24,"height":24},"golf-18":{"x":78,"y":216,"width":18,"height":18},"golf-12":{"x":96,"y":216,"width":12,"height":12},"swimming-24":{"x":108,"y":216,"width":24,"height":24},"swimming-18":{"x":132,"y":216,"width":18,"height":18},"swimming-12":{"x":150,"y":216,"width":12,"height":12},"cricket-24":{"x":162,"y":216,"width":24,"height":24},"cricket-18":{"x":186,"y":216,"width":18,"height":18},"cricket-12":{"x":204,"y":216,"width":12,"height":12},"skiing-24":{"x":216,"y":216,"width":24,"height":24},"skiing-18":{"x":240,"y":216,"width":18,"height":18},"skiing-12":{"x":258,"y":216,"width":12,"height":12},"school-24":{"x":0,"y":240,"width":24,"height":24},"school-18":{"x":24,"y":240,"width":18,"height":18},"school-12":{"x":42,"y":240,"width":12,"height":12},"college-24":{"x":54,"y":240,"width":24,"height":24},"college-18":{"x":78,"y":240,"width":18,"height":18},"college-12":{"x":96,"y":240,"width":12,"height":12},"library-24":{"x":108,"y":240,"width":24,"height":24},"library-18":{"x":132,"y":240,"width":18,"height":18},"library-12":{"x":150,"y":240,"width":12,"height":12},"post-24":{"x":162,"y":240,"width":24,"height":24},"post-18":{"x":186,"y":240,"width":18,"height":18},"post-12":{"x":204,"y":240,"width":12,"height":12},"fire-station-24":{"x":216,"y":240,"width":24,"height":24},"fire-station-18":{"x":240,"y":240,"width":18,"height":18},"fire-station-12":{"x":258,"y":240,"width":12,"height":12},"town-hall-24":{"x":0,"y":264,"width":24,"height":24},"town-hall-18":{"x":24,"y":264,"width":18,"height":18},"town-hall-12":{"x":42,"y":264,"width":12,"height":12},"police-24":{"x":54,"y":264,"width":24,"height":24},"police-18":{"x":78,"y":264,"width":18,"height":18},"police-12":{"x":96,"y":264,"width":12,"height":12},"prison-24":{"x":108,"y":264,"width":24,"height":24},"prison-18":{"x":132,"y":264,"width":18,"height":18},"prison-12":{"x":150,"y":264,"width":12,"height":12},"embassy-24":{"x":162,"y":264,"width":24,"height":24},"embassy-18":{"x":186,"y":264,"width":18,"height":18},"embassy-12":{"x":204,"y":264,"width":12,"height":12},"beer-24":{"x":216,"y":264,"width":24,"height":24},"beer-18":{"x":240,"y":264,"width":18,"height":18},"beer-12":{"x":258,"y":264,"width":12,"height":12},"restaurant-24":{"x":0,"y":288,"width":24,"height":24},"restaurant-18":{"x":24,"y":288,"width":18,"height":18},"restaurant-12":{"x":42,"y":288,"width":12,"height":12},"cafe-24":{"x":54,"y":288,"width":24,"height":24},"cafe-18":{"x":78,"y":288,"width":18,"height":18},"cafe-12":{"x":96,"y":288,"width":12,"height":12},"shop-24":{"x":108,"y":288,"width":24,"height":24},"shop-18":{"x":132,"y":288,"width":18,"height":18},"shop-12":{"x":150,"y":288,"width":12,"height":12},"fast-food-24":{"x":162,"y":288,"width":24,"height":24},"fast-food-18":{"x":186,"y":288,"width":18,"height":18},"fast-food-12":{"x":204,"y":288,"width":12,"height":12},"bar-24":{"x":216,"y":288,"width":24,"height":24},"bar-18":{"x":240,"y":288,"width":18,"height":18},"bar-12":{"x":258,"y":288,"width":12,"height":12},"bank-24":{"x":0,"y":312,"width":24,"height":24},"bank-18":{"x":24,"y":312,"width":18,"height":18},"bank-12":{"x":42,"y":312,"width":12,"height":12},"grocery-24":{"x":54,"y":312,"width":24,"height":24},"grocery-18":{"x":78,"y":312,"width":18,"height":18},"grocery-12":{"x":96,"y":312,"width":12,"height":12},"cinema-24":{"x":108,"y":312,"width":24,"height":24},"cinema-18":{"x":132,"y":312,"width":18,"height":18},"cinema-12":{"x":150,"y":312,"width":12,"height":12},"pharmacy-24":{"x":162,"y":312,"width":24,"height":24},"pharmacy-18":{"x":186,"y":312,"width":18,"height":18},"pharmacy-12":{"x":204,"y":312,"width":12,"height":12},"hospital-24":{"x":216,"y":312,"width":24,"height":24},"hospital-18":{"x":240,"y":312,"width":18,"height":18},"hospital-12":{"x":258,"y":312,"width":12,"height":12},"danger-24":{"x":0,"y":336,"width":24,"height":24},"danger-18":{"x":24,"y":336,"width":18,"height":18},"danger-12":{"x":42,"y":336,"width":12,"height":12},"industrial-24":{"x":54,"y":336,"width":24,"height":24},"industrial-18":{"x":78,"y":336,"width":18,"height":18},"industrial-12":{"x":96,"y":336,"width":12,"height":12},"warehouse-24":{"x":108,"y":336,"width":24,"height":24},"warehouse-18":{"x":132,"y":336,"width":18,"height":18},"warehouse-12":{"x":150,"y":336,"width":12,"height":12},"commercial-24":{"x":162,"y":336,"width":24,"height":24},"commercial-18":{"x":186,"y":336,"width":18,"height":18},"commercial-12":{"x":204,"y":336,"width":12,"height":12},"building-24":{"x":216,"y":336,"width":24,"height":24},"building-18":{"x":240,"y":336,"width":18,"height":18},"building-12":{"x":258,"y":336,"width":12,"height":12},"place-of-worship-24":{"x":0,"y":360,"width":24,"height":24},"place-of-worship-18":{"x":24,"y":360,"width":18,"height":18},"place-of-worship-12":{"x":42,"y":360,"width":12,"height":12},"alcohol-shop-24":{"x":54,"y":360,"width":24,"height":24},"alcohol-shop-18":{"x":78,"y":360,"width":18,"height":18},"alcohol-shop-12":{"x":96,"y":360,"width":12,"height":12},"logging-24":{"x":108,"y":360,"width":24,"height":24},"logging-18":{"x":132,"y":360,"width":18,"height":18},"logging-12":{"x":150,"y":360,"width":12,"height":12},"oil-well-24":{"x":162,"y":360,"width":24,"height":24},"oil-well-18":{"x":186,"y":360,"width":18,"height":18},"oil-well-12":{"x":204,"y":360,"width":12,"height":12},"slaughterhouse-24":{"x":216,"y":360,"width":24,"height":24},"slaughterhouse-18":{"x":240,"y":360,"width":18,"height":18},"slaughterhouse-12":{"x":258,"y":360,"width":12,"height":12},"dam-24":{"x":0,"y":384,"width":24,"height":24},"dam-18":{"x":24,"y":384,"width":18,"height":18},"dam-12":{"x":42,"y":384,"width":12,"height":12},"water-24":{"x":54,"y":384,"width":24,"height":24},"water-18":{"x":78,"y":384,"width":18,"height":18},"water-12":{"x":96,"y":384,"width":12,"height":12},"wetland-24":{"x":108,"y":384,"width":24,"height":24},"wetland-18":{"x":132,"y":384,"width":18,"height":18},"wetland-12":{"x":150,"y":384,"width":12,"height":12},"disability-24":{"x":162,"y":384,"width":24,"height":24},"disability-18":{"x":186,"y":384,"width":18,"height":18},"disability-12":{"x":204,"y":384,"width":12,"height":12},"telephone-24":{"x":216,"y":384,"width":24,"height":24},"telephone-18":{"x":240,"y":384,"width":18,"height":18},"telephone-12":{"x":258,"y":384,"width":12,"height":12},"emergency-telephone-24":{"x":0,"y":408,"width":24,"height":24},"emergency-telephone-18":{"x":24,"y":408,"width":18,"height":18},"emergency-telephone-12":{"x":42,"y":408,"width":12,"height":12},"toilets-24":{"x":54,"y":408,"width":24,"height":24},"toilets-18":{"x":78,"y":408,"width":18,"height":18},"toilets-12":{"x":96,"y":408,"width":12,"height":12},"waste-basket-24":{"x":108,"y":408,"width":24,"height":24},"waste-basket-18":{"x":132,"y":408,"width":18,"height":18},"waste-basket-12":{"x":150,"y":408,"width":12,"height":12},"music-24":{"x":162,"y":408,"width":24,"height":24},"music-18":{"x":186,"y":408,"width":18,"height":18},"music-12":{"x":204,"y":408,"width":12,"height":12},"land-use-24":{"x":216,"y":408,"width":24,"height":24},"land-use-18":{"x":240,"y":408,"width":18,"height":18},"land-use-12":{"x":258,"y":408,"width":12,"height":12},"city-24":{"x":0,"y":432,"width":24,"height":24},"city-18":{"x":24,"y":432,"width":18,"height":18},"city-12":{"x":42,"y":432,"width":12,"height":12},"town-24":{"x":54,"y":432,"width":24,"height":24},"town-18":{"x":78,"y":432,"width":18,"height":18},"town-12":{"x":96,"y":432,"width":12,"height":12},"village-24":{"x":108,"y":432,"width":24,"height":24},"village-18":{"x":132,"y":432,"width":18,"height":18},"village-12":{"x":150,"y":432,"width":12,"height":12},"farm-24":{"x":162,"y":432,"width":24,"height":24},"farm-18":{"x":186,"y":432,"width":18,"height":18},"farm-12":{"x":204,"y":432,"width":12,"height":12},"bakery-24":{"x":216,"y":432,"width":24,"height":24},"bakery-18":{"x":240,"y":432,"width":18,"height":18},"bakery-12":{"x":258,"y":432,"width":12,"height":12},"dog-park-24":{"x":0,"y":456,"width":24,"height":24},"dog-park-18":{"x":24,"y":456,"width":18,"height":18},"dog-park-12":{"x":42,"y":456,"width":12,"height":12},"lighthouse-24":{"x":54,"y":456,"width":24,"height":24},"lighthouse-18":{"x":78,"y":456,"width":18,"height":18},"lighthouse-12":{"x":96,"y":456,"width":12,"height":12},"clothing-store-24":{"x":108,"y":456,"width":24,"height":24},"clothing-store-18":{"x":132,"y":456,"width":18,"height":18},"clothing-store-12":{"x":150,"y":456,"width":12,"height":12},"polling-place-24":{"x":162,"y":456,"width":24,"height":24},"polling-place-18":{"x":186,"y":456,"width":18,"height":18},"polling-place-12":{"x":204,"y":456,"width":12,"height":12},"playground-24":{"x":216,"y":456,"width":24,"height":24},"playground-18":{"x":240,"y":456,"width":18,"height":18},"playground-12":{"x":258,"y":456,"width":12,"height":12},"entrance-24":{"x":0,"y":480,"width":24,"height":24},"entrance-18":{"x":24,"y":480,"width":18,"height":18},"entrance-12":{"x":42,"y":480,"width":12,"height":12},"heart-24":{"x":54,"y":480,"width":24,"height":24},"heart-18":{"x":78,"y":480,"width":18,"height":18},"heart-12":{"x":96,"y":480,"width":12,"height":12},"london-underground-24":{"x":108,"y":480,"width":24,"height":24},"london-underground-18":{"x":132,"y":480,"width":18,"height":18},"london-underground-12":{"x":150,"y":480,"width":12,"height":12},"minefield-24":{"x":162,"y":480,"width":24,"height":24},"minefield-18":{"x":186,"y":480,"width":18,"height":18},"minefield-12":{"x":204,"y":480,"width":12,"height":12},"rail-underground-24":{"x":216,"y":480,"width":24,"height":24},"rail-underground-18":{"x":240,"y":480,"width":18,"height":18},"rail-underground-12":{"x":258,"y":480,"width":12,"height":12},"rail-above-24":{"x":0,"y":504,"width":24,"height":24},"rail-above-18":{"x":24,"y":504,"width":18,"height":18},"rail-above-12":{"x":42,"y":504,"width":12,"height":12},"camera-24":{"x":54,"y":504,"width":24,"height":24},"camera-18":{"x":78,"y":504,"width":18,"height":18},"camera-12":{"x":96,"y":504,"width":12,"height":12},"laundry-24":{"x":108,"y":504,"width":24,"height":24},"laundry-18":{"x":132,"y":504,"width":18,"height":18},"laundry-12":{"x":150,"y":504,"width":12,"height":12},"car-24":{"x":162,"y":504,"width":24,"height":24},"car-18":{"x":186,"y":504,"width":18,"height":18},"car-12":{"x":204,"y":504,"width":12,"height":12},"suitcase-24":{"x":216,"y":504,"width":24,"height":24},"suitcase-18":{"x":240,"y":504,"width":18,"height":18},"suitcase-12":{"x":258,"y":504,"width":12,"height":12},"hairdresser-24":{"x":0,"y":528,"width":24,"height":24},"hairdresser-18":{"x":24,"y":528,"width":18,"height":18},"hairdresser-12":{"x":42,"y":528,"width":12,"height":12},"chemist-24":{"x":54,"y":528,"width":24,"height":24},"chemist-18":{"x":78,"y":528,"width":18,"height":18},"chemist-12":{"x":96,"y":528,"width":12,"height":12},"mobilephone-24":{"x":108,"y":528,"width":24,"height":24},"mobilephone-18":{"x":132,"y":528,"width":18,"height":18},"mobilephone-12":{"x":150,"y":528,"width":12,"height":12},"scooter-24":{"x":162,"y":528,"width":24,"height":24},"scooter-18":{"x":186,"y":528,"width":18,"height":18},"scooter-12":{"x":204,"y":528,"width":12,"height":12},"gift-24":{"x":216,"y":528,"width":24,"height":24},"gift-18":{"x":240,"y":528,"width":18,"height":18},"gift-12":{"x":258,"y":528,"width":12,"height":12},"ice-cream-24":{"x":0,"y":552,"width":24,"height":24},"ice-cream-18":{"x":24,"y":552,"width":18,"height":18},"ice-cream-12":{"x":42,"y":552,"width":12,"height":12}},"locales":["af","sq","ar","ar-AA","hy","ast","bn","bs","bg-BG","ca","zh","zh-CN","zh-HK","zh-TW","yue","hr","cs","da","nl","en-GB","eo","et","fi","fr","gl","de","el","hi","hu","is","id","it","ja","kn","ko","lv","lij","lt","no","fa","pl","pt","pt-BR","ro","ru","sc","sr","si","sk","sl","es","sv","tl","ta","te","th","tr","uk","vi"],"en":{"modes":{"add_area":{"title":"Area","description":"Add parks, buildings, lakes or other areas to the map.","tail":"Click on the map to start drawing an area, like a park, lake, or building."},"add_line":{"title":"Line","description":"Add highways, streets, pedestrian paths, canals or other lines to the map.","tail":"Click on the map to start drawing a road, path, or route."},"add_point":{"title":"Point","description":"Add restaurants, monuments, postal boxes or other points to the map.","tail":"Click on the map to add a point."},"browse":{"title":"Browse","description":"Pan and zoom the map."},"draw_area":{"tail":"Click to add nodes to your area. Click the first node to finish the area."},"draw_line":{"tail":"Click to add more nodes to the line. Click on other lines to connect to them, and double-click to end the line."}},"operations":{"add":{"annotation":{"point":"Added a point.","vertex":"Added a node to a way.","relation":"Added a relation."}},"start":{"annotation":{"line":"Started a line.","area":"Started an area."}},"continue":{"key":"A","title":"Continue","description":"Continue this line.","not_eligible":"No line can be continued here.","multiple":"Several lines can be continued here. To choose a line, press the Shift key and click on it to select it.","annotation":{"line":"Continued a line.","area":"Continued an area."}},"cancel_draw":{"annotation":"Canceled drawing."},"change_role":{"annotation":"Changed the role of a relation member."},"change_tags":{"annotation":"Changed tags."},"circularize":{"title":"Circularize","description":{"line":"Make this line circular.","area":"Make this area circular."},"key":"O","annotation":{"line":"Made a line circular.","area":"Made an area circular."},"not_closed":"This can't be made circular because it's not a loop.","too_large":"This can't be made circular because not enough of it is currently visible.","connected_to_hidden":"This can't be made circular because it is connected to a hidden feature."},"orthogonalize":{"title":"Square","description":{"line":"Square the corners of this line.","area":"Square the corners of this area."},"key":"S","annotation":{"line":"Squared the corners of a line.","area":"Squared the corners of an area."},"not_squarish":"This can't be made square because it is not squarish.","too_large":"This can't be made square because not enough of it is currently visible.","connected_to_hidden":"This can't be made square because it is connected to a hidden feature."},"straighten":{"title":"Straighten","description":"Straighten this line.","key":"S","annotation":"Straightened a line.","too_bendy":"This can't be straightened because it bends too much.","connected_to_hidden":"This line can't be straightened because it is connected to a hidden feature."},"delete":{"title":"Delete","description":"Delete object permanently.","annotation":{"point":"Deleted a point.","vertex":"Deleted a node from a way.","line":"Deleted a line.","area":"Deleted an area.","relation":"Deleted a relation.","multiple":"Deleted {n} objects."},"incomplete_relation":"This feature can't be deleted because it hasn't been fully downloaded.","part_of_relation":"This feature can't be deleted because it's part of a larger relation. You must remove it from the relation first.","connected_to_hidden":"This can't be deleted because it is connected to a hidden feature."},"add_member":{"annotation":"Added a member to a relation."},"delete_member":{"annotation":"Removed a member from a relation."},"connect":{"annotation":{"point":"Connected a way to a point.","vertex":"Connected a way to another.","line":"Connected a way to a line.","area":"Connected a way to an area."}},"disconnect":{"title":"Disconnect","description":"Disconnect these lines/areas from each other.","key":"D","annotation":"Disconnected lines/areas.","not_connected":"There aren't enough lines/areas here to disconnect.","connected_to_hidden":"This can't be disconnected because it is connected to a hidden feature.","relation":"This can't be disconnected because it connects members of a relation."},"merge":{"title":"Merge","description":"Merge these features.","key":"C","annotation":"Merged {n} features.","not_eligible":"These features can't be merged.","not_adjacent":"These features can't be merged because they aren't connected.","restriction":"These features can't be merged because at least one is a member of a \"{relation}\" relation.","incomplete_relation":"These features can't be merged because at least one hasn't been fully downloaded.","conflicting_tags":"These features can't be merged because some of their tags have conflicting values."},"move":{"title":"Move","description":"Move this to a different location.","key":"M","annotation":{"point":"Moved a point.","vertex":"Moved a node in a way.","line":"Moved a line.","area":"Moved an area.","multiple":"Moved multiple objects."},"incomplete_relation":"This feature can't be moved because it hasn't been fully downloaded.","too_large":"This can't be moved because not enough of it is currently visible.","connected_to_hidden":"This can't be moved because it is connected to a hidden feature."},"rotate":{"title":"Rotate","description":"Rotate this object around its center point.","key":"R","annotation":{"line":"Rotated a line.","area":"Rotated an area."},"too_large":"This can't be rotated because not enough of it is currently visible.","connected_to_hidden":"This can't be rotated because it is connected to a hidden feature."},"reverse":{"title":"Reverse","description":"Make this line go in the opposite direction.","key":"V","annotation":"Reversed a line."},"split":{"title":"Split","description":{"line":"Split this line into two at this node.","area":"Split the boundary of this area into two.","multiple":"Split the lines/area boundaries at this node into two."},"key":"X","annotation":{"line":"Split a line.","area":"Split an area boundary.","multiple":"Split {n} lines/area boundaries."},"not_eligible":"Lines can't be split at their beginning or end.","multiple_ways":"There are too many lines here to split.","connected_to_hidden":"This can't be split because it is connected to a hidden feature."},"restriction":{"help":{"select":"Click to select a road segment.","toggle":"Click to toggle turn restrictions.","toggle_on":"Click to add a \"{restriction}\" restriction.","toggle_off":"Click to remove the \"{restriction}\" restriction."},"annotation":{"create":"Added a turn restriction","delete":"Deleted a turn restriction"}}},"undo":{"tooltip":"Undo: {action}","nothing":"Nothing to undo."},"redo":{"tooltip":"Redo: {action}","nothing":"Nothing to redo."},"tooltip_keyhint":"Shortcut:","browser_notice":"This editor is supported in Firefox, Chrome, Safari, Opera, and Internet Explorer 11 and above. Please upgrade your browser or use Potlatch 2 to edit the map.","translate":{"translate":"Translate","localized_translation_label":"Multilingual name","localized_translation_language":"Choose language","localized_translation_name":"Name"},"zoom_in_edit":"Zoom in to Edit","logout":"logout","loading_auth":"Connecting to OpenStreetMap...","report_a_bug":"Report a bug","help_translate":"Help translate","feature_info":{"hidden_warning":"{count} hidden features","hidden_details":"These features are currently hidden: {details}"},"status":{"error":"Unable to connect to API.","offline":"The API is offline. Please try editing later.","readonly":"The API is read-only. You will need to wait to save your changes."},"commit":{"title":"Save Changes","description_placeholder":"Brief description of your contributions (required)","message_label":"Changeset comment","upload_explanation":"The changes you upload will be visible on all maps that use OpenStreetMap data.","upload_explanation_with_user":"The changes you upload as {user} will be visible on all maps that use OpenStreetMap data.","save":"Save","cancel":"Cancel","changes":"{count} Changes","warnings":"Warnings","modified":"Modified","deleted":"Deleted","created":"Created","about_changeset_comments":"About changeset comments","about_changeset_comments_link":"//wiki.openstreetmap.org/wiki/Good_changeset_comments","google_warning":"You mentioned Google in this comment: remember that copying from Google Maps is strictly forbidden.","google_warning_link":"http://www.openstreetmap.org/copyright"},"contributors":{"list":"Edits by {users}","truncated_list":"Edits by {users} and {count} others"},"infobox":{"selected":"{n} selected","geometry":"Geometry","closed":"closed","center":"Center","perimeter":"Perimeter","length":"Length","area":"Area","centroid":"Centroid","location":"Location","metric":"Metric","imperial":"Imperial"},"geometry":{"point":"point","vertex":"vertex","line":"line","area":"area","relation":"relation"},"geocoder":{"search":"Search worldwide...","no_results_visible":"No results in visible map area","no_results_worldwide":"No results found"},"geolocate":{"title":"Show My Location","locating":"Locating, please wait..."},"inspector":{"no_documentation_combination":"There is no documentation available for this tag combination","no_documentation_key":"There is no documentation available for this key","show_more":"Show More","view_on_osm":"View on openstreetmap.org","all_fields":"All fields","all_tags":"All tags","all_members":"All members","all_relations":"All relations","new_relation":"New relation...","role":"Role","choose":"Select feature type","results":"{n} results for {search}","reference":"View on OpenStreetMap Wiki","back_tooltip":"Change feature","remove":"Remove","search":"Search","multiselect":"Selected items","unknown":"Unknown","incomplete":"<not downloaded>","feature_list":"Search features","edit":"Edit feature","check":{"yes":"Yes","no":"No"},"add":"Add","none":"None","node":"Node","way":"Way","relation":"Relation","location":"Location","add_fields":"Add field:"},"background":{"title":"Background","description":"Background settings","percent_brightness":"{opacity}% brightness","none":"None","best_imagery":"Best known imagery source for this location","custom":"Custom","custom_button":"Edit custom background","custom_prompt":"Enter a tile URL template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme.","fix_misalignment":"Adjust imagery offset","imagery_source_faq":"Where does this imagery come from?","reset":"reset","offset":"Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters.","minimap":{"description":"Minimap","tooltip":"Show a zoomed out map to help locate the area currently displayed."}},"map_data":{"title":"Map Data","description":"Map Data","data_layers":"Data Layers","fill_area":"Fill Areas","map_features":"Map Features","autohidden":"These features have been automatically hidden because too many would be shown on the screen. You can zoom in to edit them."},"feature":{"points":{"description":"Points","tooltip":"Points of Interest"},"traffic_roads":{"description":"Traffic Roads","tooltip":"Highways, Streets, etc."},"service_roads":{"description":"Service Roads","tooltip":"Service Roads, Parking Aisles, Tracks, etc."},"paths":{"description":"Paths","tooltip":"Sidewalks, Foot Paths, Cycle Paths, etc."},"buildings":{"description":"Buildings","tooltip":"Buildings, Shelters, Garages, etc."},"landuse":{"description":"Landuse Features","tooltip":"Forests, Farmland, Parks, Residential, Commercial, etc."},"boundaries":{"description":"Boundaries","tooltip":"Administrative Boundaries"},"water":{"description":"Water Features","tooltip":"Rivers, Lakes, Ponds, Basins, etc."},"rail":{"description":"Rail Features","tooltip":"Railways"},"power":{"description":"Power Features","tooltip":"Power Lines, Power Plants, Substations, etc."},"past_future":{"description":"Past/Future","tooltip":"Proposed, Construction, Abandoned, Demolished, etc."},"others":{"description":"Others","tooltip":"Everything Else"}},"area_fill":{"wireframe":{"description":"No Fill (Wireframe)","tooltip":"Enabling wireframe mode makes it easy to see the background imagery."},"partial":{"description":"Partial Fill","tooltip":"Areas are drawn with fill only around their inner edges. (Recommended for beginner mappers)"},"full":{"description":"Full Fill","tooltip":"Areas are drawn fully filled."}},"restore":{"heading":"You have unsaved changes","description":"Do you wish to restore unsaved changes from a previous editing session?","restore":"Restore","reset":"Reset"},"save":{"title":"Save","help":"Save changes to OpenStreetMap, making them visible to other users.","no_changes":"No changes to save.","error":"Errors occurred while trying to save","status_code":"Server returned status code {code}","unknown_error_details":"Please ensure you are connected to the internet.","uploading":"Uploading changes to OpenStreetMap.","unsaved_changes":"You have unsaved changes","conflict":{"header":"Resolve conflicting edits","count":"Conflict {num} of {total}","previous":"< Previous","next":"Next >","keep_local":"Keep mine","keep_remote":"Use theirs","restore":"Restore","delete":"Leave Deleted","download_changes":"Or download your changes.","done":"All conflicts resolved!","help":"Another user changed some of the same map features you changed.\nClick on each item below for more details about the conflict, and choose whether to keep\nyour changes or the other user's changes.\n"}},"merge_remote_changes":{"conflict":{"deleted":"This object has been deleted by {user}.","location":"This object was moved by both you and {user}.","nodelist":"Nodes were changed by both you and {user}.","memberlist":"Relation members were changed by both you and {user}.","tags":"You changed the <b>{tag}</b> tag to \"{local}\" and {user} changed it to \"{remote}\"."}},"success":{"edited_osm":"Edited OSM!","just_edited":"You just edited OpenStreetMap!","view_on_osm":"View on OSM","facebook":"Share on Facebook","twitter":"Share on Twitter","google":"Share on Google+","help_html":"Your changes should appear in the \"Standard\" layer in a few minutes. Other layers, and certain features, may take longer.","help_link_text":"Details","help_link_url":"https://wiki.openstreetmap.org/wiki/FAQ#I_have_just_made_some_changes_to_the_map._How_do_I_get_to_see_my_changes.3F"},"confirm":{"okay":"Okay","cancel":"Cancel"},"splash":{"welcome":"Welcome to the iD OpenStreetMap editor","text":"iD is a friendly but powerful tool for contributing to the world's best free world map. This is version {version}. For more information see {website} and report bugs at {github}.","walkthrough":"Start the Walkthrough","start":"Edit Now"},"source_switch":{"live":"live","lose_changes":"You have unsaved changes. Switching the map server will discard them. Are you sure you want to switch servers?","dev":"dev"},"tag_reference":{"description":"Description","on_wiki":"{tag} on wiki.osm.org","used_with":"used with {type}"},"validations":{"untagged_point":"Untagged point","untagged_line":"Untagged line","untagged_area":"Untagged area","many_deletions":"You're deleting {n} objects. Are you sure you want to do this? This will delete them from the map that everyone else sees on openstreetmap.org.","tag_suggests_area":"The tag {tag} suggests line should be area, but it is not an area","untagged_point_tooltip":"Select a feature type that describes what this point is.","untagged_line_tooltip":"Select a feature type that describes what this line is.","untagged_area_tooltip":"Select a feature type that describes what this area is.","deprecated_tags":"Deprecated tags: {tags}"},"zoom":{"in":"Zoom In","out":"Zoom Out"},"cannot_zoom":"Cannot zoom out further in current mode.","full_screen":"Toggle Full Screen","gpx":{"local_layer":"Local GPX file","drag_drop":"Drag and drop a .gpx file on the page, or click the button to the right to browse","zoom":"Zoom to GPX track","browse":"Browse for a .gpx file"},"mapillary_images":{"tooltip":"Street-level photos from Mapillary","title":"Photo Overlay (Mapillary)"},"mapillary_signs":{"tooltip":"Traffic signs from Mapillary","title":"Traffic Sign Overlay (Mapillary)"},"mapillary":{"view_on_mapillary":"View this image on Mapillary"},"help":{"title":"Help","help":"# Help\n\nThis is an editor for [OpenStreetMap](http://www.openstreetmap.org/), the\nfree and editable map of the world. You can use it to add and update\ndata in your area, making an open-source and open-data map of the world\nbetter for everyone.\n\nEdits that you make on this map will be visible to everyone who uses\nOpenStreetMap. In order to make an edit, you'll need to\n[log in](https://www.openstreetmap.org/login).\n\nThe [iD editor](http://ideditor.com/) is a collaborative project with [source\ncode available on GitHub](https://github.com/openstreetmap/iD).\n","editing_saving":"# Editing & Saving\n\nThis editor is designed to work primarily online, and you're accessing\nit through a website right now.\n\n### Selecting Features\n\nTo select a map feature, like a road or point of interest, click\non it on the map. This will highlight the selected feature, open a panel with\ndetails about it, and show a menu of things you can do with the feature.\n\nTo select multiple features, hold down the 'Shift' key. Then either click\non the features you want to select, or drag on the map to draw a rectangle.\nThis will draw a box and select all the points within it.\n\n### Saving Edits\n\nWhen you make changes like editing roads, buildings, and places, these are\nstored locally until you save them to the server. Don't worry if you make\na mistake - you can undo changes by clicking the undo button, and redo\nchanges by clicking the redo button.\n\nClick 'Save' to finish a group of edits - for instance, if you've completed\nan area of town and would like to start on a new area. You'll have a chance\nto review what you've done, and the editor supplies helpful suggestions\nand warnings if something doesn't seem right about the changes.\n\nIf everything looks good, you can enter a short comment explaining the change\nyou made, and click 'Save' again to post the changes\nto [OpenStreetMap.org](http://www.openstreetmap.org/), where they are visible\nto all other users and available for others to build and improve upon.\n\nIf you can't finish your edits in one sitting, you can leave the editor\nwindow and come back (on the same browser and computer), and the\neditor application will offer to restore your work.\n\n### Using the editor\n\nA list of available keyboard shortcuts can be found [here](http://wiki.openstreetmap.org/wiki/ID/Shortcuts).\n","roads":"# Roads\n\nYou can create, fix, and delete roads with this editor. Roads can be all\nkinds: paths, highways, trails, cycleways, and more - any often-crossed\nsegment should be mappable.\n\n### Selecting\n\nClick on a road to select it. An outline should become visible, along\nwith a small tools menu on the map and a sidebar showing more information\nabout the road.\n\n### Modifying\n\nOften you'll see roads that aren't aligned to the imagery behind them\nor to a GPS track. You can adjust these roads so they are in the correct\nplace.\n\nFirst click on the road you want to change. This will highlight it and show\ncontrol points along it that you can drag to better locations. If\nyou want to add new control points for more detail, double-click a part\nof the road without a node, and one will be added.\n\nIf the road connects to another road, but doesn't properly connect on\nthe map, you can drag one of its control points onto the other road in\norder to join them. Having roads connect is important for the map\nand essential for providing driving directions.\n\nYou can also click the 'Move' tool or press the `M` shortcut key to move the entire road at\none time, and then click again to save that movement.\n\n### Deleting\n\nIf a road is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the road could simply be newly built.\n\nYou can delete a road by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n\n### Creating\n\nFound somewhere there should be a road but there isn't? Click the 'Line'\nicon in the top-left of the editor or press the shortcut key `2` to start drawing\na line.\n\nClick on the start of the road on the map to start drawing. If the road\nbranches off from an existing road, start by clicking on the place where they connect.\n\nThen click on points along the road so that it follows the right path, according\nto satellite imagery or GPS. If the road you are drawing crosses another road, connect\nit by clicking on the intersection point. When you're done drawing, double-click\nor press 'Return' or 'Enter' on your keyboard.\n","gps":"# GPS\n\nCollected GPS traces are one valuable source of data for OpenStreetMap. This editor\nsupports local traces - `.gpx` files on your local computer. You can collect\nthis kind of GPS trace with a number of smartphone applications as well as\npersonal GPS hardware.\n\nFor information on how to perform a GPS survey, read\n[Mapping with a smartphone, GPS, or paper](http://learnosm.org/en/mobile-mapping/).\n\nTo use a GPX track for mapping, drag and drop the GPX file onto the map\neditor. If it's recognized, it will be added to the map as a bright purple\nline. Click on the 'Map Data' menu on the right side to enable,\ndisable, or zoom to this new GPX-powered layer.\n\nThe GPX track isn't directly uploaded to OpenStreetMap - the best way to\nuse it is to draw on the map, using it as a guide for the new features that\nyou add, and also to [upload it to OpenStreetMap](http://www.openstreetmap.org/trace/create)\nfor other users to use.\n","imagery":"# Imagery\n\nAerial imagery is an important resource for mapping. A combination of\nairplane flyovers, satellite views, and freely-compiled sources are available\nin the editor under the 'Background Settings' menu on the right.\n\nBy default a [Bing Maps](http://www.bing.com/maps/) satellite layer is\npresented in the editor, but as you pan and zoom the map to new geographical\nareas, new sources will become available. Some countries, like the United\nStates, France, and Denmark have very high-quality imagery available for some areas.\n\nImagery is sometimes offset from the map data because of a mistake on the\nimagery provider's side. If you see a lot of roads shifted from the background,\ndon't immediately move them all to match the background. Instead you can adjust\nthe imagery so that it matches the existing data by clicking 'Fix alignment' at\nthe bottom of the Background Settings UI.\n","addresses":"# Addresses\n\nAddresses are some of the most useful information for the map.\n\nAlthough addresses are often represented as parts of streets, in OpenStreetMap\nthey're recorded as attributes of buildings and places along streets.\n\nYou can add address information to places mapped as building outlines\nas well as those mapped as single points. The optimal source of address\ndata is from an on-the-ground survey or personal knowledge - as with any\nother feature, copying from commercial sources like Google Maps is strictly\nforbidden.\n","inspector":"# Using the Inspector\n\nThe inspector is the section on the left side of the page that allows you to\nedit the details of the selected feature.\n\n### Selecting a Feature Type\n\nAfter you add a point, line, or area, you can choose what type of feature it\nis, like whether it's a highway or residential road, supermarket or cafe.\nThe inspector will display buttons for common feature types, and you can\nfind others by typing what you're looking for in the search box.\n\nClick the 'i' in the bottom-right-hand corner of a feature type button to\nlearn more about it. Click a button to choose that type.\n\n### Using Forms and Editing Tags\n\nAfter you choose a feature type, or when you select a feature that already\nhas a type assigned, the inspector will display fields with details about\nthe feature like its name and address.\n\nBelow the fields you see, you can click the 'Add field' dropdown to add\nother details, like a Wikipedia link, wheelchair access, and more.\n\nAt the bottom of the inspector, click 'Additional tags' to add arbitrary\nother tags to the element. [Taginfo](http://taginfo.openstreetmap.org/) is a\ngreat resource for learn more about popular tag combinations.\n\nChanges you make in the inspector are automatically applied to the map.\nYou can undo them at any time by clicking the 'Undo' button.\n","buildings":"# Buildings\n\nOpenStreetMap is the world's largest database of buildings. You can create\nand improve this database.\n\n### Selecting\n\nYou can select a building by clicking on its border. This will highlight the\nbuilding and open a small tools menu and a sidebar showing more information\nabout the building.\n\n### Modifying\n\nSometimes buildings are incorrectly placed or have incorrect tags.\n\nTo move an entire building, select it, then click the 'Move' tool. Move your\nmouse to shift the building, and click when it's correctly placed.\n\nTo fix the specific shape of a building, click and drag the nodes that form\nits border into better places.\n\n### Creating\n\nOne of the main questions around adding buildings to the map is that\nOpenStreetMap records buildings both as shapes and points. The rule of thumb\nis to _map a building as a shape whenever possible_, and map companies, homes,\namenities, and other things that operate out of buildings as points placed\nwithin the building shape.\n\nStart drawing a building as a shape by clicking the 'Area' button in the top\nleft of the interface, and end it either by pressing 'Return' on your keyboard\nor clicking on the first node drawn to close the shape.\n\n### Deleting\n\nIf a building is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the building could simply be newly built.\n\nYou can delete a building by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n","relations":"# Relations\n\nA relation is a special type of feature in OpenStreetMap that groups together\nother features. For example, two common types of relations are *route relations*,\nwhich group together sections of road that belong to a specific freeway or\nhighway, and *multipolygons*, which group together several lines that define\na complex area (one with several pieces or holes in it like a donut).\n\nThe group of features in a relation are called *members*. In the sidebar, you can\nsee which relations a feature is a member of, and click on a relation there\nto select the it. When the relation is selected, you can see all of its\nmembers listed in the sidebar and highlighted on the map.\n\nFor the most part, iD will take care of maintaining relations automatically\nwhile you edit. The main thing you should be aware of is that if you delete a\nsection of road to redraw it more accurately, you should make sure that the\nnew section is a member of the same relations as the original.\n\n## Editing Relations\n\nIf you want to edit relations, here are the basics.\n\nTo add a feature to a relation, select the feature, click the \"+\" button in the\n\"All relations\" section of the sidebar, and select or type the name of the relation.\n\nTo create a new relation, select the first feature that should be a member,\nclick the \"+\" button in the \"All relations\" section, and select \"New relation...\".\n\nTo remove a feature from a relation, select the feature and click the trash\nbutton next to the relation you want to remove it from.\n\nYou can create multipolygons with holes using the \"Merge\" tool. Draw two areas (inner\nand outer), hold the Shift key and click on each of them to select them both, and then\nclick the \"Merge\" (+) button.\n"},"intro":{"graph":{"city_hall":"Three Rivers City Hall","fire_department":"Three Rivers Fire Department","memory_isle_park":"Memory Isle Park","riverwalk_trail":"Riverwalk Trail","w_michigan_ave":"West Michigan Avenue","e_michigan_ave":"East Michigan Avenue","spring_st":"Spring Street","scidmore_park":"Scidmore Park","petting_zoo":"Scidmore Park Petting Zoo","n_andrews_st":"North Andrews Street","s_andrews_st":"South Andrews Street","n_constantine_st":"North Constantine Street","s_constantine_st":"South Constantine Street","rocky_river":"Rocky River","railroad_dr":"Railroad Drive","conrail_rr":"Conrail Railroad","st_joseph_river":"Saint Joseph River","n_main_st":"North Main Street","s_main_st":"South Main Street","water_st":"Water Street","foster_st":"Foster Street","portage_river":"Portage River","flower_st":"Flower Street","elm_st":"Elm Street","walnut_st":"Walnut Street","morris_ave":"Morris Avenue","east_st":"East Street","portage_ave":"Portage Avenue"},"navigation":{"title":"Navigation","drag":"The main map area shows OpenStreetMap data on top of a background. You can navigate by dragging and scrolling, just like any web map. **Drag the map!**","select":"Map features are represented three ways: using points, lines or areas. All features can be selected by clicking on them. **Click on the point to select it.**","pane":"When a feature is selected, the feature editor is displayed. The header shows us the feature type and the main pane shows the feature's attributes, such as its name and address. **Close the feature editor by pressing the {button} button in the top right.**","search":"You can also search for features in the current view, or worldwide. **Search for '{name}'**","choose":"**Choose {name} from the list to select it.**","chosen":"Great! {name} is now selected. **Close the feature editor by pressing the {button} button.**"},"points":{"title":"Points","add":"Points can be used to represent features such as shops, restaurants, and monuments. They mark a specific location, and describe what's there. **Click the {button} Point button to add a new point.**","place":"The point can be placed by clicking on the map. **Click the map to place the new point on top of the building.**","search":"There are many different features that can be represented by points. The point you just added is a Cafe. **Search for '{name}'**","choose":"**Choose Cafe from the list.**","describe":"The point is now marked as a cafe. Using the feature editor, we can add more information about the feature. **Add a name**","close":"The feature editor will remember all of your changes automatically. When you change a feature, the close button will change to a checkmark. **Click the {button} button to close the feature editor**","reselect":"Often points will already exist, but have mistakes or be incomplete. We can edit existing points. **Click to select the point you just created.**","fixname":"**Change the name, then click the {button} button to close the feature editor.**","reselect_delete":"All features on the map can be deleted. **Click to select the point you created.**","delete":"The menu around the point contains operations that can be performed on it, including delete. **Click on the {button} button to delete the point.**"},"areas":{"title":"Areas","add":"Areas are used to show the boundaries of features like lakes, buildings, and residential areas. They can be also be used for more detailed mapping of many features you might normally map as points. **Click the {button} Area button to add a new area.**","corner":"Areas are drawn by placing nodes that mark the boundary of the area. **Click to place a starting node on one of the corners of the playground.**","place":"Draw the area by placing more nodes. Finish the area by clicking on the starting node. **Draw an area for the playground.**","search":"**Search for '{name}'.**","choose":"**Choose Playground from the list.**","describe":"**Add a name, then click the {button} button to close the feature editor**"},"lines":{"title":"Lines","add":"Lines are used to represent features such as roads, railroads, and rivers. **Click the {button} Line button to add a new line.**","start":"**Start the line by clicking on the end of the road.**","intersect":"Click to add more nodes to the line. You can drag the map while drawing if necessary. Roads, and many other types of lines, are part of a larger network. It is important for these lines to be connected properly in order for routing applications to work. **Click on {name} to create an intersection connecting the two lines.**","finish":"Lines can be finished by clicking on the last node again. **Finish drawing the road.**","road":"**Select Road from the list**","residential":"There are different types of roads, the most common of which is Residential. **Choose the Residential road type**","describe":"**Name the road, then click the {button} button to close the feature editor.**","restart":"The road needs to intersect {name}.","wrong_preset":"You didn't select the Residential road type. **Click here to choose again**"},"startediting":{"title":"Start Editing","help":"You can replay this walkthrough or view more documentation by clicking the {button} Help button.","save":"Don't forget to regularly save your changes!","start":"Start mapping!"}},"presets":{"categories":{"category-building":{"name":"Building Features"},"category-golf":{"name":"Golf Features"},"category-landuse":{"name":"Land Use Features"},"category-path":{"name":"Path Features"},"category-rail":{"name":"Rail Features"},"category-restriction":{"name":"Restriction Features"},"category-road":{"name":"Road Features"},"category-route":{"name":"Route Features"},"category-water-area":{"name":"Water Features"},"category-water-line":{"name":"Water Features"}},"fields":{"access":{"label":"Allowed Access","placeholder":"Not Specified","types":{"access":"All","foot":"Foot","motor_vehicle":"Motor Vehicles","bicycle":"Bicycles","horse":"Horses"},"options":{"yes":{"title":"Allowed","description":"Access permitted by law; a right of way"},"no":{"title":"Prohibited","description":"Access not permitted to the general public"},"permissive":{"title":"Permissive","description":"Access permitted until such time as the owner revokes the permission"},"private":{"title":"Private","description":"Access permitted only with permission of the owner on an individual basis"},"designated":{"title":"Designated","description":"Access permitted according to signs or specific local laws"},"destination":{"title":"Destination","description":"Access permitted only to reach a destination"},"dismount":{"title":"Dismount","description":"Access permitted but rider must dismount"}}},"access_simple":{"label":"Allowed Access","placeholder":"yes"},"access_toilets":{"label":"Access"},"address":{"label":"Address","placeholders":{"housename":"Housename","housenumber":"123","conscriptionnumber":"123","street":"Street","city":"City","postcode":"Postcode","place":"Place","hamlet":"Hamlet","suburb":"Suburb","subdistrict":"Subdistrict","district":"District","province":"Province","state":"State","country":"Country"}},"admin_level":{"label":"Admin Level"},"aerialway":{"label":"Type"},"aerialway/access":{"label":"Access","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aerialway/bubble":{"label":"Bubble"},"aerialway/capacity":{"label":"Capacity (per hour)","placeholder":"500, 2500, 5000..."},"aerialway/duration":{"label":"Duration (minutes)","placeholder":"1, 2, 3..."},"aerialway/heating":{"label":"Heated"},"aerialway/occupancy":{"label":"Occupancy","placeholder":"2, 4, 8..."},"aerialway/summer/access":{"label":"Access (summer)","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aeroway":{"label":"Type"},"amenity":{"label":"Type"},"area/highway":{"label":"Type"},"artist":{"label":"Artist"},"artwork_type":{"label":"Type"},"atm":{"label":"ATM"},"backrest":{"label":"Backrest"},"barrier":{"label":"Type"},"bench":{"label":"Bench"},"bicycle_parking":{"label":"Type"},"bin":{"label":"Waste Bin"},"boundary":{"label":"Type"},"brand":{"label":"Brand"},"building":{"label":"Building"},"building_area":{"label":"Building"},"capacity":{"label":"Capacity","placeholder":"50, 100, 200..."},"cardinal_direction":{"label":"Direction","options":{"N":"North","E":"East","S":"South","W":"West","NE":"Northeast","SE":"Southeast","SW":"Southwest","NW":"Northwest","NNE":"North-northeast","ENE":"East-northeast","ESE":"East-southeast","SSE":"South-southeast","SSW":"South-southwest","WSW":"West-southwest","WNW":"West-northwest","NNW":"North-northwest"}},"clock_direction":{"label":"Direction","options":{"clockwise":"Clockwise","anticlockwise":"Counterclockwise"}},"collection_times":{"label":"Collection Times"},"construction":{"label":"Type"},"content":{"label":"Contents"},"country":{"label":"Country"},"covered":{"label":"Covered"},"craft":{"label":"Type"},"crop":{"label":"Crop"},"crossing":{"label":"Type"},"cuisine":{"label":"Cuisine"},"currency_multi":{"label":"Currency Types"},"cycleway":{"label":"Bike Lanes","placeholder":"none","types":{"cycleway:left":"Left side","cycleway:right":"Right side"},"options":{"none":{"title":"None","description":"No bike lane"},"lane":{"title":"Standard bike lane","description":"A bike lane separated from auto traffic by a painted line"},"shared_lane":{"title":"Shared bike lane","description":"A bike lane with no separation from auto traffic"},"track":{"title":"Bike track","description":"A bike lane separated from traffic by a physical barrier"},"share_busway":{"title":"Bike lane shared with bus","description":"A bike lane shared with a bus lane"},"opposite_lane":{"title":"Opposite bike lane","description":"A bike lane that travels in the opposite direction of traffic"},"opposite":{"title":"Contraflow bike lane","description":"A bike lane that travels in both directions on a one-way street"}}},"delivery":{"label":"Delivery"},"denomination":{"label":"Denomination"},"denotation":{"label":"Denotation"},"description":{"label":"Description"},"diaper":{"label":"Diaper Changing Available"},"dock":{"label":"Type"},"drive_through":{"label":"Drive-Through"},"electrified":{"label":"Electrification","placeholder":"Contact Line, Electrified Rail...","options":{"contact_line":"Contact Line","rail":"Electrified Rail","yes":"Yes (unspecified)","no":"No"}},"elevation":{"label":"Elevation"},"emergency":{"label":"Emergency"},"entrance":{"label":"Type"},"except":{"label":"Exceptions"},"fax":{"label":"Fax","placeholder":"+31 42 123 4567"},"fee":{"label":"Fee"},"fire_hydrant/type":{"label":"Type","options":{"pillar":"Pillar/Aboveground","underground":"Underground","wall":"Wall","pond":"Pond"}},"fixme":{"label":"Fix Me"},"fuel":{"label":"Fuel"},"fuel_multi":{"label":"Fuel Types"},"gauge":{"label":"Gauge"},"gender":{"label":"Gender","placeholder":"Unknown","options":{"male":"Male","female":"Female","unisex":"Unisex"}},"generator/method":{"label":"Method"},"generator/source":{"label":"Source"},"generator/type":{"label":"Type"},"golf_hole":{"label":"Reference","placeholder":"Hole number (1-18)"},"handicap":{"label":"Handicap","placeholder":"1-18"},"handrail":{"label":"Handrail"},"highway":{"label":"Type"},"historic":{"label":"Type"},"hoops":{"label":"Hoops","placeholder":"1, 2, 4..."},"iata":{"label":"IATA"},"icao":{"label":"ICAO"},"incline":{"label":"Incline"},"incline_steps":{"label":"Incline","options":{"up":"Up","down":"Down"}},"information":{"label":"Type"},"internet_access":{"label":"Internet Access","options":{"yes":"Yes","no":"No","wlan":"Wifi","wired":"Wired","terminal":"Terminal"}},"lamp_type":{"label":"Type"},"landuse":{"label":"Type"},"lanes":{"label":"Lanes","placeholder":"1, 2, 3..."},"layer":{"label":"Layer"},"leaf_cycle":{"label":"Leaf Cycle","options":{"evergreen":"Evergreen","deciduous":"Deciduous","semi_evergreen":"Semi-Evergreen","semi_deciduous":"Semi-Deciduous","mixed":"Mixed"}},"leaf_cycle_singular":{"label":"Leaf Cycle","options":{"evergreen":"Evergreen","deciduous":"Deciduous","semi_evergreen":"Semi-Evergreen","semi_deciduous":"Semi-Deciduous"}},"leaf_type":{"label":"Leaf Type","options":{"broadleaved":"Broadleaved","needleleaved":"Needleleaved","mixed":"Mixed","leafless":"Leafless"}},"leaf_type_singular":{"label":"Leaf Type","options":{"broadleaved":"Broadleaved","needleleaved":"Needleleaved","leafless":"Leafless"}},"leisure":{"label":"Type"},"length":{"label":"Length (Meters)"},"level":{"label":"Level"},"levels":{"label":"Levels","placeholder":"2, 4, 6..."},"lit":{"label":"Lit"},"location":{"label":"Location"},"man_made":{"label":"Type"},"maxspeed":{"label":"Speed Limit","placeholder":"40, 50, 60..."},"maxstay":{"label":"Max Stay"},"mtb/scale":{"label":"Mountain Biking Difficulty","placeholder":"0, 1, 2, 3...","options":{"0":"0: Solid gravel/packed earth, no obstacles, wide curves","1":"1: Some loose surface, small obstacles, wide curves","2":"2: Much loose surface, large obstacles, easy hairpins","3":"3: Slippery surface, large obstacles, tight hairpins","4":"4: Loose surface or boulders, dangerous hairpins","5":"5: Maximum difficulty, boulder fields, landslides","6":"6: Not rideable except by the very best mountain bikers"}},"mtb/scale/imba":{"label":"IMBA Trail Difficulty","placeholder":"Easy, Medium, Difficult...","options":{"0":"Easiest (white circle)","1":"Easy (green circle)","2":"Medium (blue square)","3":"Difficult (black diamond)","4":"Extremely Difficult (double black diamond)"}},"mtb/scale/uphill":{"label":"Mountain Biking Uphill Difficulty","placeholder":"0, 1, 2, 3...","options":{"0":"0: Avg. incline <10%, gravel/packed earth, no obstacles","1":"1: Avg. incline <15%, gravel/packed earth, few small objects","2":"2: Avg. incline <20%, stable surface, fistsize rocks/roots","3":"3: Avg. incline <25%, variable surface, fistsize rocks/branches","4":"4: Avg. incline <30%, poor condition, big rocks/branches","5":"5: Very steep, bike generally needs to be pushed or carried"}},"name":{"label":"Name","placeholder":"Common name (if any)"},"natural":{"label":"Natural"},"network":{"label":"Network"},"note":{"label":"Note"},"office":{"label":"Type"},"oneway":{"label":"One Way","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"oneway_yes":{"label":"One Way","options":{"undefined":"Assumed to be Yes","yes":"Yes","no":"No"}},"opening_hours":{"label":"Hours"},"operator":{"label":"Operator"},"par":{"label":"Par","placeholder":"3, 4, 5..."},"park_ride":{"label":"Park and Ride"},"parking":{"label":"Type","options":{"surface":"Surface","multi-storey":"Multilevel","underground":"Underground","sheds":"Sheds","carports":"Carports","garage_boxes":"Garage Boxes","lane":"Roadside Lane"}},"payment_multi":{"label":"Payment Types"},"phone":{"label":"Phone","placeholder":"+31 42 123 4567"},"piste/difficulty":{"label":"Difficulty","placeholder":"Easy, Intermediate, Advanced...","options":{"novice":"Novice (instructional)","easy":"Easy (green circle)","intermediate":"Intermediate (blue square)","advanced":"Advanced (black diamond)","expert":"Expert (double black diamond)","freeride":"Freeride (off-piste)","extreme":"Extreme (climbing equipment required)"}},"piste/grooming":{"label":"Grooming","options":{"classic":"Classic","mogul":"Mogul","backcountry":"Backcountry","classic+skating":"Classic and Skating","scooter":"Scooter/Snowmobile","skating":"Skating"}},"piste/type":{"label":"Type","options":{"downhill":"Downhill","nordic":"Nordic","skitour":"Skitour","sled":"Sled","hike":"Hike","sleigh":"Sleigh","ice_skate":"Ice Skate","snow_park":"Snow Park","playground":"Playground"}},"place":{"label":"Type"},"population":{"label":"Population"},"power":{"label":"Type"},"power_supply":{"label":"Power Supply"},"railway":{"label":"Type"},"recycling_accepts":{"label":"Accepts"},"ref":{"label":"Reference"},"relation":{"label":"Type"},"religion":{"label":"Religion"},"restriction":{"label":"Type"},"restrictions":{"label":"Turn Restrictions"},"route":{"label":"Type"},"route_master":{"label":"Type"},"sac_scale":{"label":"Hiking Difficulty","placeholder":"Mountain Hiking, Alpine Hiking...","options":{"hiking":"T1: Hiking","mountain_hiking":"T2: Mountain Hiking","demanding_mountain_hiking":"T3: Demanding Mountain Hiking","alpine_hiking":"T4: Alpine Hiking","demanding_alpine_hiking":"T5: Demanding Alpine Hiking","difficult_alpine_hiking":"T6: Difficult Alpine Hiking"}},"sanitary_dump_station":{"label":"Toilet Disposal"},"seasonal":{"label":"Seasonal"},"service":{"label":"Type"},"service/bicycle/chain_tool":{"label":"Chain Tool","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service/bicycle/pump":{"label":"Air Pump","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service_rail":{"label":"Service Type","options":{"spur":"Spur","yard":"Yard","siding":"Siding","crossover":"Crossover"}},"shelter":{"label":"Shelter"},"shelter_type":{"label":"Type"},"shop":{"label":"Type"},"site":{"label":"Type"},"sloped_curb":{"label":"Sloped Curb"},"smoking":{"label":"Smoking","placeholder":"No, Separated, Yes...","options":{"no":"No smoking anywhere","separated":"In smoking areas, not physically isolated","isolated":"In smoking areas, physically isolated","outside":"Allowed outside","yes":"Allowed everywhere","dedicated":"Dedicated to smokers (e.g. smokers' club)"}},"smoothness":{"label":"Smoothness","placeholder":"Thin Rollers, Wheels, Off-Road...","options":{"excellent":"Thin Rollers: rollerblade, skateboard","good":"Thin Wheels: racing bike","intermediate":"Wheels: city bike, wheelchair, scooter","bad":"Robust Wheels: trekking bike, car, rickshaw","very_bad":"High Clearance: light duty off-road vehicle","horrible":"Off-Road: heavy duty off-road vehicle","very_horrible":"Specialized off-road: tractor, ATV","impassable":"Impassable / No wheeled vehicle"}},"social_facility_for":{"label":"People served","placeholder":"Homeless, Disabled, Child, etc"},"source":{"label":"Source"},"sport":{"label":"Sport"},"sport_ice":{"label":"Sport"},"sport_racing":{"label":"Sport"},"structure":{"label":"Structure","placeholder":"Unknown","options":{"bridge":"Bridge","tunnel":"Tunnel","embankment":"Embankment","cutting":"Cutting","ford":"Ford"}},"studio":{"label":"Type"},"substation":{"label":"Type"},"supervised":{"label":"Supervised"},"surface":{"label":"Surface"},"tactile_paving":{"label":"Tactile Paving"},"takeaway":{"label":"Takeaway","placeholder":"Yes, No, Takeaway Only...","options":{"yes":"Yes","no":"No","only":"Takeaway Only"}},"toilets/disposal":{"label":"Disposal","options":{"flush":"Flush","pitlatrine":"Pit/Latrine","chemical":"Chemical","bucket":"Bucket"}},"tourism":{"label":"Type"},"towertype":{"label":"Tower type"},"tracktype":{"label":"Track Type","placeholder":"Solid, Mostly Solid, Soft...","options":{"grade1":"Solid: paved or heavily compacted hardcore surface","grade2":"Mostly Solid: gravel/rock with some soft material mixed in","grade3":"Even mixture of hard and soft materials","grade4":"Mostly Soft: soil/sand/grass with some hard material mixed in","grade5":"Soft: soil/sand/grass"}},"traffic_signals":{"label":"Type"},"trail_visibility":{"label":"Trail Visibility","placeholder":"Excellent, Good, Bad...","options":{"excellent":"Excellent: unambiguous path or markers everywhere","good":"Good: markers visible, sometimes require searching","intermediate":"Intermediate: few markers, path mostly visible","bad":"Bad: no markers, path sometimes invisible/pathless","horrible":"Horrible: often pathless, some orientation skills required","no":"No: pathless, excellent orientation skills required"}},"trees":{"label":"Trees"},"tunnel":{"label":"Tunnel"},"vending":{"label":"Type of Goods"},"water":{"label":"Type"},"water_point":{"label":"Water Point"},"waterway":{"label":"Type"},"website":{"label":"Website","placeholder":"http://example.com/"},"wetland":{"label":"Type"},"wheelchair":{"label":"Wheelchair Access"},"width":{"label":"Width (Meters)"},"wikipedia":{"label":"Wikipedia"}},"presets":{"address":{"name":"Address","terms":""},"aerialway":{"name":"Aerialway","terms":"ski lift,funifor,funitel"},"aerialway/cable_car":{"name":"Cable Car","terms":"tramway,ropeway"},"aerialway/chair_lift":{"name":"Chair Lift","terms":""},"aerialway/gondola":{"name":"Gondola","terms":""},"aerialway/magic_carpet":{"name":"Magic Carpet Lift","terms":""},"aerialway/platter":{"name":"Platter Lift","terms":"button lift,poma lift"},"aerialway/pylon":{"name":"Aerialway Pylon","terms":""},"aerialway/rope_tow":{"name":"Rope Tow Lift","terms":"handle tow,bugel lift"},"aerialway/station":{"name":"Aerialway Station","terms":""},"aerialway/t-bar":{"name":"T-bar Lift","terms":""},"aeroway":{"name":"Aeroway","terms":""},"aeroway/aerodrome":{"name":"Airport","terms":"airplane,airport,aerodrome"},"aeroway/apron":{"name":"Apron","terms":"ramp"},"aeroway/gate":{"name":"Airport gate","terms":""},"aeroway/hangar":{"name":"Hangar","terms":""},"aeroway/helipad":{"name":"Helipad","terms":"helicopter,helipad,heliport"},"aeroway/runway":{"name":"Runway","terms":"landing strip"},"aeroway/taxiway":{"name":"Taxiway","terms":""},"aeroway/terminal":{"name":"Airport terminal","terms":"airport,aerodrome"},"amenity":{"name":"Amenity","terms":""},"amenity/arts_centre":{"name":"Arts Center","terms":""},"amenity/atm":{"name":"ATM","terms":"money,cash,machine"},"amenity/bank":{"name":"Bank","terms":"credit union,check,deposit,fund,investment,repository,reserve,safe,savings,stock,treasury,trust,vault"},"amenity/bar":{"name":"Bar","terms":"dive,beer,bier,booze"},"amenity/bbq":{"name":"Barbecue/Grill","terms":"bbq"},"amenity/bench":{"name":"Bench","terms":"seat"},"amenity/bicycle_parking":{"name":"Bicycle Parking","terms":"bike"},"amenity/bicycle_rental":{"name":"Bicycle Rental","terms":"bike"},"amenity/bicycle_repair_station":{"name":"Bicycle Repair Tool Stand","terms":"bike,repair,chain,pump"},"amenity/biergarten":{"name":"Beer Garden","terms":"beer,bier,booze"},"amenity/boat_rental":{"name":"Boat Rental","terms":""},"amenity/bureau_de_change":{"name":"Currency Exchange","terms":"bureau de change,money changer"},"amenity/bus_station":{"name":"Bus Station","terms":""},"amenity/cafe":{"name":"Cafe","terms":"bistro,coffee,tea"},"amenity/car_rental":{"name":"Car Rental","terms":""},"amenity/car_sharing":{"name":"Car Sharing","terms":""},"amenity/car_wash":{"name":"Car Wash","terms":""},"amenity/casino":{"name":"Casino","terms":"gambling,roulette,craps,poker,blackjack"},"amenity/charging_station":{"name":"Charging Station","terms":"EV,Electric Vehicle,Supercharger"},"amenity/childcare":{"name":"Nursery/Childcare","terms":"daycare,orphanage,playgroup"},"amenity/cinema":{"name":"Cinema","terms":"drive-in,film,flick,movie,theater,picture,show,screen"},"amenity/clinic":{"name":"Clinic","terms":"medical,urgentcare"},"amenity/clock":{"name":"Clock","terms":""},"amenity/college":{"name":"College Grounds","terms":"university"},"amenity/community_centre":{"name":"Community Center","terms":"event,hall"},"amenity/compressed_air":{"name":"Compressed Air","terms":""},"amenity/courthouse":{"name":"Courthouse","terms":""},"amenity/dentist":{"name":"Dentist","terms":"tooth,teeth"},"amenity/doctors":{"name":"Doctor","terms":"medic*"},"amenity/dojo":{"name":"Dojo / Martial Arts Academy","terms":"martial arts,dojang"},"amenity/drinking_water":{"name":"Drinking Water","terms":"fountain,potable"},"amenity/embassy":{"name":"Embassy","terms":""},"amenity/fast_food":{"name":"Fast Food","terms":"restaurant"},"amenity/ferry_terminal":{"name":"Ferry Terminal","terms":""},"amenity/fire_station":{"name":"Fire Station","terms":""},"amenity/fountain":{"name":"Fountain","terms":""},"amenity/fuel":{"name":"Gas Station","terms":"petrol,fuel,gasoline,propane,diesel,lng,cng,biodiesel"},"amenity/grave_yard":{"name":"Graveyard","terms":""},"amenity/grit_bin":{"name":"Grit Bin","terms":"salt,sand"},"amenity/hospital":{"name":"Hospital Grounds","terms":"clinic,doctor,emergency room,health service,hospice,infirmary,institution,nursing home,sanatorium,sanitarium,sick,surgery,ward"},"amenity/hunting_stand":{"name":"Hunting Stand","terms":""},"amenity/kindergarten":{"name":"Preschool/Kindergarten Grounds","terms":"kindergarden,pre-school"},"amenity/library":{"name":"Library","terms":"book"},"amenity/marketplace":{"name":"Marketplace","terms":""},"amenity/motorcycle_parking":{"name":"Motorcycle Parking","terms":""},"amenity/nightclub":{"name":"Nightclub","terms":"disco*,night club,dancing,dance club"},"amenity/parking":{"name":"Car Parking","terms":""},"amenity/parking_entrance":{"name":"Parking Garage Entrance/Exit","terms":""},"amenity/parking_space":{"name":"Parking Space","terms":""},"amenity/pharmacy":{"name":"Pharmacy","terms":"drug,medicine"},"amenity/place_of_worship":{"name":"Place of Worship","terms":"abbey,basilica,bethel,cathedral,chancel,chantry,chapel,church,fold,house of God,house of prayer,house of worship,minster,mission,mosque,oratory,parish,sacellum,sanctuary,shrine,synagogue,tabernacle,temple"},"amenity/place_of_worship/buddhist":{"name":"Buddhist Temple","terms":"stupa,vihara,monastery,temple,pagoda,zendo,dojo"},"amenity/place_of_worship/christian":{"name":"Church","terms":"christian,abbey,basilica,bethel,cathedral,chancel,chantry,chapel,fold,house of God,house of prayer,house of worship,minster,mission,oratory,parish,sacellum,sanctuary,shrine,tabernacle,temple"},"amenity/place_of_worship/jewish":{"name":"Synagogue","terms":"jewish"},"amenity/place_of_worship/muslim":{"name":"Mosque","terms":"muslim"},"amenity/police":{"name":"Police","terms":"badge,constable,constabulary,cop,detective,fed,law,enforcement,officer,patrol"},"amenity/post_box":{"name":"Mailbox","terms":"letter,post"},"amenity/post_office":{"name":"Post Office","terms":"letter,mail"},"amenity/prison":{"name":"Prison Grounds","terms":"cell,jail"},"amenity/pub":{"name":"Pub","terms":"dive,beer,bier,booze"},"amenity/public_bookcase":{"name":"Public Bookcase","terms":"library,bookcrossing"},"amenity/ranger_station":{"name":"Ranger Station","terms":"visitor center,visitor centre,permit center,permit centre,backcountry office,warden office,warden center"},"amenity/recycling":{"name":"Recycling","terms":"can,bottle,garbage,scrap,trash"},"amenity/register_office":{"name":"Register Office","terms":""},"amenity/restaurant":{"name":"Restaurant","terms":"bar,breakfast,cafe,café,canteen,coffee,dine,dining,dinner,drive-in,eat,grill,lunch,table"},"amenity/sanitary_dump_station":{"name":"RV Toilet Disposal","terms":"Motor Home,Camper,Sanitary,Dump Station,Elsan,CDP,CTDP,Chemical Toilet"},"amenity/school":{"name":"School Grounds","terms":"academy,elementary school,middle school,high school"},"amenity/shelter":{"name":"Shelter","terms":"lean-to,gazebo,picnic"},"amenity/social_facility":{"name":"Social Facility","terms":""},"amenity/social_facility/food_bank":{"name":"Food Bank","terms":""},"amenity/social_facility/group_home":{"name":"Elderly Group Home","terms":"old,senior,living"},"amenity/social_facility/homeless_shelter":{"name":"Homeless Shelter","terms":"houseless,unhoused,displaced"},"amenity/studio":{"name":"Studio","terms":"recording,radio,television"},"amenity/swimming_pool":{"name":"Swimming Pool","terms":""},"amenity/taxi":{"name":"Taxi Stand","terms":"cab"},"amenity/telephone":{"name":"Telephone","terms":"phone"},"amenity/theatre":{"name":"Theater","terms":"theatre,performance,play,musical"},"amenity/toilets":{"name":"Toilets","terms":"bathroom,restroom,outhouse,privy,head,lavatory,latrine,water closet,WC,W.C."},"amenity/townhall":{"name":"Town Hall","terms":"village,city,government,courthouse,municipal"},"amenity/university":{"name":"University Grounds","terms":"college"},"amenity/vending_machine/cigarettes":{"name":"Cigarette Vending Machine","terms":"cigarette"},"amenity/vending_machine/condoms":{"name":"Condom Vending Machine","terms":"condom"},"amenity/vending_machine/drinks":{"name":"Drink Vending Machine","terms":"drink,soda,beverage,juice,pop"},"amenity/vending_machine/excrement_bags":{"name":"Excrement Bag Vending Machine","terms":"excrement bags,poop,dog,animal"},"amenity/vending_machine/news_papers":{"name":"Newspaper Vending Machine","terms":"newspaper"},"amenity/vending_machine/parcel_pickup_dropoff":{"name":"Parcel Pickup/Dropoff Vending Machine","terms":"parcel,mail,pickup"},"amenity/vending_machine/parking_tickets":{"name":"Parking Ticket Vending Machine","terms":"parking,ticket"},"amenity/vending_machine/public_transport_tickets":{"name":"Transit Ticket Vending Machine","terms":"bus,train,ferry,rail,ticket,transportation"},"amenity/vending_machine/sweets":{"name":"Snack Vending Machine","terms":"candy,gum,chip,pretzel,cookie,cracker"},"amenity/vending_machine/vending_machine":{"name":"Vending Machine","terms":""},"amenity/veterinary":{"name":"Veterinary","terms":"pet clinic,veterinarian,animal hospital,pet doctor"},"amenity/waste_basket":{"name":"Waste Basket","terms":"bin,rubbish,litter,trash,garbage"},"amenity/waste_disposal":{"name":"Garbage Dumpster","terms":"rubbish,litter,trash"},"amenity/water_point":{"name":"RV Drinking Water","terms":""},"area":{"name":"Area","terms":""},"area/highway":{"name":"Road Surface","terms":""},"barrier":{"name":"Barrier","terms":""},"barrier/block":{"name":"Block","terms":""},"barrier/bollard":{"name":"Bollard","terms":""},"barrier/cattle_grid":{"name":"Cattle Grid","terms":""},"barrier/city_wall":{"name":"City Wall","terms":""},"barrier/cycle_barrier":{"name":"Cycle Barrier","terms":""},"barrier/ditch":{"name":"Ditch","terms":""},"barrier/entrance":{"name":"Entrance","terms":""},"barrier/fence":{"name":"Fence","terms":""},"barrier/gate":{"name":"Gate","terms":""},"barrier/hedge":{"name":"Hedge","terms":""},"barrier/kissing_gate":{"name":"Kissing Gate","terms":""},"barrier/lift_gate":{"name":"Lift Gate","terms":""},"barrier/retaining_wall":{"name":"Retaining Wall","terms":""},"barrier/stile":{"name":"Stile","terms":""},"barrier/toll_booth":{"name":"Toll Booth","terms":""},"barrier/wall":{"name":"Wall","terms":""},"boundary/administrative":{"name":"Administrative Boundary","terms":""},"building":{"name":"Building","terms":""},"building/apartments":{"name":"Apartments","terms":""},"building/barn":{"name":"Barn","terms":""},"building/bunker":{"name":"Bunker","terms":""},"building/cabin":{"name":"Cabin","terms":""},"building/cathedral":{"name":"Cathedral Building","terms":""},"building/chapel":{"name":"Chapel Building","terms":""},"building/church":{"name":"Church Building","terms":""},"building/college":{"name":"College Building","terms":"university"},"building/commercial":{"name":"Commercial Building","terms":""},"building/construction":{"name":"Building Under Construction","terms":""},"building/detached":{"name":"Detached House","terms":"home,single,family,residence,dwelling"},"building/dormitory":{"name":"Dormitory","terms":""},"building/entrance":{"name":"Entrance/Exit","terms":""},"building/garage":{"name":"Garage","terms":""},"building/garages":{"name":"Garages","terms":""},"building/greenhouse":{"name":"Greenhouse","terms":""},"building/hospital":{"name":"Hospital Building","terms":""},"building/hotel":{"name":"Hotel Building","terms":""},"building/house":{"name":"House","terms":"home,family,residence,dwelling"},"building/hut":{"name":"Hut","terms":""},"building/industrial":{"name":"Industrial Building","terms":""},"building/kindergarten":{"name":"Preschool/Kindergarten Building","terms":"kindergarden,pre-school"},"building/public":{"name":"Public Building","terms":""},"building/residential":{"name":"Residential Building","terms":""},"building/retail":{"name":"Retail Building","terms":""},"building/roof":{"name":"Roof","terms":""},"building/school":{"name":"School Building","terms":"academy,elementary school,middle school,high school"},"building/semidetached_house":{"name":"Semi-Detached House","terms":"home,double,duplex,twin,family,residence,dwelling"},"building/shed":{"name":"Shed","terms":""},"building/stable":{"name":"Stable","terms":""},"building/static_caravan":{"name":"Static Mobile Home","terms":""},"building/terrace":{"name":"Row Houses","terms":"home,terrace,brownstone,family,residence,dwelling"},"building/train_station":{"name":"Train Station","terms":""},"building/university":{"name":"University Building","terms":"college"},"building/warehouse":{"name":"Warehouse","terms":""},"craft":{"name":"Craft","terms":""},"craft/basket_maker":{"name":"Basket Maker","terms":""},"craft/beekeeper":{"name":"Beekeeper","terms":""},"craft/blacksmith":{"name":"Blacksmith","terms":""},"craft/boatbuilder":{"name":"Boat Builder","terms":""},"craft/bookbinder":{"name":"Bookbinder","terms":"book repair"},"craft/brewery":{"name":"Brewery","terms":"beer,bier"},"craft/carpenter":{"name":"Carpenter","terms":"woodworker"},"craft/carpet_layer":{"name":"Carpet Layer","terms":""},"craft/caterer":{"name":"Caterer","terms":""},"craft/clockmaker":{"name":"Clockmaker","terms":""},"craft/confectionery":{"name":"Confectionery","terms":"sweets,candy"},"craft/dressmaker":{"name":"Dressmaker","terms":"seamstress"},"craft/electrician":{"name":"Electrician","terms":"power,wire"},"craft/gardener":{"name":"Gardener","terms":"landscaper,grounds keeper"},"craft/glaziery":{"name":"Glaziery","terms":"glass,stained-glass,window"},"craft/handicraft":{"name":"Handicraft","terms":""},"craft/hvac":{"name":"HVAC","terms":"heat*,vent*,air conditioning"},"craft/insulator":{"name":"Insulator","terms":""},"craft/jeweler":{"name":"Jeweler","terms":""},"craft/key_cutter":{"name":"Key Cutter","terms":""},"craft/locksmith":{"name":"Locksmith","terms":""},"craft/metal_construction":{"name":"Metal Construction","terms":""},"craft/optician":{"name":"Optician","terms":""},"craft/painter":{"name":"Painter","terms":""},"craft/photographer":{"name":"Photographer","terms":""},"craft/photographic_laboratory":{"name":"Photographic Laboratory","terms":"film"},"craft/plasterer":{"name":"Plasterer","terms":""},"craft/plumber":{"name":"Plumber","terms":"pipe"},"craft/pottery":{"name":"Pottery","terms":"ceramic"},"craft/rigger":{"name":"Rigger","terms":""},"craft/roofer":{"name":"Roofer","terms":""},"craft/saddler":{"name":"Saddler","terms":""},"craft/sailmaker":{"name":"Sailmaker","terms":""},"craft/sawmill":{"name":"Sawmill","terms":"lumber"},"craft/scaffolder":{"name":"Scaffolder","terms":""},"craft/sculpter":{"name":"Sculpter","terms":""},"craft/shoemaker":{"name":"Shoemaker","terms":"cobbler"},"craft/stonemason":{"name":"Stonemason","terms":"masonry"},"craft/sweep":{"name":"Chimney Sweep","terms":""},"craft/tailor":{"name":"Tailor","terms":"clothes,suit"},"craft/tiler":{"name":"Tiler","terms":""},"craft/tinsmith":{"name":"Tinsmith","terms":""},"craft/upholsterer":{"name":"Upholsterer","terms":""},"craft/watchmaker":{"name":"Watchmaker","terms":""},"craft/window_construction":{"name":"Window Construction","terms":"glass"},"craft/winery":{"name":"Winery","terms":""},"embankment":{"name":"Embankment","terms":""},"emergency/ambulance_station":{"name":"Ambulance Station","terms":"EMS,EMT,rescue"},"emergency/fire_hydrant":{"name":"Fire Hydrant","terms":""},"emergency/phone":{"name":"Emergency Phone","terms":""},"entrance":{"name":"Entrance/Exit","terms":""},"footway/crossing":{"name":"Street Crossing","terms":""},"footway/crosswalk":{"name":"Pedestrian Crosswalk","terms":"zebra crossing"},"footway/sidewalk":{"name":"Sidewalk","terms":""},"ford":{"name":"Ford","terms":""},"golf/bunker":{"name":"Sand Trap","terms":"hazard,bunker"},"golf/fairway":{"name":"Fairway","terms":""},"golf/green":{"name":"Putting Green","terms":""},"golf/hole":{"name":"Golf Hole","terms":""},"golf/lateral_water_hazard":{"name":"Lateral Water Hazard","terms":""},"golf/rough":{"name":"Rough","terms":""},"golf/tee":{"name":"Tee Box","terms":"teeing ground"},"golf/water_hazard":{"name":"Water Hazard","terms":""},"highway":{"name":"Highway","terms":""},"highway/bridleway":{"name":"Bridle Path","terms":"bridleway,equestrian,horse"},"highway/bus_stop":{"name":"Bus Stop","terms":""},"highway/corridor":{"name":"Indoor Corridor","terms":"gallery,hall,hallway,indoor,passage,passageway"},"highway/crossing":{"name":"Street Crossing","terms":""},"highway/crosswalk":{"name":"Pedestrian Crosswalk","terms":"zebra crossing"},"highway/cycleway":{"name":"Cycle Path","terms":"bike"},"highway/footway":{"name":"Foot Path","terms":"hike,hiking,trackway,trail,walk"},"highway/living_street":{"name":"Living Street","terms":""},"highway/mini_roundabout":{"name":"Mini-Roundabout","terms":""},"highway/motorway":{"name":"Motorway","terms":""},"highway/motorway_junction":{"name":"Motorway Junction / Exit","terms":""},"highway/motorway_link":{"name":"Motorway Link","terms":"ramp,on ramp,off ramp"},"highway/path":{"name":"Path","terms":"hike,hiking,trackway,trail,walk"},"highway/pedestrian":{"name":"Pedestrian Street","terms":""},"highway/primary":{"name":"Primary Road","terms":""},"highway/primary_link":{"name":"Primary Link","terms":"ramp,on ramp,off ramp"},"highway/raceway":{"name":"Motor Raceway","terms":"auto*,race*,nascar"},"highway/residential":{"name":"Residential Road","terms":""},"highway/rest_area":{"name":"Rest Area","terms":"rest stop"},"highway/road":{"name":"Unknown Road","terms":""},"highway/secondary":{"name":"Secondary Road","terms":""},"highway/secondary_link":{"name":"Secondary Link","terms":"ramp,on ramp,off ramp"},"highway/service":{"name":"Service Road","terms":""},"highway/service/alley":{"name":"Alley","terms":""},"highway/service/drive-through":{"name":"Drive-Through","terms":""},"highway/service/driveway":{"name":"Driveway","terms":""},"highway/service/emergency_access":{"name":"Emergency Access","terms":""},"highway/service/parking_aisle":{"name":"Parking Aisle","terms":""},"highway/services":{"name":"Service Area","terms":"services,travel plaza,service station"},"highway/steps":{"name":"Steps","terms":"stairs,staircase"},"highway/stop":{"name":"Stop Sign","terms":"stop sign"},"highway/street_lamp":{"name":"Street Lamp","terms":"streetlight,street light,lamp,light,gaslight"},"highway/tertiary":{"name":"Tertiary Road","terms":""},"highway/tertiary_link":{"name":"Tertiary Link","terms":"ramp,on ramp,off ramp"},"highway/track":{"name":"Unmaintained Track Road","terms":"woods road,forest road,logging road,fire road,farm road,agricultural road,ranch road,carriage road,primitive,unmaintained,rut,offroad,4wd,4x4,four wheel drive,atv,quad,jeep,double track,two track"},"highway/traffic_signals":{"name":"Traffic Signals","terms":"light,stoplight,traffic light"},"highway/trunk":{"name":"Trunk Road","terms":""},"highway/trunk_link":{"name":"Trunk Link","terms":"ramp,on ramp,off ramp"},"highway/turning_circle":{"name":"Turning Circle","terms":"cul-de-sac"},"highway/unclassified":{"name":"Minor/Unclassified Road","terms":""},"historic":{"name":"Historic Site","terms":""},"historic/archaeological_site":{"name":"Archaeological Site","terms":""},"historic/boundary_stone":{"name":"Boundary Stone","terms":""},"historic/castle":{"name":"Castle","terms":""},"historic/memorial":{"name":"Memorial","terms":""},"historic/monument":{"name":"Monument","terms":""},"historic/ruins":{"name":"Ruins","terms":""},"historic/wayside_cross":{"name":"Wayside Cross","terms":""},"historic/wayside_shrine":{"name":"Wayside Shrine","terms":""},"junction":{"name":"Junction","terms":""},"landuse":{"name":"Land Use","terms":""},"landuse/allotments":{"name":"Community Garden","terms":"allotment,garden"},"landuse/basin":{"name":"Basin","terms":""},"landuse/cemetery":{"name":"Cemetery","terms":""},"landuse/churchyard":{"name":"Churchyard","terms":""},"landuse/commercial":{"name":"Commercial Area","terms":""},"landuse/construction":{"name":"Construction","terms":""},"landuse/farm":{"name":"Farmland","terms":""},"landuse/farmland":{"name":"Farmland","terms":""},"landuse/farmyard":{"name":"Farmyard","terms":""},"landuse/forest":{"name":"Forest","terms":"tree"},"landuse/garages":{"name":"Garages","terms":""},"landuse/grass":{"name":"Grass","terms":""},"landuse/industrial":{"name":"Industrial Area","terms":""},"landuse/landfill":{"name":"Landfill","terms":"dump"},"landuse/meadow":{"name":"Meadow","terms":""},"landuse/military":{"name":"Military Area","terms":""},"landuse/orchard":{"name":"Orchard","terms":""},"landuse/plant_nursery":{"name":"Plant Nursery","terms":"vivero"},"landuse/quarry":{"name":"Quarry","terms":""},"landuse/residential":{"name":"Residential Area","terms":""},"landuse/retail":{"name":"Retail Area","terms":""},"landuse/vineyard":{"name":"Vineyard","terms":""},"leisure":{"name":"Leisure","terms":""},"leisure/adult_gaming_centre":{"name":"Adult Gaming Center","terms":"gambling,slot machine"},"leisure/bird_hide":{"name":"Bird Hide","terms":"machan,ornithology"},"leisure/bowling_alley":{"name":"Bowling Alley","terms":""},"leisure/common":{"name":"Common","terms":"open space"},"leisure/dog_park":{"name":"Dog Park","terms":""},"leisure/firepit":{"name":"Firepit","terms":"fireplace,campfire"},"leisure/garden":{"name":"Garden","terms":""},"leisure/golf_course":{"name":"Golf Course","terms":"links"},"leisure/ice_rink":{"name":"Ice Rink","terms":"hockey,skating,curling"},"leisure/marina":{"name":"Marina","terms":"boat"},"leisure/nature_reserve":{"name":"Nature Reserve","terms":"protected,wildlife"},"leisure/park":{"name":"Park","terms":"esplanade,estate,forest,garden,grass,green,grounds,lawn,lot,meadow,parkland,place,playground,plaza,pleasure garden,recreation area,square,tract,village green,woodland"},"leisure/picnic_table":{"name":"Picnic Table","terms":"bench"},"leisure/pitch":{"name":"Sport Pitch","terms":"field"},"leisure/pitch/american_football":{"name":"American Football Field","terms":""},"leisure/pitch/baseball":{"name":"Baseball Diamond","terms":""},"leisure/pitch/basketball":{"name":"Basketball Court","terms":""},"leisure/pitch/rugby_league":{"name":"Rugby League Field","terms":""},"leisure/pitch/rugby_union":{"name":"Rugby Union Field","terms":""},"leisure/pitch/skateboard":{"name":"Skate Park","terms":""},"leisure/pitch/soccer":{"name":"Soccer Field","terms":""},"leisure/pitch/tennis":{"name":"Tennis Court","terms":""},"leisure/pitch/volleyball":{"name":"Volleyball Court","terms":""},"leisure/playground":{"name":"Playground","terms":"jungle gym,play area"},"leisure/running_track":{"name":"Running Track","terms":""},"leisure/slipway":{"name":"Slipway","terms":"boat launch,boat ramp"},"leisure/sports_centre":{"name":"Sports Center / Gym","terms":"gym"},"leisure/sports_centre/swimming":{"name":"Swimming Pool Facility","terms":"dive,water"},"leisure/stadium":{"name":"Stadium","terms":""},"leisure/swimming_pool":{"name":"Swimming Pool","terms":"dive,water"},"leisure/track":{"name":"Racetrack (non-Motorsport)","terms":""},"leisure/water_park":{"name":"Water Park","terms":"swim,pool,dive"},"line":{"name":"Line","terms":""},"man_made":{"name":"Man Made","terms":""},"man_made/adit":{"name":"Adit","terms":"entrance,underground,mine,cave"},"man_made/breakwater":{"name":"Breakwater","terms":""},"man_made/chimney":{"name":"Chimney","terms":""},"man_made/cutline":{"name":"Cut line","terms":""},"man_made/embankment":{"name":"Embankment","terms":""},"man_made/flagpole":{"name":"Flagpole","terms":""},"man_made/gasometer":{"name":"Gasometer","terms":"gas holder"},"man_made/groyne":{"name":"Groyne","terms":""},"man_made/lighthouse":{"name":"Lighthouse","terms":""},"man_made/mast":{"name":"Radio Mast","terms":"broadcast tower,cell phone tower,cell tower,guyed tower,mobile phone tower,radio tower,television tower,transmission mast,transmission tower,tv tower"},"man_made/observation":{"name":"Observation Tower","terms":"lookout tower,fire tower"},"man_made/petroleum_well":{"name":"Oil Well","terms":"drilling rig,oil derrick,oil drill,oil horse,oil rig,oil pump,petroleum well,pumpjack"},"man_made/pier":{"name":"Pier","terms":"dock"},"man_made/pipeline":{"name":"Pipeline","terms":""},"man_made/silo":{"name":"Silo","terms":"grain,corn,wheat"},"man_made/storage_tank":{"name":"Storage Tank","terms":"water,oil,gas,petrol"},"man_made/surveillance":{"name":"Surveillance","terms":""},"man_made/survey_point":{"name":"Survey Point","terms":""},"man_made/tower":{"name":"Tower","terms":""},"man_made/wastewater_plant":{"name":"Wastewater Plant","terms":"sewage*,water treatment plant,reclamation plant"},"man_made/water_tower":{"name":"Water Tower","terms":""},"man_made/water_well":{"name":"Water Well","terms":""},"man_made/water_works":{"name":"Water Works","terms":""},"man_made/works":{"name":"Works","terms":"car assembly plant,aluminium processing plant,brewery,furniture manufacture factory,oil refinery,plastic recycling"},"military/airfield":{"name":"Airfield","terms":""},"military/barracks":{"name":"Barracks","terms":""},"military/bunker":{"name":"Bunker","terms":""},"military/checkpoint":{"name":"Checkpoint","terms":""},"military/danger_area":{"name":"Danger Area","terms":""},"military/naval_base":{"name":"Naval Base","terms":""},"military/obstacle_course":{"name":"Obstacle Course","terms":""},"military/range":{"name":"Military Range","terms":""},"military/training_area":{"name":"Training area","terms":""},"natural":{"name":"Natural","terms":""},"natural/bay":{"name":"Bay","terms":""},"natural/beach":{"name":"Beach","terms":""},"natural/cave_entrance":{"name":"Cave Entrance","terms":"cavern,hollow,grotto,shelter,cavity"},"natural/cliff":{"name":"Cliff","terms":""},"natural/coastline":{"name":"Coastline","terms":"shore"},"natural/fell":{"name":"Fell","terms":""},"natural/glacier":{"name":"Glacier","terms":""},"natural/grassland":{"name":"Grassland","terms":""},"natural/heath":{"name":"Heath","terms":""},"natural/peak":{"name":"Peak","terms":"acme,aiguille,alp,climax,crest,crown,hill,mount,mountain,pinnacle,summit,tip,top"},"natural/saddle":{"name":"Saddle","terms":"pass,mountain pass,top"},"natural/scree":{"name":"Scree","terms":"loose rocks"},"natural/scrub":{"name":"Scrub","terms":"bush,shrubs"},"natural/spring":{"name":"Spring","terms":""},"natural/tree":{"name":"Tree","terms":""},"natural/tree_row":{"name":"Tree row","terms":""},"natural/volcano":{"name":"Volcano","terms":"mountain,crater"},"natural/water":{"name":"Water","terms":""},"natural/water/lake":{"name":"Lake","terms":"lakelet,loch,mere"},"natural/water/pond":{"name":"Pond","terms":"lakelet,millpond,tarn,pool,mere"},"natural/water/reservoir":{"name":"Reservoir","terms":""},"natural/wetland":{"name":"Wetland","terms":""},"natural/wood":{"name":"Wood","terms":"tree"},"office":{"name":"Office","terms":""},"office/accountant":{"name":"Accountant","terms":""},"office/administrative":{"name":"Administrative Office","terms":""},"office/architect":{"name":"Architect","terms":""},"office/company":{"name":"Company Office","terms":""},"office/educational_institution":{"name":"Educational Institution","terms":""},"office/employment_agency":{"name":"Employment Agency","terms":"job"},"office/estate_agent":{"name":"Real Estate Office","terms":""},"office/financial":{"name":"Financial Office","terms":""},"office/government":{"name":"Government Office","terms":""},"office/insurance":{"name":"Insurance Office","terms":""},"office/it":{"name":"IT Office","terms":""},"office/lawyer":{"name":"Law Office","terms":""},"office/newspaper":{"name":"Newspaper","terms":""},"office/ngo":{"name":"NGO Office","terms":""},"office/physician":{"name":"Physician","terms":""},"office/political_party":{"name":"Political Party","terms":""},"office/research":{"name":"Research Office","terms":""},"office/telecommunication":{"name":"Telecom Office","terms":""},"office/therapist":{"name":"Therapist","terms":""},"office/travel_agent":{"name":"Travel Agency","terms":""},"piste":{"name":"Piste/Ski Trail","terms":"ski,sled,sleigh,snowboard,nordic,downhill,snowmobile"},"place":{"name":"Place","terms":""},"place/city":{"name":"City","terms":""},"place/farm":{"name":"Farm","terms":""},"place/hamlet":{"name":"Hamlet","terms":""},"place/island":{"name":"Island","terms":"archipelago,atoll,bar,cay,isle,islet,key,reef"},"place/isolated_dwelling":{"name":"Isolated Dwelling","terms":""},"place/locality":{"name":"Locality","terms":""},"place/neighbourhood":{"name":"Neighborhood","terms":"neighbourhood"},"place/suburb":{"name":"Borough","terms":"Boro,Quarter"},"place/town":{"name":"Town","terms":""},"place/village":{"name":"Village","terms":""},"point":{"name":"Point","terms":""},"power":{"name":"Power","terms":""},"power/generator":{"name":"Power Generator","terms":""},"power/line":{"name":"Power Line","terms":""},"power/minor_line":{"name":"Minor Power Line","terms":""},"power/pole":{"name":"Power Pole","terms":""},"power/sub_station":{"name":"Substation","terms":""},"power/substation":{"name":"Substation","terms":""},"power/tower":{"name":"High-Voltage Tower","terms":""},"power/transformer":{"name":"Transformer","terms":""},"public_transport/platform":{"name":"Platform","terms":""},"public_transport/stop_position":{"name":"Stop Position","terms":""},"railway":{"name":"Railway","terms":""},"railway/abandoned":{"name":"Abandoned Railway","terms":""},"railway/disused":{"name":"Disused Railway","terms":""},"railway/funicular":{"name":"Funicular","terms":"venicular,cliff railway,cable car,cable railway,funicular railway"},"railway/halt":{"name":"Railway Halt","terms":"break,interrupt,rest,wait,interruption"},"railway/level_crossing":{"name":"Railway Crossing","terms":"crossing,railroad crossing,level crossing,grade crossing,road through railroad,train crossing"},"railway/monorail":{"name":"Monorail","terms":""},"railway/narrow_gauge":{"name":"Narrow Gauge Rail","terms":"narrow gauge railway,narrow gauge railroad"},"railway/platform":{"name":"Railway Platform","terms":""},"railway/rail":{"name":"Rail","terms":""},"railway/station":{"name":"Railway Station","terms":"train station,station"},"railway/subway":{"name":"Subway","terms":""},"railway/subway_entrance":{"name":"Subway Entrance","terms":""},"railway/tram":{"name":"Tram","terms":"streetcar"},"relation":{"name":"Relation","terms":""},"roundabout":{"name":"Roundabout","terms":""},"route/ferry":{"name":"Ferry Route","terms":""},"shop":{"name":"Shop","terms":""},"shop/alcohol":{"name":"Liquor Store","terms":"alcohol,beer,booze,wine"},"shop/anime":{"name":"Anime Shop","terms":""},"shop/antiques":{"name":"Antiques Shop","terms":""},"shop/art":{"name":"Art Store","terms":"art*,exhibit*,gallery"},"shop/baby_goods":{"name":"Baby Goods Store","terms":""},"shop/bag":{"name":"Bag/Luggage Store","terms":"handbag,purse"},"shop/bakery":{"name":"Bakery","terms":""},"shop/bathroom_furnishing":{"name":"Bathroom Furnishing Store","terms":""},"shop/beauty":{"name":"Beauty Shop","terms":"nail spa,spa,salon,tanning"},"shop/bed":{"name":"Bedding/Mattress Store","terms":""},"shop/beverages":{"name":"Beverage Store","terms":""},"shop/bicycle":{"name":"Bicycle Shop","terms":"bike,repair"},"shop/bookmaker":{"name":"Bookmaker","terms":""},"shop/books":{"name":"Book Store","terms":""},"shop/boutique":{"name":"Boutique","terms":""},"shop/butcher":{"name":"Butcher","terms":"meat"},"shop/candles":{"name":"Candle Shop","terms":""},"shop/car":{"name":"Car Dealership","terms":"auto"},"shop/car_parts":{"name":"Car Parts Store","terms":"auto"},"shop/car_repair":{"name":"Car Repair Shop","terms":"auto"},"shop/carpet":{"name":"Carpet Store","terms":"rug"},"shop/cheese":{"name":"Cheese Store","terms":""},"shop/chemist":{"name":"Chemist","terms":""},"shop/chocolate":{"name":"Chocolate Store","terms":""},"shop/clothes":{"name":"Clothing Store","terms":""},"shop/coffee":{"name":"Coffee Store","terms":""},"shop/computer":{"name":"Computer Store","terms":""},"shop/confectionery":{"name":"Candy Store","terms":""},"shop/convenience":{"name":"Convenience Store","terms":""},"shop/copyshop":{"name":"Copy Store","terms":""},"shop/cosmetics":{"name":"Cosmetics Store","terms":""},"shop/craft":{"name":"Arts and Crafts Store","terms":"art*,paint*,frame"},"shop/curtain":{"name":"Curtain Store","terms":"drape*,window"},"shop/dairy":{"name":"Dairy Store","terms":"milk,egg,cheese"},"shop/deli":{"name":"Deli","terms":"lunch,meat,sandwich"},"shop/department_store":{"name":"Department Store","terms":""},"shop/doityourself":{"name":"DIY Store","terms":""},"shop/dry_cleaning":{"name":"Dry Cleaner","terms":""},"shop/electronics":{"name":"Electronics Store","terms":"appliance,audio,computer,tv"},"shop/erotic":{"name":"Erotic Store","terms":"sex,porn"},"shop/fabric":{"name":"Fabric Store","terms":"sew"},"shop/farm":{"name":"Produce Stand","terms":"farm shop,farm stand"},"shop/fashion":{"name":"Fashion Store","terms":""},"shop/fishmonger":{"name":"Fishmonger","terms":""},"shop/florist":{"name":"Florist","terms":"flower"},"shop/frame":{"name":"Framing Shop","terms":"art*,paint*,photo*,frame"},"shop/funeral_directors":{"name":"Funeral Home","terms":"undertaker,memorial home"},"shop/furnace":{"name":"Furnace Store","terms":"oven,stove"},"shop/furniture":{"name":"Furniture Store","terms":"chair,sofa,table"},"shop/garden_centre":{"name":"Garden Center","terms":"landscape,mulch,shrub,tree"},"shop/gift":{"name":"Gift Shop","terms":""},"shop/greengrocer":{"name":"Greengrocer","terms":"fruit,vegetable"},"shop/hairdresser":{"name":"Hairdresser","terms":""},"shop/hardware":{"name":"Hardware Store","terms":""},"shop/hearing_aids":{"name":"Hearing Aids Store","terms":""},"shop/herbalist":{"name":"Herbalist","terms":""},"shop/hifi":{"name":"Hifi Store","terms":"stereo,video"},"shop/houseware":{"name":"Houseware Store","terms":"home,household"},"shop/interior_decoration":{"name":"Interior Decoration Store","terms":""},"shop/jewelry":{"name":"Jeweler","terms":"diamond,gem,ring"},"shop/kiosk":{"name":"News Kiosk","terms":""},"shop/kitchen":{"name":"Kitchen Design Store","terms":""},"shop/laundry":{"name":"Laundry","terms":""},"shop/leather":{"name":"Leather Store","terms":""},"shop/locksmith":{"name":"Locksmith","terms":"key,lockpick"},"shop/lottery":{"name":"Lottery Shop","terms":""},"shop/mall":{"name":"Mall","terms":""},"shop/massage":{"name":"Massage Shop","terms":""},"shop/medical_supply":{"name":"Medical Supply Store","terms":""},"shop/mobile_phone":{"name":"Mobile Phone Store","terms":""},"shop/money_lender":{"name":"Money Lender","terms":""},"shop/motorcycle":{"name":"Motorcycle Dealership","terms":""},"shop/music":{"name":"Music Store","terms":"CD,vinyl"},"shop/musical_instrument":{"name":"Musical Instrument Store","terms":""},"shop/newsagent":{"name":"Newspaper/Magazine Shop","terms":""},"shop/nutrition_supplements":{"name":"Nutrition Supplements Store","terms":""},"shop/optician":{"name":"Optician","terms":"eye,glasses"},"shop/organic":{"name":"Organic Goods Store","terms":""},"shop/outdoor":{"name":"Outdoors Store","terms":"camping,climbing,hiking"},"shop/paint":{"name":"Paint Store","terms":""},"shop/pawnbroker":{"name":"Pawn Shop","terms":""},"shop/pet":{"name":"Pet Store","terms":"cat,dog,fish"},"shop/photo":{"name":"Photography Store","terms":"camera,film"},"shop/pyrotechnics":{"name":"Fireworks Store","terms":""},"shop/radiotechnics":{"name":"Radio/Electronic Component Store","terms":""},"shop/religion":{"name":"Religious Store","terms":""},"shop/scuba_diving":{"name":"Scuba Diving Shop","terms":""},"shop/seafood":{"name":"Seafood Shop","terms":"fishmonger"},"shop/second_hand":{"name":"Consignment/Thrift Store","terms":"secondhand,second hand,resale,thrift,used"},"shop/shoes":{"name":"Shoe Store","terms":""},"shop/sports":{"name":"Sporting Goods Store","terms":""},"shop/stationery":{"name":"Stationery Store","terms":"card,paper"},"shop/storage_rental":{"name":"Storage Rental","terms":""},"shop/supermarket":{"name":"Supermarket","terms":"grocery,store,shop"},"shop/tailor":{"name":"Tailor","terms":"clothes,suit"},"shop/tattoo":{"name":"Tattoo Parlor","terms":""},"shop/tea":{"name":"Tea Store","terms":""},"shop/ticket":{"name":"Ticket Seller","terms":""},"shop/tobacco":{"name":"Tobacco Shop","terms":""},"shop/toys":{"name":"Toy Store","terms":""},"shop/travel_agency":{"name":"Travel Agency","terms":""},"shop/tyres":{"name":"Tire Store","terms":""},"shop/vacant":{"name":"Vacant Shop","terms":""},"shop/vacuum_cleaner":{"name":"Vacuum Cleaner Store","terms":""},"shop/variety_store":{"name":"Variety Store","terms":""},"shop/video":{"name":"Video Store","terms":"DVD"},"shop/video_games":{"name":"Video Game Store","terms":""},"shop/water_sports":{"name":"Watersport/Swim Shop","terms":""},"shop/weapons":{"name":"Weapon Shop","terms":"ammo,gun,knife,knives"},"shop/window_blind":{"name":"Window Blind Store","terms":""},"shop/wine":{"name":"Wine Shop","terms":""},"tourism":{"name":"Tourism","terms":""},"tourism/alpine_hut":{"name":"Alpine Hut","terms":""},"tourism/artwork":{"name":"Artwork","terms":"mural,sculpture,statue"},"tourism/attraction":{"name":"Tourist Attraction","terms":""},"tourism/camp_site":{"name":"Camp Site","terms":"Tent"},"tourism/caravan_site":{"name":"RV Park","terms":"Motor Home,Camper"},"tourism/chalet":{"name":"Chalet","terms":""},"tourism/gallery":{"name":"Art Gallery","terms":"art*,exhibit*,paint*,photo*,sculpt*"},"tourism/guest_house":{"name":"Guest House","terms":"B&B,Bed and Breakfast"},"tourism/hostel":{"name":"Hostel","terms":""},"tourism/hotel":{"name":"Hotel","terms":""},"tourism/information":{"name":"Information","terms":""},"tourism/motel":{"name":"Motel","terms":""},"tourism/museum":{"name":"Museum","terms":"art*,exhibit*,gallery,foundation,hall,institution,paint*,photo*,sculpt*"},"tourism/picnic_site":{"name":"Picnic Site","terms":"camp"},"tourism/theme_park":{"name":"Theme Park","terms":""},"tourism/viewpoint":{"name":"Viewpoint","terms":""},"tourism/zoo":{"name":"Zoo","terms":""},"traffic_calming/bump":{"name":"Speed Bump","terms":"speed hump"},"traffic_calming/hump":{"name":"Speed Hump","terms":"speed bump"},"traffic_calming/rumble_strip":{"name":"Rumble Strip","terms":"sleeper lines,audible lines,growlers"},"traffic_calming/table":{"name":"Raised Pedestrian Crossing","terms":"speed table,flat top hump"},"type/boundary":{"name":"Boundary","terms":""},"type/boundary/administrative":{"name":"Administrative Boundary","terms":""},"type/multipolygon":{"name":"Multipolygon","terms":""},"type/restriction":{"name":"Restriction","terms":""},"type/restriction/no_left_turn":{"name":"No Left Turn","terms":""},"type/restriction/no_right_turn":{"name":"No Right Turn","terms":""},"type/restriction/no_straight_on":{"name":"No Straight On","terms":""},"type/restriction/no_u_turn":{"name":"No U-turn","terms":""},"type/restriction/only_left_turn":{"name":"Left Turn Only","terms":""},"type/restriction/only_right_turn":{"name":"Right Turn Only","terms":""},"type/restriction/only_straight_on":{"name":"No Turns","terms":""},"type/route":{"name":"Route","terms":""},"type/route/bicycle":{"name":"Cycle Route","terms":""},"type/route/bus":{"name":"Bus Route","terms":""},"type/route/detour":{"name":"Detour Route","terms":""},"type/route/ferry":{"name":"Ferry Route","terms":""},"type/route/foot":{"name":"Foot Route","terms":""},"type/route/hiking":{"name":"Hiking Route","terms":""},"type/route/pipeline":{"name":"Pipeline Route","terms":""},"type/route/power":{"name":"Power Route","terms":""},"type/route/road":{"name":"Road Route","terms":""},"type/route/train":{"name":"Train Route","terms":""},"type/route/tram":{"name":"Tram Route","terms":""},"type/route_master":{"name":"Route Master","terms":""},"type/site":{"name":"Site","terms":""},"vertex":{"name":"Other","terms":""},"waterway":{"name":"Waterway","terms":""},"waterway/boatyard":{"name":"Boatyard","terms":""},"waterway/canal":{"name":"Canal","terms":""},"waterway/dam":{"name":"Dam","terms":""},"waterway/ditch":{"name":"Ditch","terms":""},"waterway/dock":{"name":"Wet Dock / Dry Dock","terms":"boat,ship,vessel,marine"},"waterway/drain":{"name":"Drain","terms":""},"waterway/fuel":{"name":"Marine Fuel Station","terms":"petrol,gas,diesel,boat"},"waterway/river":{"name":"River","terms":"beck,branch,brook,course,creek,estuary,rill,rivulet,run,runnel,stream,tributary,watercourse"},"waterway/riverbank":{"name":"Riverbank","terms":""},"waterway/sanitary_dump_station":{"name":"Marine Toilet Disposal","terms":"Boat,Watercraft,Sanitary,Dump Station,Pumpout,Pump out,Elsan,CDP,CTDP,Chemical Toilet"},"waterway/stream":{"name":"Stream","terms":"beck,branch,brook,burn,course,creek,current,drift,flood,flow,freshet,race,rill,rindle,rivulet,run,runnel,rush,spate,spritz,surge,tide,torrent,tributary,watercourse"},"waterway/water_point":{"name":"Marine Drinking Water","terms":""},"waterway/weir":{"name":"Weir","terms":""}}}},"suggestions":{"amenity":{"fuel":{"76":{"count":314},"Neste":{"count":189},"BP":{"count":2511},"Shell":{"count":8380},"Agip":{"count":2651},"Migrol":{"count":65},"Avia":{"count":897},"Texaco":{"count":680},"Total":{"count":2607},"Statoil":{"count":596},"Esso":{"count":3652},"Jet":{"count":441},"Avanti":{"count":90},"Sainsbury's":{"count":58},"OMV":{"count":701},"Aral":{"count":1339},"Tesco":{"count":197},"JET":{"count":180},"Morrisons":{"count":111},"United":{"count":91},"Canadian Tire":{"count":66},"Mobil":{"count":613},"Caltex":{"count":1001},"Sunoco":{"count":355},"Q8":{"count":1161},"ABC":{"count":79},"ARAL":{"count":375},"CEPSA":{"count":1018},"BFT":{"count":89},"Petron":{"count":878},"Intermarché":{"count":434},"Total Access":{"count":51},"Super U":{"count":124},"Auchan":{"count":53},"Elf":{"count":129},"Carrefour":{"count":205},"Station Service E. Leclerc":{"count":530},"Shell Express":{"count":131},"Hess":{"count":127},"Flying V":{"count":129},"bft":{"count":168},"Gulf":{"count":199},"PTT":{"count":191},"St1":{"count":100},"Teboil":{"count":115},"HEM":{"count":212},"GALP":{"count":626},"OK":{"count":163},"ÖMV":{"count":101},"Tinq":{"count":215},"OKQ8":{"count":186},"Repsol":{"count":424},"Westfalen":{"count":96},"Esso Express":{"count":98},"Raiffeisenbank":{"count":117},"Tamoil":{"count":866},"Engen":{"count":241},"Sasol":{"count":59},"Topaz":{"count":78},"LPG":{"count":174},"Coop":{"count":62},"Orlen":{"count":598},"Oilibya":{"count":68},"Tango":{"count":122},"Star":{"count":319},"Петрол":{"count":84},"Cepsa":{"count":96},"OIL!":{"count":63},"Ultramar":{"count":125},"Irving":{"count":87},"Lukoil":{"count":701},"Petro-Canada":{"count":489},"7-Eleven":{"count":488},"Agrola":{"count":69},"Husky":{"count":126},"Slovnaft":{"count":219},"Sheetz":{"count":134},"Mol":{"count":61},"Petronas":{"count":159},"Газпромнефть":{"count":748},"Лукойл":{"count":1477},"Elan":{"count":112},"Роснефть":{"count":638},"Turmöl":{"count":57},"Neste A24":{"count":55},"Marathon":{"count":189},"Valero":{"count":366},"Eni":{"count":236},"Chevron":{"count":954},"ТНК":{"count":520},"REPSOL":{"count":1603},"MOL":{"count":228},"Bliska":{"count":150},"Api":{"count":302},"Arco":{"count":179},"Pemex":{"count":423},"Exxon":{"count":506},"Coles Express":{"count":115},"Petrom":{"count":259},"PETRONOR":{"count":207},"Rompetrol":{"count":174},"Lotos":{"count":178},"ОМВ":{"count":60},"BR":{"count":129},"Copec":{"count":505},"Petrobras":{"count":270},"Liberty":{"count":55},"IP":{"count":871},"Erg":{"count":596},"Eneos":{"count":97},"Citgo":{"count":279},"Metano":{"count":208},"Сургутнефтегаз":{"count":61},"EKO":{"count":59},"Eko":{"count":58},"Indipend.":{"count":172},"IES":{"count":63},"TotalErg":{"count":89},"Cenex":{"count":115},"ПТК":{"count":82},"HP":{"count":79},"Phillips 66":{"count":216},"CARREFOUR":{"count":74},"ERG":{"count":76},"Speedway":{"count":148},"Benzina":{"count":96},"Татнефть":{"count":264},"Terpel":{"count":259},"WOG":{"count":189},"Seaoil":{"count":54},"АЗС":{"count":1077},"Kwik Trip":{"count":108},"Wawa":{"count":89},"Pertamina":{"count":186},"COSMO":{"count":64},"Z":{"count":76},"Indian Oil":{"count":183},"АГЗС":{"count":494},"INA":{"count":121},"JOMO":{"count":62},"Holiday":{"count":97},"YPF":{"count":70},"IDEMITSU":{"count":87},"ENEOS":{"count":736},"Bharat Petroleum":{"count":64},"CAMPSA":{"count":615},"Casey's General Store":{"count":190},"Башнефть":{"count":60},"Kangaroo":{"count":60},"コスモ石油 (COSMO)":{"count":136},"MEROIL":{"count":77},"1-2-3":{"count":71},"出光":{"count":228,"tags":{"name:en":"IDEMITSU"}},"НК Альянс":{"count":88},"Sinclair":{"count":100},"Conoco":{"count":189},"SPBU":{"count":54},"Макпетрол":{"count":109},"Circle K":{"count":166},"Posto Ipiranga":{"count":70},"Posto Shell":{"count":54},"Phoenix":{"count":144},"Ipiranga":{"count":119},"OKKO":{"count":85},"ОККО":{"count":119},"บางจาก":{"count":60},"QuikTrip":{"count":105},"Stewart's":{"count":63},"Posto BR":{"count":68},"ป ต ท":{"count":152},"ปตท":{"count":88},"ANP":{"count":80},"Kum & Go":{"count":80},"Petrolimex":{"count":55},"Sokimex":{"count":66},"Tela":{"count":154},"Posto":{"count":71},"H-E-B":{"count":182},"Укрнафта":{"count":58},"Татнефтепродукт":{"count":54},"Afriquia":{"count":88},"Murphy USA":{"count":67},"昭和シェル (Showa-shell)":{"count":94},"エネオス":{"count":53},"CNG":{"count":94}},"pub":{"Kings Arms":{"count":67},"The Ship":{"count":89},"The White Horse":{"count":204},"The White Hart":{"count":226},"Royal Oak":{"count":150},"The Red Lion":{"count":233},"The Kings Arms":{"count":58},"The Star":{"count":73},"The Anchor":{"count":64},"The Cross Keys":{"count":55},"The Wheatsheaf":{"count":117},"The Crown Inn":{"count":67},"The Kings Head":{"count":53},"The Castle":{"count":62},"The Railway":{"count":102},"The White Lion":{"count":118},"The Bell":{"count":121},"The Bull":{"count":68},"The Plough":{"count":179},"The George":{"count":110},"The Royal Oak":{"count":209},"The Fox":{"count":74},"Prince of Wales":{"count":77},"The Rising Sun":{"count":71},"The Prince of Wales":{"count":51},"The Crown":{"count":244},"The Chequers":{"count":66},"The Swan":{"count":152},"Rose and Crown":{"count":79},"The Victoria":{"count":67},"New Inn":{"count":90},"Royal Hotel":{"count":57},"Red Lion":{"count":207},"Cross Keys":{"count":61},"The Greyhound":{"count":96},"The Black Horse":{"count":94},"The New Inn":{"count":105},"Kings Head":{"count":59},"The Albion":{"count":51},"The Angel":{"count":52},"The Queens Head":{"count":52},"The Ship Inn":{"count":83},"Rose & Crown":{"count":51},"Queens Head":{"count":52},"Irish Pub":{"count":76}},"fast_food":{"Quick":{"count":484},"McDonald's":{"count":12376,"tags":{"cuisine":"burger"}},"Subway":{"count":5576,"tags":{"cuisine":"sandwich"}},"Burger King":{"count":3734,"tags":{"cuisine":"burger"}},"Ali Baba":{"count":61},"Hungry Jacks":{"count":173,"tags":{"cuisine":"burger"}},"Red Rooster":{"count":148},"KFC":{"count":3198,"tags":{"cuisine":"chicken"}},"Domino's Pizza":{"count":985,"tags":{"cuisine":"pizza"}},"Chowking":{"count":142},"Jollibee":{"count":396},"Hesburger":{"count":102},"肯德基":{"count":86},"Wendy's":{"count":1621,"tags":{"cuisine":"burger"}},"Tim Hortons":{"count":323},"Steers":{"count":151},"Hardee's":{"count":268,"tags":{"cuisine":"burger"}},"Arby's":{"count":782},"A&W":{"count":283},"Dairy Queen":{"count":791},"Hallo Pizza":{"count":76},"Fish & Chips":{"count":93},"Harvey's":{"count":90},"麥當勞":{"count":65},"Pizza Pizza":{"count":215},"Kotipizza":{"count":74},"Jack in the Box":{"count":546,"tags":{"cuisine":"burger"}},"Istanbul":{"count":56},"Kochlöffel":{"count":68},"Döner":{"count":228},"Telepizza":{"count":201},"Sibylla":{"count":61},"Carl's Jr.":{"count":298,"tags":{"cuisine":"burger"}},"Quiznos":{"count":266,"tags":{"cuisine":"sandwich"}},"Wimpy":{"count":141},"Sonic":{"count":566,"tags":{"cuisine":"burger"}},"Taco Bell":{"count":1423,"tags":{"cuisine":"mexican"}},"Pizza Nova":{"count":63},"Papa John's":{"count":304,"tags":{"cuisine":"pizza"}},"Nordsee":{"count":159},"Mr. Sub":{"count":103},"Макдоналдс":{"count":324,"tags":{"name:en":"McDonald's"}},"Asia Imbiss":{"count":111},"Chipotle":{"count":290,"tags":{"cuisine":"mexican"}},"マクドナルド":{"count":692,"tags":{"name:en":"McDonald's","cuisine":"burger"}},"In-N-Out Burger":{"count":65},"Jimmy John's":{"count":141},"Jamba Juice":{"count":68},"Робин Сдобин":{"count":82},"Baskin Robbins":{"count":74},"ケンタッキーフライドチキン":{"count":164,"tags":{"name:en":"KFC","cuisine":"chicken"}},"吉野家":{"count":191},"Taco Time":{"count":88},"松屋":{"count":281,"tags":{"name:en":"Matsuya"}},"Little Caesars":{"count":81},"El Pollo Loco":{"count":63},"Del Taco":{"count":141},"White Castle":{"count":80},"Boston Market":{"count":66},"Chick-fil-A":{"count":257,"tags":{"cuisine":"chicken"}},"Panda Express":{"count":238,"tags":{"cuisine":"chinese"}},"Whataburger":{"count":364},"Taco John's":{"count":78},"Теремок":{"count":68},"Culver's":{"count":425},"Five Guys":{"count":141},"Church's Chicken":{"count":95},"Popeye's":{"count":167,"tags":{"cuisine":"chicken"}},"Long John Silver's":{"count":93},"Pollo Campero":{"count":62},"Zaxby's":{"count":51},"すき家":{"count":276,"tags":{"name:en":"SUKIYA"}},"モスバーガー":{"count":257,"tags":{"name:en":"MOS BURGER"}},"Русский Аппетит":{"count":69},"なか卯":{"count":63}},"restaurant":{"Pizza Hut":{"count":1180,"tags":{"cuisine":"pizza"}},"Little Chef":{"count":64},"Adler":{"count":158},"Zur Krone":{"count":90},"Deutsches Haus":{"count":90},"Krone":{"count":171},"Akropolis":{"count":152},"Schützenhaus":{"count":124},"Kreuz":{"count":74},"Waldschänke":{"count":55},"La Piazza":{"count":69},"Lamm":{"count":66},"Zur Sonne":{"count":73},"Zur Linde":{"count":204},"Poseidon":{"count":110},"Shanghai":{"count":82},"Red Lobster":{"count":235},"Zum Löwen":{"count":84},"Swiss Chalet":{"count":107},"Olympia":{"count":74},"Wagamama":{"count":64},"Frankie & Benny's":{"count":66},"Hooters":{"count":103},"Sternen":{"count":78},"Hirschen":{"count":79},"Denny's":{"count":450},"Athen":{"count":68},"Sonne":{"count":126},"Hirsch":{"count":79},"Ratskeller":{"count":150},"La Cantina":{"count":56},"Gasthaus Krone":{"count":56},"El Greco":{"count":86},"Gasthof zur Post":{"count":79},"Nando's":{"count":246},"Löwen":{"count":112},"La Pataterie":{"count":51},"Bella Napoli":{"count":53},"Pizza Express":{"count":262},"Mandarin":{"count":65},"Hong Kong":{"count":83},"Zizzi":{"count":68},"Cracker Barrel":{"count":183},"Rhodos":{"count":81},"Lindenhof":{"count":79},"Milano":{"count":54},"Dolce Vita":{"count":77},"Kirchenwirt":{"count":81},"Kantine":{"count":52},"Ochsen":{"count":95},"Spur":{"count":62},"Mykonos":{"count":59},"Lotus":{"count":66},"Applebee's":{"count":531},"Flunch":{"count":72},"Zur Post":{"count":116},"China Town":{"count":76},"La Dolce Vita":{"count":73},"Waffle House":{"count":207},"Delphi":{"count":88},"Linde":{"count":103},"Outback Steakhouse":{"count":218},"Dionysos":{"count":69},"Kelsey's":{"count":57},"Boston Pizza":{"count":165},"Bella Italia":{"count":132},"Sizzler":{"count":53},"Grüner Baum":{"count":116},"Taj Mahal":{"count":104},"Rössli":{"count":68},"Wimpy":{"count":51},"Traube":{"count":65},"Adria":{"count":52},"Red Robin":{"count":185},"Roma":{"count":61},"San Marco":{"count":67},"Hellas":{"count":55},"La Perla":{"count":67},"Vips":{"count":53},"Panera Bread":{"count":218},"Da Vinci":{"count":54},"Hippopotamus":{"count":96},"Prezzo":{"count":75},"Courtepaille":{"count":106},"Hard Rock Cafe":{"count":70},"Panorama":{"count":61},"デニーズ":{"count":82},"Sportheim":{"count":65},"餃子の王将":{"count":57},"Bären":{"count":60},"Alte Post":{"count":60},"Pizzeria Roma":{"count":51},"China Garden":{"count":66},"Vapiano":{"count":82},"Mamma Mia":{"count":64},"Schwarzer Adler":{"count":57},"IHOP":{"count":317},"Chili's":{"count":328},"Asia":{"count":51},"Olive Garden":{"count":279},"TGI Friday's":{"count":159},"Friendly's":{"count":78},"Buffalo Grill":{"count":202},"Texas Roadhouse":{"count":110},"ガスト":{"count":230,"tags":{"name:en":"Gusto"}},"Sakura":{"count":75},"Mensa":{"count":99},"The Keg":{"count":53},"サイゼリヤ":{"count":93},"La Strada":{"count":52},"Village Inn":{"count":92},"Buffalo Wild Wings":{"count":176},"Peking":{"count":59},"Boston Market":{"count":61},"Round Table Pizza":{"count":53},"Jimmy John's":{"count":69},"California Pizza Kitchen":{"count":61},"Якитория":{"count":77},"Golden Corral":{"count":101},"Perkins":{"count":105},"Ruby Tuesday":{"count":162},"Shari's":{"count":65},"Bob Evans":{"count":129},"바다횟집 (Bada Fish Restaurant)":{"count":55},"Mang Inasal":{"count":84},"Евразия":{"count":102},"ジョナサン":{"count":59},"Longhorn Steakhouse":{"count":66}},"bank":{"Chase":{"count":721},"Commonwealth Bank":{"count":232},"Citibank":{"count":277},"HSBC":{"count":1102},"Barclays":{"count":965},"Westpac":{"count":208},"NAB":{"count":131},"ANZ":{"count":218},"Lloyds Bank":{"count":547},"Landbank":{"count":81},"Sparkasse":{"count":4555},"UCPB":{"count":92},"PNB":{"count":244},"Metrobank":{"count":269},"BDO":{"count":290},"Volksbank":{"count":2591},"BPI":{"count":415},"Postbank":{"count":443},"NatWest":{"count":628},"Raiffeisenbank":{"count":2119},"Yorkshire Bank":{"count":63},"ABSA":{"count":95},"Standard Bank":{"count":109},"FNB":{"count":97},"Deutsche Bank":{"count":855},"SEB":{"count":133},"Commerzbank":{"count":806},"Targobank":{"count":166},"ABN AMRO":{"count":130},"Handelsbanken":{"count":184},"Swedbank":{"count":223},"Kreissparkasse":{"count":600},"UniCredit Bank":{"count":408},"Monte dei Paschi di Siena":{"count":132},"Caja Rural":{"count":99},"Dresdner Bank":{"count":66},"Sparda-Bank":{"count":320},"VÚB":{"count":107},"Slovenská sporiteľňa":{"count":134},"Bank of Montreal":{"count":118},"KBC":{"count":203},"Royal Bank of Scotland":{"count":111},"TSB":{"count":80},"US Bank":{"count":256},"HypoVereinsbank":{"count":561},"Bank Austria":{"count":176},"ING":{"count":496},"Erste Bank":{"count":180},"CIBC":{"count":326},"Scotiabank":{"count":413},"Caisse d'Épargne":{"count":882},"Santander":{"count":1323},"Bank of Scotland":{"count":89},"TD Canada Trust":{"count":450},"BMO":{"count":169},"Danske Bank":{"count":131},"OTP":{"count":192},"Crédit Agricole":{"count":1239},"LCL":{"count":553},"VR-Bank":{"count":430},"ČSOB":{"count":160},"Česká spořitelna":{"count":212},"BNP":{"count":112},"Royal Bank":{"count":65},"Nationwide":{"count":209},"Halifax":{"count":225},"BAWAG PSK":{"count":102},"National Bank":{"count":84},"Nedbank":{"count":80},"First National Bank":{"count":85},"Nordea":{"count":319},"Rabobank":{"count":609},"Sparkasse KölnBonn":{"count":69},"Tatra banka":{"count":67},"Berliner Sparkasse":{"count":62},"Berliner Volksbank":{"count":77},"Wells Fargo":{"count":874},"Credit Suisse":{"count":71},"Société Générale":{"count":634},"Osuuspankki":{"count":75},"Sparkasse Aachen":{"count":56},"Hamburger Sparkasse":{"count":156},"Cassa di Risparmio del Veneto":{"count":68},"BNP Paribas":{"count":617},"Banque Populaire":{"count":433},"BNP Paribas Fortis":{"count":209},"Banco Popular":{"count":291},"Bancaja":{"count":55},"Banesto":{"count":208},"La Caixa":{"count":583},"Santander Consumer Bank":{"count":88},"BRD":{"count":191},"BCR":{"count":143},"Banca Transilvania":{"count":141},"BW-Bank":{"count":97},"Komerční banka":{"count":132},"Banco Pastor":{"count":64},"Stadtsparkasse":{"count":86},"Ulster Bank":{"count":86},"Sberbank":{"count":58},"CIC":{"count":427},"Bancpost":{"count":56},"Caja Madrid":{"count":115},"Maybank":{"count":94},"中国银行":{"count":85},"Unicredit Banca":{"count":243},"Crédit Mutuel":{"count":690},"BBVA":{"count":647},"Intesa San Paolo":{"count":69},"TD Bank":{"count":206},"Belfius":{"count":231},"Bank of America":{"count":924},"RBC":{"count":230},"Alpha Bank":{"count":123},"Сбербанк":{"count":4794},"Россельхозбанк":{"count":201},"Crédit du Nord":{"count":96},"BancoEstado":{"count":80},"Millennium Bank":{"count":414},"State Bank of India":{"count":151},"Беларусбанк":{"count":242},"ING Bank Śląski":{"count":67},"Caixa Geral de Depósitos":{"count":129},"Kreissparkasse Köln":{"count":65},"Banco BCI":{"count":51},"Banco de Chile":{"count":98},"ВТБ24":{"count":326},"UBS":{"count":134},"PKO BP":{"count":265},"Chinabank":{"count":55},"PSBank":{"count":59},"Union Bank":{"count":124},"China Bank":{"count":66},"RCBC":{"count":122},"Unicaja":{"count":83},"BBK":{"count":79},"Ibercaja":{"count":69},"RBS":{"count":143},"Commercial Bank of Ceylon PLC":{"count":79},"Bank of Ireland":{"count":109},"BNL":{"count":87},"Banco Santander":{"count":138},"Banco Itaú":{"count":111},"AIB":{"count":72},"BZ WBK":{"count":77},"Banco do Brasil":{"count":557},"Caixa Econômica Federal":{"count":184},"Fifth Third Bank":{"count":84},"Banca Popolare di Vicenza":{"count":81},"Wachovia":{"count":58},"OLB":{"count":53},"みずほ銀行":{"count":78},"BES":{"count":72},"ICICI Bank":{"count":91},"HDFC Bank":{"count":91},"La Banque Postale":{"count":67},"Pekao SA":{"count":56},"Oberbank":{"count":90},"Bradesco":{"count":295},"Oldenburgische Landesbank":{"count":56},"Bendigo Bank":{"count":93},"Argenta":{"count":86},"AXA":{"count":68},"Axis Bank":{"count":61},"Banco Nación":{"count":67},"GE Money Bank":{"count":72},"Альфа-Банк":{"count":185},"Белагропромбанк":{"count":70},"Caja Círculo":{"count":65},"Banco Galicia":{"count":51},"Eurobank":{"count":97},"Banca Intesa":{"count":62},"Canara Bank":{"count":92},"Cajamar":{"count":77},"Banamex":{"count":149},"Crédit Mutuel de Bretagne":{"count":335},"Davivienda":{"count":83},"Bank Spółdzielczy":{"count":159},"Credit Agricole":{"count":157},"Bankinter":{"count":59},"Banque Nationale":{"count":63},"Bank of the West":{"count":96},"Key Bank":{"count":155},"Western Union":{"count":88},"Citizens Bank":{"count":115},"ПриватБанк":{"count":513},"Security Bank":{"count":78},"Millenium":{"count":60},"Bankia":{"count":149},"三菱東京UFJ銀行":{"count":159},"Caixa":{"count":117},"Banco de Costa Rica":{"count":63},"SunTrust Bank":{"count":73},"Itaú":{"count":338},"PBZ":{"count":52},"中国工商银行":{"count":51},"Bancolombia":{"count":89},"Райффайзен Банк Аваль":{"count":64},"Bancomer":{"count":115},"Banorte":{"count":80},"Alior Bank":{"count":81},"BOC":{"count":51},"Банк Москвы":{"count":118},"ВТБ":{"count":59},"Getin Bank":{"count":55},"Caja Duero":{"count":57},"Regions Bank":{"count":62},"Росбанк":{"count":177},"Banco Estado":{"count":72},"BCI":{"count":68},"SunTrust":{"count":68},"PNC Bank":{"count":254},"신한은행":{"count":217,"tags":{"name:en":"Sinhan Bank"}},"우리은행":{"count":291,"tags":{"name:en":"Uri Bank"}},"국민은행":{"count":165,"tags":{"name:en":"Gungmin Bank"}},"중소기업은행":{"count":52,"tags":{"name:en":"Industrial Bank of Korea"}},"광주은행":{"count":51,"tags":{"name:en":"Gwangju Bank"}},"Газпромбанк":{"count":100},"M&T Bank":{"count":92},"Caja de Burgos":{"count":51},"Santander Totta":{"count":69},"УкрСиббанк":{"count":192},"Ощадбанк":{"count":364},"Уралсиб":{"count":85},"りそな銀行":{"count":225,"tags":{"name:en":"Mizuho Bank"}},"Ecobank":{"count":66},"Cajero Automatico Bancared":{"count":145},"Промсвязьбанк":{"count":93},"三井住友銀行":{"count":129},"Banco Provincia":{"count":67},"BB&T":{"count":147},"Возрождение":{"count":59},"Capital One":{"count":59},"横浜銀行":{"count":51},"Bank Mandiri":{"count":62},"Banco de la Nación":{"count":92},"Banco G&T Continental":{"count":62},"Peoples Bank":{"count":60},"工商银行":{"count":51},"Совкомбанк":{"count":55},"Provincial":{"count":56},"Banco de Desarrollo Banrural":{"count":73},"Banco Bradesco":{"count":65},"Bicentenario":{"count":182},"ლიბერთი ბანკი":{"count":54,"tags":{"name:en":"Liberty Bank"}},"Banesco":{"count":108},"Mercantil":{"count":75},"Bank BRI":{"count":53},"Del Tesoro":{"count":91},"하나은행":{"count":77},"CityCommerce Bank":{"count":71},"De Venezuela":{"count":117}},"car_rental":{"Europcar":{"count":291},"Budget":{"count":92},"Sixt":{"count":161},"Avis":{"count":282},"Hertz":{"count":293},"Enterprise":{"count":199},"stadtmobil CarSharing-Station":{"count":148}},"pharmacy":{"Rowlands Pharmacy":{"count":71},"Boots":{"count":840},"Marien-Apotheke":{"count":314},"Mercury Drug":{"count":426},"Löwen-Apotheke":{"count":356},"Superdrug":{"count":117},"Sonnen-Apotheke":{"count":311},"Rathaus-Apotheke":{"count":132},"Engel-Apotheke":{"count":123},"Hirsch-Apotheke":{"count":83},"Stern-Apotheke":{"count":67},"Lloyds Pharmacy":{"count":295},"Rosen-Apotheke":{"count":208},"Stadt-Apotheke":{"count":302},"Markt-Apotheke":{"count":164},"Аптека":{"count":1989},"Pharmasave":{"count":64},"Brunnen-Apotheke":{"count":53},"Shoppers Drug Mart":{"count":430},"Apotheke am Markt":{"count":60},"Alte Apotheke":{"count":88},"Neue Apotheke":{"count":109},"Gintarinė vaistinė":{"count":101},"Rats-Apotheke":{"count":84},"Adler Apotheke":{"count":313},"Pharmacie Centrale":{"count":64},"Walgreens":{"count":1619},"Rite Aid":{"count":745},"Apotheke":{"count":165},"Linden-Apotheke":{"count":211},"Bahnhof-Apotheke":{"count":66},"Burg-Apotheke":{"count":55},"Jean Coutu":{"count":62},"Pharmaprix":{"count":60},"Farmacias Ahumada":{"count":104},"Farmacia Comunale":{"count":113},"Farmacias Cruz Verde":{"count":86},"Cruz Verde":{"count":99},"Hubertus Apotheke":{"count":52},"CVS":{"count":1560},"Farmacias SalcoBrand":{"count":133},"Фармация":{"count":120},"Bären-Apotheke":{"count":74},"Clicks":{"count":113},"セイジョー":{"count":53},"マツモトキヨシ":{"count":115},"Dr. Max":{"count":51},"Вита":{"count":106},"Радуга":{"count":70},"サンドラッグ":{"count":61},"Apteka":{"count":366},"Первая помощь":{"count":74},"Ригла":{"count":113},"Имплозия":{"count":63},"Kinney Drugs":{"count":68},"Классика":{"count":67},"Ljekarna":{"count":53},"SalcoBrand":{"count":88},"Аптека 36,6":{"count":224},"Фармакор":{"count":75},"スギ薬局":{"count":84},"Аптечный пункт":{"count":148},"Невис":{"count":60},"トモズ (Tomod's)":{"count":83},"Eurovaistinė":{"count":65},"Farmacity":{"count":68},"аптека":{"count":96},"The Generics Pharmacy":{"count":95},"Farmatodo":{"count":123},"Duane Reade":{"count":61},"H-E-B":{"count":262},"Фармленд":{"count":82},"ドラッグてらしま (Drug Terashima)":{"count":96},"Арніка":{"count":125},"ავერსი (Aversi)":{"count":62},"Farmahorro":{"count":58}},"cafe":{"Starbucks":{"count":4238,"tags":{"cuisine":"coffee_shop"}},"Cafeteria":{"count":115},"Costa":{"count":618},"Caffè Nero":{"count":169},"Кафе":{"count":226},"Café Central":{"count":61},"Second Cup":{"count":193},"Dunkin Donuts":{"count":428,"tags":{"cuisine":"donut"}},"Espresso House":{"count":53},"Segafredo":{"count":69},"Coffee Time":{"count":94},"Cafe Coffee Day":{"count":120},"Eiscafe Venezia":{"count":180},"スターバックス":{"count":251,"tags":{"name:en":"Starbucks"}},"Шоколадница":{"count":145},"Pret A Manger":{"count":119},"Столовая":{"count":391},"Jamba Juice":{"count":53},"ドトール":{"count":164,"tags":{"name:en":"DOUTOR"}},"Tchibo":{"count":100},"Кофе Хауз":{"count":104},"Caribou Coffee":{"count":100},"Уют":{"count":51},"Шашлычная":{"count":58},"คาเฟ่ อเมซอน":{"count":62},"Traveler's Coffee":{"count":60},"カフェ・ド・クリエ":{"count":67,"tags":{"name:en":"Cafe de CRIE"}},"Cafe Amazon":{"count":65}}},"shop":{"supermarket":{"Budgens":{"count":88},"Morrisons":{"count":411},"Interspar":{"count":142},"Merkur":{"count":107},"Sainsbury's":{"count":547},"Lidl":{"count":7130},"Edeka":{"count":2293},"Coles":{"count":400},"Iceland":{"count":315},"Coop":{"count":2100},"Tesco":{"count":1297},"Woolworths":{"count":541},"Zielpunkt":{"count":239},"Nahkauf":{"count":170},"Billa":{"count":1432},"Kaufland":{"count":1004},"Plus":{"count":120},"ALDI":{"count":5172},"Checkers":{"count":128},"Tesco Metro":{"count":137},"NP":{"count":153},"Penny":{"count":1759},"Norma":{"count":1068},"Asda":{"count":225},"Netto":{"count":4379},"Rewe":{"count":2645},"Aldi Süd":{"count":594},"Real":{"count":246},"Tesco Express":{"count":406},"King Soopers":{"count":72},"Kiwi":{"count":167},"Pick n Pay":{"count":241},"ICA":{"count":192},"Tengelmann":{"count":188},"Carrefour":{"count":1640},"Waitrose":{"count":258},"Spar":{"count":2386},"Hofer":{"count":442},"M-Preis":{"count":76},"tegut":{"count":210},"Sainsbury's Local":{"count":118},"E-Center":{"count":66},"Aldi Nord":{"count":210},"nahkauf":{"count":84},"Meijer":{"count":76},"Safeway":{"count":410},"Costco":{"count":152},"Albert":{"count":185},"Jumbo":{"count":194},"Shoprite":{"count":244},"MPreis":{"count":54},"Penny Market":{"count":429},"Tesco Extra":{"count":123},"Albert Heijn":{"count":476},"IGA":{"count":363},"Super U":{"count":488},"Metro":{"count":260},"Neukauf":{"count":77},"Migros":{"count":459},"Marktkauf":{"count":121},"Delikatesy Centrum":{"count":59},"C1000":{"count":307},"Hoogvliet":{"count":53},"Food Basics":{"count":75},"Casino":{"count":264},"Penny Markt":{"count":466},"Giant":{"count":191},"COOP Jednota":{"count":73},"Rema 1000":{"count":368},"Kaufpark":{"count":96},"ALDI SÜD":{"count":113},"Simply Market":{"count":330},"Konzum":{"count":230},"Carrefour Express":{"count":353},"Eurospar":{"count":270},"Mercator":{"count":125},"Famila":{"count":130},"Hemköp":{"count":82},"real,-":{"count":81},"Markant":{"count":88},"Volg":{"count":135},"Leader Price":{"count":267},"Treff 3000":{"count":94},"SuperBrugsen":{"count":67},"Kaiser's":{"count":256},"K+K":{"count":106},"Unimarkt":{"count":86},"Carrefour City":{"count":126},"Sobeys":{"count":122},"S-Market":{"count":109},"Combi":{"count":55},"Denner":{"count":276},"Konsum":{"count":133},"Franprix":{"count":312},"Monoprix":{"count":198},"Diska":{"count":69},"PENNY":{"count":79},"Dia":{"count":835},"Giant Eagle":{"count":85},"NORMA":{"count":115},"AD Delhaize":{"count":63},"Auchan":{"count":152},"Mercadona":{"count":769},"Consum":{"count":130},"Carrefour Market":{"count":80},"Whole Foods":{"count":210,"tags":{"shop":"supermarket"}},"Pam":{"count":56},"sky":{"count":105},"Despar":{"count":146},"Eroski":{"count":208},"Costcutter":{"count":63},"Maxi":{"count":108},"Colruyt":{"count":180},"The Co-operative":{"count":64},"Intermarché":{"count":1210},"Delhaize":{"count":207},"CBA":{"count":176},"Shopi":{"count":53},"Walmart":{"count":644},"Kroger":{"count":317},"Albertsons":{"count":242},"Trader Joe's":{"count":235},"Feneberg":{"count":58},"denn's Biomarkt":{"count":52},"dm":{"count":114},"Kvickly":{"count":55},"Makro":{"count":140},"Dico":{"count":53},"Nah & Frisch":{"count":73},"Champion":{"count":59},"ICA Supermarket":{"count":51},"Fakta":{"count":235},"Магнит":{"count":1760},"Caprabo":{"count":103},"Famiglia Cooperativa":{"count":64},"Народная 7Я семьЯ":{"count":154},"Esselunga":{"count":85},"Maxima":{"count":102},"Petit Casino":{"count":111},"Wasgau":{"count":60},"Pingo Doce":{"count":253},"Match":{"count":140},"Profi":{"count":60},"Lider":{"count":65},"Unimarc":{"count":177},"The Co-operative Food":{"count":190},"Santa Isabel":{"count":128},"Седьмой континент":{"count":79},"HIT":{"count":59},"Rimi":{"count":106},"Conad":{"count":304},"Фуршет":{"count":76},"Willys":{"count":56},"Farmfoods":{"count":64},"U Express":{"count":51},"Фора":{"count":52},"Dunnes Stores":{"count":73},"Сільпо":{"count":125},"マルエツ":{"count":59},"Piggly Wiggly":{"count":57},"Crai":{"count":54},"El Árbol":{"count":73},"Centre Commercial E. Leclerc":{"count":549},"Foodland":{"count":100},"Super Brugsen":{"count":67},"Дикси":{"count":683},"Пятёрочка":{"count":1344},"Publix":{"count":339},"Føtex":{"count":66},"coop":{"count":73},"Fressnapf":{"count":69},"Coop Konsum":{"count":79},"Carrefour Contact":{"count":83},"No Frills":{"count":105},"Plodine":{"count":52},"ADEG":{"count":68},"Minipreço":{"count":111},"Biedronka":{"count":1335},"Eurospin":{"count":155},"Семья":{"count":62},"Gadis":{"count":53},"Евроопт":{"count":68},"Centra":{"count":51},"Квартал":{"count":82},"New World":{"count":69},"Countdown":{"count":95},"Reliance Fresh":{"count":61},"Stokrotka":{"count":98},"Coop Jednota":{"count":74},"Fred Meyer":{"count":64},"Irma":{"count":58},"Continente":{"count":75},"Price Chopper":{"count":99},"Game":{"count":52},"Soriana":{"count":93},"Alimerka":{"count":64},"Piotr i Paweł":{"count":53},"Перекресток":{"count":312},"Maxima X":{"count":117},"Карусель":{"count":55},"ALDI Nord":{"count":51},"Condis":{"count":67},"Sam's Club":{"count":138},"Копейка":{"count":87},"Géant Casino":{"count":54},"ASDA":{"count":180},"Intermarche":{"count":115},"Stop & Shop":{"count":66},"Food Lion":{"count":216},"Harris Teeter":{"count":92},"Foodworks":{"count":62},"Polo Market":{"count":86},"Лента":{"count":51},"西友 (SEIYU)":{"count":58},"H-E-B":{"count":293},"Атак":{"count":53},"Полушка":{"count":139},"Extra":{"count":82},"Lewiatan":{"count":94},"Sigma":{"count":51},"АТБ":{"count":322},"Społem":{"count":55},"Bodega Aurrera":{"count":82},"Tesco Lotus":{"count":77},"Мария-Ра":{"count":108},"Магнолия":{"count":72},"Магазин":{"count":120},"Монетка":{"count":174},"Hy-Vee":{"count":75},"Walmart Supercenter":{"count":133},"Hannaford":{"count":57},"Wegmans":{"count":83},"業務スーパー":{"count":61},"Norfa XL":{"count":55},"ヨークマート (YorkMart)":{"count":64},"Leclerc Drive":{"count":76}},"electronics":{"Media Markt":{"count":285},"Maplin":{"count":65},"Best Buy":{"count":345},"Future Shop":{"count":73},"Saturn":{"count":134},"Currys":{"count":80},"Radio Shack":{"count":269},"Euronics":{"count":115},"Expert":{"count":123},"Эльдорадо":{"count":184},"Darty":{"count":74},"М.Видео":{"count":89},"ヤマダ電機":{"count":51}},"convenience":{"Shell":{"count":255},"Spar":{"count":1119},"McColl's":{"count":100},"Tesco Express":{"count":426},"Sainsbury's Local":{"count":104},"Aral":{"count":56},"One Stop":{"count":146},"The Co-operative Food":{"count":115},"Londis":{"count":352},"7-Eleven":{"count":4440},"CBA":{"count":135},"Coop":{"count":678},"Sale":{"count":80},"Statoil":{"count":69},"Sheetz":{"count":54},"Konzum":{"count":173},"Siwa":{"count":216},"Mercator":{"count":57},"Esso":{"count":67},"COOP Jednota":{"count":181},"Mac's":{"count":152},"Alepa":{"count":62},"Hasty Market":{"count":54},"K-Market":{"count":54},"Costcutter":{"count":292},"Valintatalo":{"count":62},"Casino":{"count":90},"Franprix":{"count":61},"Circle K":{"count":289},"セブンイレブン":{"count":3011,"tags":{"name:en":"7-Eleven"}},"ローソン":{"count":1596,"tags":{"name:en":"LAWSON"}},"BP":{"count":163},"Tesco":{"count":55},"Petit Casino":{"count":233},"Volg":{"count":116},"Mace":{"count":115},"Mini Market":{"count":272},"Nisa Local":{"count":77},"Dorfladen":{"count":75},"Продукты":{"count":4285},"Mini Stop":{"count":228},"LAWSON":{"count":419},"デイリーヤマザキ":{"count":141},"Biedronka":{"count":83},"Надежда":{"count":56},"Mobil":{"count":66},"Nisa":{"count":51},"Premier":{"count":129},"ABC":{"count":152},"ミニストップ":{"count":316,"tags":{"name:en":"MINISTOP"}},"サンクス":{"count":560,"tags":{"name:en":"sunkus"}},"スリーエフ":{"count":88},"8 à Huit":{"count":61},"Tchibo":{"count":56},"Żabka":{"count":546},"Almacen":{"count":229},"Vival":{"count":194},"FamilyMart":{"count":529},"ファミリーマート":{"count":1608,"tags":{"name:en":"FamilyMart"}},"Carrefour City":{"count":57},"Sunkus":{"count":62},"Casey's General Store":{"count":95},"セブンイレブン(Seven-Eleven)":{"count":65},"Jednota":{"count":58},"Магазин":{"count":915},"Гастроном":{"count":152},"Centra":{"count":111},"Магнит":{"count":701},"サークルK":{"count":538,"tags":{"name:en":"Circle K"}},"Wawa":{"count":135},"Proxi":{"count":123},"Универсам":{"count":78},"Перекресток":{"count":51},"Groszek":{"count":65},"Select":{"count":62},"Večerka":{"count":51},"Potraviny":{"count":249},"Смак":{"count":78},"Эконом":{"count":55},"Березка":{"count":77},"Społem":{"count":93},"Carrefour Express":{"count":84},"Cumberland Farms":{"count":63},"Chevron":{"count":59},"Coop Jednota":{"count":66},"Tesco Lotus Express":{"count":67},"Kiosk":{"count":55},"Sklep spożywczy":{"count":130},"24 часа":{"count":58},"Минимаркет":{"count":102},"Oxxo":{"count":669},"Пятёрочка":{"count":398},"abc":{"count":74},"7/11":{"count":51},"Stewart's":{"count":255},"Продукти":{"count":171},"ローソンストア100 (LAWSON STORE 100)":{"count":85},"Дикси":{"count":119},"Радуга":{"count":86},"ローソンストア100":{"count":76},"เซเว่นอีเลฟเว่น":{"count":185},"Delikatesy Centrum":{"count":53},"Citgo":{"count":62},"Фортуна":{"count":51},"Kum & Go":{"count":59},"Мария-Ра":{"count":76},"Picard":{"count":57},"Four Square":{"count":52},"Визит":{"count":57},"Авоська":{"count":55},"Dollar General":{"count":127},"Studenac":{"count":76},"Central Convenience Store":{"count":55},"Монетка":{"count":62},"продукты":{"count":114},"Теремок":{"count":56},"Kwik Trip":{"count":69},"Кулинария":{"count":55},"全家":{"count":90},"Мечта":{"count":54},"Epicerie":{"count":102},"Кировский":{"count":67},"Food Mart":{"count":117},"Delikatesy":{"count":81},"ポプラ":{"count":54},"Lewiatan":{"count":135},"Продуктовый магазин":{"count":149},"Продуктовый":{"count":84},"セイコーマート (Seicomart)":{"count":72},"Виктория":{"count":70},"Весна":{"count":57},"Mini Market Non-Stop":{"count":60},"QuikTrip":{"count":75},"Копеечка":{"count":51},"Royal Farms":{"count":51},"Alfamart":{"count":103},"Indomaret":{"count":141},"магазин":{"count":171},"全家便利商店":{"count":156},"Boutique":{"count":59},"მარკეტი (Market)":{"count":144},"Stores":{"count":61}},"chemist":{"dm":{"count":939},"Müller":{"count":212},"Schlecker":{"count":187},"Etos":{"count":467},"Bipa":{"count":289},"Rossmann":{"count":1669},"DM Drogeriemarkt":{"count":55},"Ihr Platz":{"count":73},"Douglas":{"count":62},"Kruidvat":{"count":123}},"car_repair":{"Peugeot":{"count":83},"Kwik Fit":{"count":128},"ATU":{"count":261},"Midas":{"count":202},"Feu Vert":{"count":113},"Norauto":{"count":152},"Speedy":{"count":115},"Автозапчасти":{"count":212},"Renault":{"count":171},"Pit Stop":{"count":58},"Jiffy Lube":{"count":198},"Шиномонтаж":{"count":1157},"СТО":{"count":395},"O'Reilly Auto Parts":{"count":81},"Carglass":{"count":112},"шиномонтаж":{"count":62},"Citroen":{"count":51},"Euromaster":{"count":87},"Firestone":{"count":88},"Автосервис":{"count":361},"Advance Auto Parts":{"count":52},"Roady":{"count":56}},"furniture":{"IKEA":{"count":169},"Jysk":{"count":109},"Roller":{"count":78},"Dänisches Bettenlager":{"count":309},"Conforama":{"count":99},"Matratzen Concord":{"count":52},"Мебель":{"count":210},"But":{"count":63}},"doityourself":{"Hornbach":{"count":123},"B&Q":{"count":225},"Hubo":{"count":77},"Mr Bricolage":{"count":88},"Gamma":{"count":111},"OBI":{"count":422},"Lowes":{"count":1152},"Wickes":{"count":123},"Hagebau":{"count":59},"Max Bahr":{"count":79},"Castorama":{"count":153},"Rona":{"count":61},"Home Depot":{"count":865},"Toom Baumarkt":{"count":71},"Homebase":{"count":225},"Baumax":{"count":95},"Lagerhaus":{"count":79},"Bauhaus":{"count":186},"Canadian Tire":{"count":97},"Leroy Merlin":{"count":209},"Hellweg":{"count":58},"Brico":{"count":98},"Bricomarché":{"count":235},"Toom":{"count":67},"Hagebaumarkt":{"count":107},"Praktiker":{"count":122},"Menards":{"count":70},"Weldom":{"count":73},"Bunnings Warehouse":{"count":91},"Ace Hardware":{"count":147},"Home Hardware":{"count":72},"Хозтовары":{"count":86},"Стройматериалы":{"count":197},"Bricorama":{"count":60},"Point P":{"count":59}},"stationery":{"Staples":{"count":299},"McPaper":{"count":83},"Office Depot":{"count":98},"Канцтовары":{"count":63}},"car":{"Skoda":{"count":97},"BMW":{"count":149},"Citroen":{"count":277},"Renault":{"count":382},"Mercedes-Benz":{"count":235},"Volvo":{"count":96},"Ford":{"count":239},"Volkswagen":{"count":217},"Mazda":{"count":105},"Mitsubishi":{"count":73},"Fiat":{"count":93},"Автозапчасти":{"count":277},"Opel":{"count":165},"Audi":{"count":121},"Toyota":{"count":271},"Nissan":{"count":189},"Suzuki":{"count":75},"Honda":{"count":157},"Peugeot":{"count":308},"Шиномонтаж":{"count":259},"Hyundai":{"count":166},"Subaru":{"count":58},"Chevrolet":{"count":86},"Автомагазин":{"count":72}},"clothes":{"Matalan":{"count":90},"KiK":{"count":1219},"H&M":{"count":658},"Urban Outfitters":{"count":63},"Vögele":{"count":132},"Zeeman":{"count":121},"Takko":{"count":515},"Adler":{"count":55},"C&A":{"count":506},"Zara":{"count":217},"Vero Moda":{"count":95},"NKD":{"count":486},"Ernsting's family":{"count":312},"Winners":{"count":65},"River Island":{"count":59},"Next":{"count":176},"Gap":{"count":81},"Adidas":{"count":92},"Woolworths":{"count":117},"Mr Price":{"count":88},"Jet":{"count":61},"Pep":{"count":134},"Edgars":{"count":110},"Ackermans":{"count":91},"Truworths":{"count":65},"Ross":{"count":93},"Burton":{"count":51},"Dorothy Perkins":{"count":53},"Deichmann":{"count":61},"Lindex":{"count":73},"s.Oliver":{"count":56},"Cecil":{"count":51},"Dress Barn":{"count":52},"Old Navy":{"count":174},"Jack & Jones":{"count":52},"Pimkie":{"count":73},"Esprit":{"count":231},"Primark":{"count":92},"Bonita":{"count":155},"Mexx":{"count":67},"Gerry Weber":{"count":71},"Tally Weijl":{"count":70},"Mango":{"count":133},"TK Maxx":{"count":84},"Benetton":{"count":101},"Ulla Popken":{"count":61},"AWG":{"count":66},"Tommy Hilfiger":{"count":75},"New Yorker":{"count":180},"Orsay":{"count":73},"Jeans Fritz":{"count":51},"Charles Vögele":{"count":69},"New Look":{"count":126},"Lacoste":{"count":78},"Etam":{"count":53},"Kiabi":{"count":148},"Jack Wolfskin":{"count":60},"American Apparel":{"count":57},"Men's Wearhouse":{"count":54},"Intimissimi":{"count":52},"United Colors of Benetton":{"count":96},"Jules":{"count":63},"Second Hand":{"count":53},"AOKI":{"count":57},"Calzedonia":{"count":68},"洋服の青山":{"count":100},"Levi's":{"count":63},"Celio":{"count":74},"TJ Maxx":{"count":57},"Promod":{"count":82},"Street One":{"count":72},"ユニクロ":{"count":59},"Banana Republic":{"count":57},"Одежда":{"count":75},"Marshalls":{"count":56},"La Halle":{"count":62},"Peacocks":{"count":89},"しまむら":{"count":60}},"books":{"Bruna":{"count":58},"Waterstones":{"count":90},"Libro":{"count":57},"Barnes & Noble":{"count":267},"Weltbild":{"count":74},"Thalia":{"count":121},"Книги":{"count":112}},"department_store":{"Debenhams":{"count":67},"Canadian Tire":{"count":75},"Karstadt":{"count":64},"Walmart":{"count":517},"Kmart":{"count":143},"Target":{"count":574},"Galeria Kaufhof":{"count":61},"Marks & Spencer":{"count":66},"Big W":{"count":57},"Woolworth":{"count":78},"Универмаг":{"count":72},"Sears":{"count":235},"Walmart Supercenter":{"count":101},"Kohl's":{"count":153},"Macy's":{"count":147},"Sam's Club":{"count":54},"JCPenney":{"count":66}},"alcohol":{"Alko":{"count":145},"The Beer Store":{"count":150},"Systembolaget":{"count":210},"LCBO":{"count":239},"Ароматный мир":{"count":62},"Bargain Booze":{"count":62},"Nicolas":{"count":119},"BWS":{"count":70},"Botilleria":{"count":77},"SAQ":{"count":72},"Gall & Gall":{"count":512},"Живое пиво":{"count":70}},"bakery":{"Kamps":{"count":252},"Banette":{"count":52},"Bäckerei Schmidt":{"count":57},"Anker":{"count":73},"Hofpfisterei":{"count":111},"Greggs":{"count":276},"Oebel":{"count":57},"Boulangerie":{"count":266},"Stadtbäckerei":{"count":57},"Steinecke":{"count":145},"Ihle":{"count":76},"Goldilocks":{"count":59},"Dat Backhus":{"count":67},"K&U":{"count":61},"Der Beck":{"count":96},"Thürmann":{"count":54},"Backwerk":{"count":95},"Schäfer's":{"count":51},"Panaderia":{"count":168},"Goeken backen":{"count":51},"Stadtbäckerei Junge":{"count":51},"Boulangerie Patisserie":{"count":119},"Paul":{"count":81},"Хлеб":{"count":89},"Пекарня":{"count":52},"Кулиничи":{"count":51}},"sports":{"Sports Direct":{"count":57},"Decathlon":{"count":309},"Intersport":{"count":283},"Sports Authority":{"count":75},"Спортмастер":{"count":87},"Sport 2000":{"count":90},"Dick's Sporting Goods":{"count":77}},"variety_store":{"Tedi":{"count":157},"Dollarama":{"count":103},"Family Dollar":{"count":61},"Dollar Tree":{"count":110},"Dollar General":{"count":80}},"pet":{"Fressnapf":{"count":318},"PetSmart":{"count":177},"Das Futterhaus":{"count":69},"Pets at Home":{"count":62},"Petco":{"count":101},"Зоомагазин":{"count":100}},"shoes":{"Deichmann":{"count":622},"Reno":{"count":183},"Ecco":{"count":55},"Clarks":{"count":109},"La Halle aux Chaussures":{"count":69},"Brantano":{"count":71},"Geox":{"count":51},"Salamander":{"count":51},"Обувь":{"count":100},"Payless Shoe Source":{"count":67},"Famous Footwear":{"count":59},"Quick Schuh":{"count":72},"Shoe Zone":{"count":55},"Foot Locker":{"count":82},"Bata":{"count":101},"ЦентрОбувь":{"count":51}},"toys":{"La Grande Récré":{"count":56},"Toys R Us":{"count":151,"tags":{"shop":"toys"}},"Intertoys":{"count":57},"Детский мир":{"count":86},"Игрушки":{"count":58}},"travel_agency":{"Flight Centre":{"count":92},"Thomas Cook":{"count":119}},"jewelry":{"Bijou Brigitte":{"count":57},"Christ":{"count":57},"Swarovski":{"count":74}},"optician":{"Fielmann":{"count":232},"Apollo Optik":{"count":150},"Vision Express":{"count":58},"Оптика":{"count":182},"Optic 2000":{"count":98},"Alain Afflelou":{"count":73},"Specsavers":{"count":124},"Krys":{"count":77},"Atol":{"count":55}},"video":{"Blockbuster":{"count":184},"World of Video":{"count":64}},"mobile_phone":{"Билайн":{"count":128},"ソフトバンクショップ (SoftBank shop)":{"count":255},"Vodafone":{"count":355},"O2":{"count":208},"Carphone Warehouse":{"count":127},"Orange":{"count":246},"Verizon Wireless":{"count":125},"Sprint":{"count":109},"T-Mobile":{"count":175},"МТС":{"count":352},"Евросеть":{"count":506},"Bell":{"count":190},"The Phone House":{"count":83},"SFR":{"count":71},"Связной":{"count":439},"Мегафон":{"count":251},"AT&T":{"count":124},"ドコモショップ (docomo shop)":{"count":114},"au":{"count":65},"Movistar":{"count":77},"Bitė":{"count":72}},"hifi":{},"computer":{"PC World":{"count":55},"DNS":{"count":128}},"hairdresser":{"Klier":{"count":119},"Supercuts":{"count":106},"Hairkiller":{"count":51},"Great Clips":{"count":182},"Парикмахерская":{"count":510},"Стиль":{"count":51},"Franck Provost":{"count":70},"Салон красоты":{"count":70}},"hardware":{"1000 мелочей":{"count":61},"Хозтовары":{"count":151},"Стройматериалы":{"count":54}},"motorcycle":{"Yamaha":{"count":67},"Honda":{"count":69}}}},"addressFormats":[{"format":[["housenumber","street"],["city","postcode"]]},{"countryCodes":["gb"],"format":[["housename"],["housenumber","street"],["city","postcode"]]},{"countryCodes":["ie"],"format":[["housename"],["housenumber","street"],["city"],["postcode"]]},{"countryCodes":["ad","at","ba","be","ch","cz","de","dk","es","fi","gr","hr","is","it","li","nl","no","pl","pt","se","si","sk","sm","va"],"format":[["street","housenumber"],["postcode","city"]]},{"countryCodes":["fr","lu","mo"],"format":[["housenumber","street"],["postcode","city"]]},{"countryCodes":["br"],"format":[["street"],["housenumber","suburb"],["city","postcode"]]},{"countryCodes":["vn"],"format":[["housenumber","street"],["subdistrict"],["district"],["city"],["province","postcode"]]},{"countryCodes":["us"],"format":[["housenumber","street"],["city","state","postcode"]]},{"countryCodes":["ca"],"format":[["housenumber","street"],["city","province","postcode"]]}]};