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'),
6227 var fetcher = function(val, cb) {
6228 cb(data.filter(function(d) {
6232 .indexOf(val.toLowerCase()) !== -1;
6236 var combobox = function(input) {
6238 container = d3.select(document.body)
6239 .selectAll('div.combobox')
6240 .filter(function(d) { return d === input.node(); }),
6241 shown = !container.empty();
6244 .classed('combobox-input', true)
6245 .on('focus.typeahead', focus)
6246 .on('blur.typeahead', blur)
6247 .on('keydown.typeahead', keydown)
6248 .on('keyup.typeahead', keyup)
6249 .on('input.typeahead', change)
6251 var parent = this.parentNode,
6252 sibling = this.nextSibling;
6254 var caret = d3.select(parent).selectAll('.combobox-caret')
6255 .filter(function(d) { return d === input.node(); })
6256 .data([input.node()]);
6258 caret.enter().insert('div', function() { return sibling; })
6259 .attr('class', 'combobox-caret');
6262 .on('mousedown', function () {
6263 // prevent the form element from blurring. it blurs
6265 d3.event.stopPropagation();
6266 d3.event.preventDefault();
6268 input.node().focus();
6277 fetch(value(), render);
6281 window.setTimeout(hide, 150);
6286 container = d3.select(document.body)
6287 .insert('div', ':first-child')
6288 .datum(input.node())
6289 .attr('class', 'combobox')
6291 position: 'absolute',
6295 .on('mousedown', function () {
6296 // prevent moving focus out of the text field
6297 d3.event.preventDefault();
6300 d3.select(document.body)
6301 .on('scroll.combobox', render, true);
6312 d3.select(document.body)
6313 .on('scroll.combobox', null);
6319 function keydown() {
6320 switch (d3.event.keyCode) {
6321 // backspace, delete
6324 input.on('input.typeahead', function() {
6327 var start = input.property('selectionStart');
6328 input.node().setSelectionRange(start, start);
6329 input.on('input.typeahead', change);
6334 container.selectAll('a.selected').each(event.accept);
6338 d3.event.preventDefault();
6343 d3.event.preventDefault();
6348 d3.event.preventDefault();
6351 d3.event.stopPropagation();
6355 switch (d3.event.keyCode) {
6362 container.selectAll('a.selected').each(event.accept);
6369 fetch(value(), function() {
6376 idx = Math.max(Math.min(idx + dir, suggestions.length - 1), 0);
6377 input.property('value', suggestions[idx].value);
6383 var value = input.property('value'),
6384 start = input.property('selectionStart'),
6385 end = input.property('selectionEnd');
6388 value = value.substring(0, start);
6394 function fetch(v, cb) {
6395 fetcher.call(input, v, function(_) {
6401 function autocomplete() {
6408 for (var i = 0; i < suggestions.length; i++) {
6409 if (suggestions[i].value.toLowerCase().indexOf(v.toLowerCase()) === 0) {
6410 var completion = suggestions[i].value;
6412 input.property('value', completion);
6413 input.node().setSelectionRange(v.length, completion.length);
6420 if (suggestions.length >= minItems && document.activeElement === input.node()) {
6427 var options = container
6428 .selectAll('a.combobox-option')
6429 .data(suggestions, function(d) { return d.value; });
6431 options.enter().append('a')
6432 .attr('class', 'combobox-option')
6433 .text(function(d) { return d.value; });
6436 .attr('title', function(d) { return d.title; })
6437 .classed('selected', function(d, i) { return i == idx; })
6438 .on('mouseover', select)
6439 .on('click', accept)
6445 var rect = input.node().getBoundingClientRect();
6448 'left': rect.left + 'px',
6449 'width': rect.width + 'px',
6450 'top': rect.height + rect.top + 'px'
6454 function select(d, i) {
6459 function ensureVisible() {
6460 var node = container.selectAll('a.selected').node();
6461 if (node) node.scrollIntoView();
6464 function accept(d) {
6467 .property('value', d.value)
6474 combobox.fetcher = function(_) {
6475 if (!arguments.length) return fetcher;
6480 combobox.data = function(_) {
6481 if (!arguments.length) return data;
6486 combobox.minItems = function(_) {
6487 if (!arguments.length) return minItems;
6492 return d3.rebind(combobox, event, 'on');
6495 d3.combobox.off = function(input) {
6500 .on('focus.typeahead', null)
6501 .on('blur.typeahead', null)
6502 .on('keydown.typeahead', null)
6503 .on('keyup.typeahead', null)
6504 .on('input.typeahead', null)
6506 d3.select(this.parentNode).selectAll('.combobox-caret')
6507 .filter(function(d) { return d === input.node(); })
6508 .on('mousedown', null);
6511 d3.select(document.body)
6512 .on('scroll.combobox', null);
6514 d3.geo.tile = function() {
6515 var size = [960, 500],
6517 scaleExtent = [0, 20],
6518 translate = [size[0] / 2, size[1] / 2],
6522 return Math.min(scaleExtent[1], Math.max(scaleExtent[0], _));
6526 var z = Math.max(Math.log(scale) / Math.LN2 - 8, 0),
6527 z0 = bound(Math.round(z + zoomDelta)),
6528 k = Math.pow(2, z - z0 + 8),
6529 origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k],
6531 cols = d3.range(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))),
6532 rows = d3.range(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1])));
6534 rows.forEach(function(y) {
6535 cols.forEach(function(x) {
6536 tiles.push([x, y, z0]);
6540 tiles.translate = origin;
6546 tile.scaleExtent = function(_) {
6547 if (!arguments.length) return scaleExtent;
6552 tile.size = function(_) {
6553 if (!arguments.length) return size;
6558 tile.scale = function(_) {
6559 if (!arguments.length) return scale;
6564 tile.translate = function(_) {
6565 if (!arguments.length) return translate;
6570 tile.zoomDelta = function(_) {
6571 if (!arguments.length) return zoomDelta;
6578 d3.jsonp = function (url, callback) {
6580 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
6582 while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
6586 function create(url) {
6587 var e = url.match(/callback=d3.jsonp.(\w+)/),
6588 c = e ? e[1] : rand();
6589 d3.jsonp[c] = function(data) {
6594 return 'd3.jsonp.' + c;
6597 var cb = create(url),
6598 script = d3.select('head')
6600 .attr('type', 'text/javascript')
6601 .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
6604 * This code is licensed under the MIT license.
6606 * Copyright © 2013, iD authors.
6608 * Portions copyright © 2011, Keith Cirkel
6609 * See https://github.com/keithamus/jwerty
6612 d3.keybinding = function(namespace) {
6615 function matches(binding, event) {
6616 for (var p in binding.event) {
6617 if (event[p] != binding.event[p])
6621 return (!binding.capture) === (event.eventPhase !== Event.CAPTURING_PHASE);
6624 function capture() {
6625 for (var i = 0; i < bindings.length; i++) {
6626 var binding = bindings[i];
6627 if (matches(binding, d3.event)) {
6634 var tagName = d3.select(d3.event.target).node().tagName;
6635 if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
6641 function keybinding(selection) {
6642 selection = selection || d3.select(document);
6643 selection.on('keydown.capture' + namespace, capture, true);
6644 selection.on('keydown.bubble' + namespace, bubble, false);
6648 keybinding.off = function(selection) {
6649 selection = selection || d3.select(document);
6650 selection.on('keydown.capture' + namespace, null);
6651 selection.on('keydown.bubble' + namespace, null);
6655 keybinding.on = function(code, callback, capture) {
6668 code = code.toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
6670 for (var i = 0; i < code.length; i++) {
6671 // Normalise matching errors
6672 if (code[i] === '++') code[i] = '+';
6674 if (code[i] in d3.keybinding.modifierCodes) {
6675 binding.event[d3.keybinding.modifierProperties[d3.keybinding.modifierCodes[code[i]]]] = true;
6676 } else if (code[i] in d3.keybinding.keyCodes) {
6677 binding.event.keyCode = d3.keybinding.keyCodes[code[i]];
6681 bindings.push(binding);
6690 d3.keybinding.modifierCodes = {
6693 // CTRL key, on Mac: ⌃
6695 // ALT key, on Mac: ⌥ (Alt)
6696 '⌥': 18, alt: 18, option: 18,
6697 // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
6698 '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
6701 d3.keybinding.modifierProperties = {
6708 d3.keybinding.keyCodes = {
6709 // Backspace key, on Mac: ⌫ (Backspace)
6710 '⌫': 8, backspace: 8,
6711 // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
6712 '⇥': 9, '⇆': 9, tab: 9,
6714 '↩': 13, 'return': 13, enter: 13, '⌅': 13,
6716 'pause': 19, 'pause-break': 19,
6718 '⇪': 20, caps: 20, 'caps-lock': 20,
6719 // Escape key, on Mac: ⎋, on Windows: Esc
6720 '⎋': 27, escape: 27, esc: 27,
6723 // Page-Up key, or pgup, on Mac: ↖
6724 '↖': 33, pgup: 33, 'page-up': 33,
6725 // Page-Down key, or pgdown, on Mac: ↘
6726 '↘': 34, pgdown: 34, 'page-down': 34,
6727 // END key, on Mac: ⇟
6729 // HOME key, on Mac: ⇞
6731 // Insert key, or ins
6732 ins: 45, insert: 45,
6733 // Delete key, on Mac: ⌦ (Delete)
6734 '⌦': 46, del: 46, 'delete': 46,
6735 // Left Arrow Key, or ←
6736 '←': 37, left: 37, 'arrow-left': 37,
6737 // Up Arrow Key, or ↑
6738 '↑': 38, up: 38, 'arrow-up': 38,
6739 // Right Arrow Key, or →
6740 '→': 39, right: 39, 'arrow-right': 39,
6741 // Up Arrow Key, or ↓
6742 '↓': 40, down: 40, 'arrow-down': 40,
6743 // odities, printing characters that come out wrong:
6746 // Num-Multiply, or *
6747 '*': 106, star: 106, asterisk: 106, multiply: 106,
6749 '+': 107, 'plus': 107,
6750 // Num-Subtract, or -
6751 '-': 109, subtract: 109,
6757 ';': 186, semicolon: 186,
6759 '=': 187, 'equals': 187,
6761 ',': 188, comma: 188,
6763 // Period, or ., or full-stop
6764 '.': 190, period: 190, 'full-stop': 190,
6765 // Slash, or /, or forward-slash
6766 '/': 191, slash: 191, 'forward-slash': 191,
6767 // Tick, or `, or back-quote
6768 '`': 192, tick: 192, 'back-quote': 192,
6769 // Open bracket, or [
6770 '[': 219, 'open-bracket': 219,
6772 '\\': 220, 'back-slash': 220,
6773 // Close backet, or ]
6774 ']': 221, 'close-bracket': 221,
6775 // Apostrophe, or Quote, or '
6776 '\'': 222, quote: 222, apostrophe: 222
6782 d3.keybinding.keyCodes['num-' + n] = i;
6789 d3.keybinding.keyCodes[n] = i;
6796 d3.keybinding.keyCodes['f' + n] = i;
6803 d3.keybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
6806 d3.selection.prototype.one = function (type, listener, capture) {
6807 var target = this, typeOnce = type + ".once";
6809 target.on(typeOnce, null);
6810 listener.apply(this, arguments);
6812 target.on(typeOnce, one, capture);
6815 d3.selection.prototype.dimensions = function (dimensions) {
6816 if (!arguments.length) {
6817 var node = this.node();
6818 return [node.offsetWidth,
6821 return this.attr({width: dimensions[0], height: dimensions[1]});
6823 d3.selection.prototype.trigger = function (type) {
6824 this.each(function() {
6825 var evt = document.createEvent('HTMLEvents');
6826 evt.initEvent(type, true, true);
6827 this.dispatchEvent(evt);
6830 d3.typeahead = function() {
6831 var event = d3.dispatch('accept'),
6832 autohighlight = false,
6835 var typeahead = function(selection) {
6838 idx = autohighlight ? 0 : -1;
6841 var rect = selection.node().getBoundingClientRect();
6842 container = d3.select(document.body)
6843 .append('div').attr('class', 'typeahead')
6845 position: 'absolute',
6846 left: rect.left + 'px',
6847 top: rect.bottom + 'px'
6850 .on('keyup.typeahead', key);
6856 idx = autohighlight ? 0 : -1;
6860 function slowHide() {
6861 if (autohighlight) {
6862 if (container.select('a.selected').node()) {
6863 select(container.select('a.selected').datum());
6867 window.setTimeout(hide, 150);
6871 .on('focus.typeahead', setup)
6872 .on('blur.typeahead', slowHide);
6875 var len = container.selectAll('a').data().length;
6876 if (d3.event.keyCode === 40) {
6877 idx = Math.min(idx + 1, len - 1);
6879 } else if (d3.event.keyCode === 38) {
6880 idx = Math.max(idx - 1, 0);
6882 } else if (d3.event.keyCode === 13) {
6883 if (container.select('a.selected').node()) {
6884 select(container.select('a.selected').datum());
6893 function highlight() {
6896 .classed('selected', function(d, i) { return i == idx; });
6900 if (hidden) setup();
6902 data(selection, function(data) {
6903 container.style('display', function() {
6904 return data.length ? 'block' : 'none';
6907 var options = container
6909 .data(data, function(d) { return d.value; });
6913 .text(function(d) { return d.value; })
6914 .attr('title', function(d) { return d.title; })
6915 .on('click', select);
6917 options.exit().remove();
6920 .classed('selected', function(d, i) { return i == idx; });
6924 function select(d) {
6926 .property('value', d.value)
6932 typeahead.data = function(_) {
6933 if (!arguments.length) return data;
6938 typeahead.autohighlight = function(_) {
6939 if (!arguments.length) return autohighlight;
6944 return d3.rebind(typeahead, event, 'on');
6946 // Tooltips and svg mask used to highlight certain features
6947 d3.curtain = function() {
6949 var event = d3.dispatch(),
6954 function curtain(selection) {
6956 surface = selection.append('svg')
6957 .attr('id', 'curtain')
6960 'pointer-events': 'none',
6961 'position': 'absolute',
6966 darkness = surface.append('path')
6970 'class': 'curtain-darkness'
6973 d3.select(window).on('resize.curtain', resize);
6975 tooltip = selection.append('div')
6976 .attr('class', 'tooltip')
6977 .style('z-index', 1002);
6979 tooltip.append('div').attr('class', 'tooltip-arrow');
6980 tooltip.append('div').attr('class', 'tooltip-inner');
6986 width: window.innerWidth,
6987 height: window.innerHeight
6989 curtain.cut(darkness.datum());
6993 curtain.reveal = function(box, text, tooltipclass, duration) {
6994 if (typeof box === 'string') box = d3.select(box).node();
6995 if (box.getBoundingClientRect) box = box.getBoundingClientRect();
6997 curtain.cut(box, duration);
7000 // pseudo markdown bold text hack
7001 var parts = text.split('**');
7002 var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
7003 if (parts[1]) html += '<span class="bold">' + parts[1] + '</span>';
7005 var dimensions = tooltip.classed('in', true)
7006 .select('.tooltip-inner')
7012 var w = window.innerWidth,
7013 h = window.innerHeight;
7015 if (box.top + box.height < Math.min(100, box.width + box.left)) {
7017 pos = [box.left + box.width / 2 - dimensions[0]/ 2, box.top + box.height];
7019 } else if (box.left + box.width + 300 < window.innerWidth) {
7021 pos = [box.left + box.width, box.top + box.height / 2 - dimensions[1] / 2];
7023 } else if (box.left > 300) {
7025 pos = [box.left - 200, box.top + box.height / 2 - dimensions[1] / 2];
7028 pos = [box.left, box.top + box.height];
7032 Math.min(Math.max(10, pos[0]), w - dimensions[0] - 10),
7033 Math.min(Math.max(10, pos[1]), h - dimensions[1] - 10)
7037 if (duration !== 0 || !tooltip.classed(side)) tooltip.call(iD.ui.Toggle(true));
7040 .style('top', pos[1] + 'px')
7041 .style('left', pos[0] + 'px')
7042 .attr('class', 'curtain-tooltip tooltip in ' + side + ' ' + tooltipclass)
7043 .select('.tooltip-inner')
7047 tooltip.call(iD.ui.Toggle(false));
7051 curtain.cut = function(datum, duration) {
7052 darkness.datum(datum);
7054 (duration === 0 ? darkness : darkness.transition().duration(duration || 600))
7055 .attr('d', function(d) {
7056 var string = "M 0,0 L 0," + window.innerHeight + " L " +
7057 window.innerWidth + "," + window.innerHeight + "L" +
7058 window.innerWidth + ",0 Z";
7060 if (!d) return string;
7061 return string + 'M' +
7062 d.left + ',' + d.top + 'L' +
7063 d.left + ',' + (d.top + d.height) + 'L' +
7064 (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
7065 (d.left + d.width) + ',' + (d.top) + 'Z';
7070 curtain.remove = function() {
7075 return d3.rebind(curtain, event, 'on');
7077 // Like selection.property('value', ...), but avoids no-op value sets,
7078 // which can result in layout/repaint thrashing in some situations.
7079 d3.selection.prototype.value = function(value) {
7080 function d3_selection_value(value) {
7081 function valueNull() {
7085 function valueConstant() {
7086 if (this.value !== value) this.value = value;
7089 function valueFunction() {
7090 var x = value.apply(this, arguments);
7091 if (x == null) delete this.value;
7092 else if (this.value !== x) this.value = x;
7095 return value == null
7096 ? valueNull : (typeof value === "function"
7097 ? valueFunction : valueConstant);
7100 if (!arguments.length) return this.property('value');
7101 return this.each(d3_selection_value(value));
7103 // Copyright (c) 2006, 2008 Tony Garnock-Jones <tonyg@lshift.net>
7104 // Copyright (c) 2006, 2008 LShift Ltd. <query@lshift.net>
7106 // Permission is hereby granted, free of charge, to any person
7107 // obtaining a copy of this software and associated documentation files
7108 // (the "Software"), to deal in the Software without restriction,
7109 // including without limitation the rights to use, copy, modify, merge,
7110 // publish, distribute, sublicense, and/or sell copies of the Software,
7111 // and to permit persons to whom the Software is furnished to do so,
7112 // subject to the following conditions:
7114 // The above copyright notice and this permission notice shall be
7115 // included in all copies or substantial portions of the Software.
7117 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
7118 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
7119 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
7120 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
7121 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
7122 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7123 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7126 // source: https://bitbucket.org/lshift/synchrotron/src
7128 Diff3 = (function() {
7132 longest_common_subsequence: function(file1, file2) {
7133 /* Text diff algorithm following Hunt and McIlroy 1976.
7134 * J. W. Hunt and M. D. McIlroy, An algorithm for differential file
7135 * comparison, Bell Telephone Laboratories CSTR #41 (1976)
7136 * http://www.cs.dartmouth.edu/~doug/
7138 * Expects two arrays of strings.
7140 var equivalenceClasses;
7145 var c, i, j, jX, r, s;
7147 equivalenceClasses = {};
7148 for (j = 0; j < file2.length; j++) {
7150 if (equivalenceClasses[line]) {
7151 equivalenceClasses[line].push(j);
7153 equivalenceClasses[line] = [j];
7157 candidates = [{file1index: -1,
7161 for (i = 0; i < file1.length; i++) {
7163 file2indices = equivalenceClasses[line] || [];
7168 for (jX = 0; jX < file2indices.length; jX++) {
7169 j = file2indices[jX];
7171 for (s = 0; s < candidates.length; s++) {
7172 if ((candidates[s].file2index < j) &&
7173 ((s == candidates.length - 1) ||
7174 (candidates[s + 1].file2index > j)))
7178 if (s < candidates.length) {
7179 newCandidate = {file1index: i,
7181 chain: candidates[s]};
7182 if (r == candidates.length) {
7189 if (r == candidates.length) {
7190 break; // no point in examining further (j)s
7198 // At this point, we know the LCS: it's in the reverse of the
7199 // linked-list through .chain of
7200 // candidates[candidates.length - 1].
7202 return candidates[candidates.length - 1];
7205 diff_comm: function(file1, file2) {
7206 // We apply the LCS to build a "comm"-style picture of the
7207 // differences between file1 and file2.
7210 var tail1 = file1.length;
7211 var tail2 = file2.length;
7212 var common = {common: []};
7214 function processCommon() {
7215 if (common.common.length) {
7216 common.common.reverse();
7217 result.push(common);
7218 common = {common: []};
7222 for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7224 candidate = candidate.chain)
7226 var different = {file1: [], file2: []};
7228 while (--tail1 > candidate.file1index) {
7229 different.file1.push(file1[tail1]);
7232 while (--tail2 > candidate.file2index) {
7233 different.file2.push(file2[tail2]);
7236 if (different.file1.length || different.file2.length) {
7238 different.file1.reverse();
7239 different.file2.reverse();
7240 result.push(different);
7244 common.common.push(file1[tail1]);
7254 diff_patch: function(file1, file2) {
7255 // We apply the LCD to build a JSON representation of a
7256 // diff(1)-style patch.
7259 var tail1 = file1.length;
7260 var tail2 = file2.length;
7262 function chunkDescription(file, offset, length) {
7264 for (var i = 0; i < length; i++) {
7265 chunk.push(file[offset + i]);
7267 return {offset: offset,
7272 for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7274 candidate = candidate.chain)
7276 var mismatchLength1 = tail1 - candidate.file1index - 1;
7277 var mismatchLength2 = tail2 - candidate.file2index - 1;
7278 tail1 = candidate.file1index;
7279 tail2 = candidate.file2index;
7281 if (mismatchLength1 || mismatchLength2) {
7282 result.push({file1: chunkDescription(file1,
7283 candidate.file1index + 1,
7285 file2: chunkDescription(file2,
7286 candidate.file2index + 1,
7295 strip_patch: function(patch) {
7296 // Takes the output of Diff3.diff_patch(), and removes
7297 // information from it. It can still be used by patch(),
7298 // below, but can no longer be inverted.
7300 for (var i = 0; i < patch.length; i++) {
7301 var chunk = patch[i];
7302 newpatch.push({file1: {offset: chunk.file1.offset,
7303 length: chunk.file1.length},
7304 file2: {chunk: chunk.file2.chunk}});
7309 invert_patch: function(patch) {
7310 // Takes the output of Diff3.diff_patch(), and inverts the
7311 // sense of it, so that it can be applied to file2 to give
7312 // file1 rather than the other way around.
7314 for (var i = 0; i < patch.length; i++) {
7315 var chunk = patch[i];
7316 var tmp = chunk.file1;
7317 chunk.file1 = chunk.file2;
7322 patch: function (file, patch) {
7323 // Applies a patch to a file.
7325 // Given file1 and file2, Diff3.patch(file1,
7326 // Diff3.diff_patch(file1, file2)) should give file2.
7329 var commonOffset = 0;
7331 function copyCommon(targetOffset) {
7332 while (commonOffset < targetOffset) {
7333 result.push(file[commonOffset]);
7338 for (var chunkIndex = 0; chunkIndex < patch.length; chunkIndex++) {
7339 var chunk = patch[chunkIndex];
7340 copyCommon(chunk.file1.offset);
7341 for (var lineIndex = 0; lineIndex < chunk.file2.chunk.length; lineIndex++) {
7342 result.push(chunk.file2.chunk[lineIndex]);
7344 commonOffset += chunk.file1.length;
7347 copyCommon(file.length);
7351 diff_indices: function(file1, file2) {
7352 // We apply the LCS to give a simple representation of the
7353 // offsets and lengths of mismatched chunks in the input
7354 // files. This is used by diff3_merge_indices below.
7357 var tail1 = file1.length;
7358 var tail2 = file2.length;
7360 for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7362 candidate = candidate.chain)
7364 var mismatchLength1 = tail1 - candidate.file1index - 1;
7365 var mismatchLength2 = tail2 - candidate.file2index - 1;
7366 tail1 = candidate.file1index;
7367 tail2 = candidate.file2index;
7369 if (mismatchLength1 || mismatchLength2) {
7370 result.push({file1: [tail1 + 1, mismatchLength1],
7371 file2: [tail2 + 1, mismatchLength2]});
7379 diff3_merge_indices: function (a, o, b) {
7380 // Given three files, A, O, and B, where both A and B are
7381 // independently derived from O, returns a fairly complicated
7382 // internal representation of merge decisions it's taken. The
7383 // interested reader may wish to consult
7385 // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce. "A
7386 // Formal Investigation of Diff3." In Arvind and Prasad,
7387 // editors, Foundations of Software Technology and Theoretical
7388 // Computer Science (FSTTCS), December 2007.
7390 // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
7393 var m1 = Diff3.diff_indices(o, a);
7394 var m2 = Diff3.diff_indices(o, b);
7397 function addHunk(h, side) {
7398 hunks.push([h.file1[0], side, h.file1[1], h.file2[0], h.file2[1]]);
7400 for (i = 0; i < m1.length; i++) { addHunk(m1[i], 0); }
7401 for (i = 0; i < m2.length; i++) { addHunk(m2[i], 2); }
7405 var commonOffset = 0;
7406 function copyCommon(targetOffset) {
7407 if (targetOffset > commonOffset) {
7408 result.push([1, commonOffset, targetOffset - commonOffset]);
7409 commonOffset = targetOffset;
7413 for (var hunkIndex = 0; hunkIndex < hunks.length; hunkIndex++) {
7414 var firstHunkIndex = hunkIndex;
7415 var hunk = hunks[hunkIndex];
7416 var regionLhs = hunk[0];
7417 var regionRhs = regionLhs + hunk[2];
7418 while (hunkIndex < hunks.length - 1) {
7419 var maybeOverlapping = hunks[hunkIndex + 1];
7420 var maybeLhs = maybeOverlapping[0];
7421 if (maybeLhs > regionRhs) break;
7422 regionRhs = maybeLhs + maybeOverlapping[2];
7426 copyCommon(regionLhs);
7427 if (firstHunkIndex == hunkIndex) {
7428 // The "overlap" was only one hunk long, meaning that
7429 // there's no conflict here. Either a and o were the
7430 // same, or b and o were the same.
7432 result.push([hunk[1], hunk[3], hunk[4]]);
7435 // A proper conflict. Determine the extents of the
7436 // regions involved from a, o and b. Effectively merge
7437 // all the hunks on the left into one giant hunk, and
7438 // do the same for the right; then, correct for skew
7439 // in the regions of o that each side changed, and
7440 // report appropriate spans for the three sides.
7442 0: [a.length, -1, o.length, -1],
7443 2: [b.length, -1, o.length, -1]
7445 for (i = firstHunkIndex; i <= hunkIndex; i++) {
7448 var r = regions[side];
7450 var oRhs = oLhs + hunk[2];
7451 var abLhs = hunk[3];
7452 var abRhs = abLhs + hunk[4];
7453 r[0] = Math.min(abLhs, r[0]);
7454 r[1] = Math.max(abRhs, r[1]);
7455 r[2] = Math.min(oLhs, r[2]);
7456 r[3] = Math.max(oRhs, r[3]);
7458 var aLhs = regions[0][0] + (regionLhs - regions[0][2]);
7459 var aRhs = regions[0][1] + (regionRhs - regions[0][3]);
7460 var bLhs = regions[2][0] + (regionLhs - regions[2][2]);
7461 var bRhs = regions[2][1] + (regionRhs - regions[2][3]);
7464 regionLhs, regionRhs - regionLhs,
7465 bLhs, bRhs - bLhs]);
7467 commonOffset = regionRhs;
7470 copyCommon(o.length);
7474 diff3_merge: function (a, o, b, excludeFalseConflicts) {
7475 // Applies the output of Diff3.diff3_merge_indices to actually
7476 // construct the merged file; the returned result alternates
7477 // between "ok" and "conflict" blocks.
7480 var files = [a, o, b];
7481 var indices = Diff3.diff3_merge_indices(a, o, b);
7484 function flushOk() {
7485 if (okLines.length) {
7486 result.push({ok: okLines});
7490 function pushOk(xs) {
7491 for (var j = 0; j < xs.length; j++) {
7492 okLines.push(xs[j]);
7496 function isTrueConflict(rec) {
7497 if (rec[2] != rec[6]) return true;
7500 for (var j = 0; j < rec[2]; j++) {
7501 if (a[j + aoff] != b[j + boff]) return true;
7506 for (var i = 0; i < indices.length; i++) {
7510 if (excludeFalseConflicts && !isTrueConflict(x)) {
7511 pushOk(files[0].slice(x[1], x[1] + x[2]));
7514 result.push({conflict: {a: a.slice(x[1], x[1] + x[2]),
7516 o: o.slice(x[3], x[3] + x[4]),
7518 b: b.slice(x[5], x[5] + x[6]),
7522 pushOk(files[side].slice(x[1], x[1] + x[2]));
7533 if (typeof module !== 'undefined') module.exports = Diff3;
7534 var JXON = new (function () {
7536 sValueProp = "keyValue", sAttributesProp = "keyAttributes", sAttrPref = "@", /* you can customize these values */
7537 aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
7539 function parseText (sValue) {
7540 if (rIsNull.test(sValue)) { return null; }
7541 if (rIsBool.test(sValue)) { return sValue.toLowerCase() === "true"; }
7542 if (isFinite(sValue)) { return parseFloat(sValue); }
7543 if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
7547 function EmptyTree () { }
7548 EmptyTree.prototype.toString = function () { return "null"; };
7549 EmptyTree.prototype.valueOf = function () { return null; };
7551 function objectify (vValue) {
7552 return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
7555 function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
7557 nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
7558 bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
7561 sProp, vContent, nLength = 0, sCollectedTxt = "",
7562 vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
7565 for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
7566 oNode = oParentNode.childNodes.item(nItem);
7567 if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is "CDATASection" (4) */
7568 else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is "Text" (3) */
7569 else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is "Element" (1) */
7573 var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
7575 if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
7577 for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
7578 sProp = aCache[nElId].nodeName.toLowerCase();
7579 vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
7580 if (vResult.hasOwnProperty(sProp)) {
7581 if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
7582 vResult[sProp].push(vContent);
7584 vResult[sProp] = vContent;
7591 nAttrLen = oParentNode.attributes.length,
7592 sAPrefix = bNesteAttr ? "" : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
7594 for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
7595 oAttrib = oParentNode.attributes.item(nAttrib);
7596 oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
7600 if (bFreeze) { Object.freeze(oAttrParent); }
7601 vResult[sAttributesProp] = oAttrParent;
7602 nLength -= nAttrLen - 1;
7606 if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
7607 vResult[sValueProp] = vBuiltVal;
7608 } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
7609 vResult = vBuiltVal;
7612 if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
7614 aCache.length = nLevelStart;
7619 function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
7622 if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
7623 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
7624 } else if (oParentObj.constructor === Date) {
7625 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));
7628 for (var sName in oParentObj) {
7629 vValue = oParentObj[sName];
7630 if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
7631 if (sName === sValueProp) {
7632 if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
7633 } else if (sName === sAttributesProp) { /* verbosity level is 3 */
7634 for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
7635 } else if (sName.charAt(0) === sAttrPref) {
7636 oParentEl.setAttribute(sName.slice(1), vValue);
7637 } else if (vValue.constructor === Array) {
7638 for (var nItem = 0; nItem < vValue.length; nItem++) {
7639 oChild = oXMLDoc.createElement(sName);
7640 loadObjTree(oXMLDoc, oChild, vValue[nItem]);
7641 oParentEl.appendChild(oChild);
7644 oChild = oXMLDoc.createElement(sName);
7645 if (vValue instanceof Object) {
7646 loadObjTree(oXMLDoc, oChild, vValue);
7647 } else if (vValue !== null && vValue !== true) {
7648 oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
7650 oParentEl.appendChild(oChild);
7655 this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
7656 var _nVerb = arguments.length > 1 && typeof nVerbosity === "number" ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
7657 return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);
7660 this.unbuild = function (oObjTree) {
7661 var oNewDoc = document.implementation.createDocument("", "", null);
7662 loadObjTree(oNewDoc, oNewDoc, oObjTree);
7666 this.stringify = function (oObjTree) {
7667 return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
7670 // var myObject = JXON.build(doc);
7671 // we got our javascript object! try: alert(JSON.stringify(myObject));
7673 // var newDoc = JXON.unbuild(myObject);
7674 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
7677 * lodash 3.9.3 (Custom Build) <https://lodash.com/>
7678 * 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"`
7679 * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
7680 * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
7681 * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
7682 * Available under MIT license <https://lodash.com/license>
7686 /** Used as a safe reference for `undefined` in pre-ES5 environments. */
7689 /** Used as the semantic version number. */
7690 var VERSION = '3.9.3';
7692 /** Used to compose bitmasks for wrapper metadata. */
7695 CURRY_BOUND_FLAG = 4,
7697 CURRY_RIGHT_FLAG = 16,
7699 PARTIAL_RIGHT_FLAG = 64,
7703 /** Used to detect when a function becomes hot. */
7704 var HOT_COUNT = 150,
7707 /** Used to indicate the type of lazy iteratees. */
7708 var LAZY_DROP_WHILE_FLAG = 0,
7709 LAZY_FILTER_FLAG = 1,
7712 /** Used as the `TypeError` message for "Functions" methods. */
7713 var FUNC_ERROR_TEXT = 'Expected a function';
7715 /** Used as the internal argument placeholder. */
7716 var PLACEHOLDER = '__lodash_placeholder__';
7718 /** `Object#toString` result references. */
7719 var argsTag = '[object Arguments]',
7720 arrayTag = '[object Array]',
7721 boolTag = '[object Boolean]',
7722 dateTag = '[object Date]',
7723 errorTag = '[object Error]',
7724 funcTag = '[object Function]',
7725 mapTag = '[object Map]',
7726 numberTag = '[object Number]',
7727 objectTag = '[object Object]',
7728 regexpTag = '[object RegExp]',
7729 setTag = '[object Set]',
7730 stringTag = '[object String]',
7731 weakMapTag = '[object WeakMap]';
7733 var arrayBufferTag = '[object ArrayBuffer]',
7734 float32Tag = '[object Float32Array]',
7735 float64Tag = '[object Float64Array]',
7736 int8Tag = '[object Int8Array]',
7737 int16Tag = '[object Int16Array]',
7738 int32Tag = '[object Int32Array]',
7739 uint8Tag = '[object Uint8Array]',
7740 uint8ClampedTag = '[object Uint8ClampedArray]',
7741 uint16Tag = '[object Uint16Array]',
7742 uint32Tag = '[object Uint32Array]';
7744 /** Used to match property names within property paths. */
7745 var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,
7746 reIsPlainProp = /^\w*$/,
7747 rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g;
7750 * Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special).
7751 * In addition to special characters the forward slash is escaped to allow for
7752 * easier `eval` use and `Function` compilation.
7754 var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g,
7755 reHasRegExpChars = RegExp(reRegExpChars.source);
7757 /** Used to match backslashes in property paths. */
7758 var reEscapeChar = /\\(\\)?/g;
7760 /** Used to match `RegExp` flags from their coerced string values. */
7761 var reFlags = /\w*$/;
7763 /** Used to detect host constructors (Safari > 5). */
7764 var reIsHostCtor = /^\[object .+?Constructor\]$/;
7766 /** Used to detect unsigned integer values. */
7767 var reIsUint = /^\d+$/;
7769 /** Used to fix the JScript `[[DontEnum]]` bug. */
7771 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
7772 'toLocaleString', 'toString', 'valueOf'
7775 /** Used to identify `toStringTag` values of typed arrays. */
7776 var typedArrayTags = {};
7777 typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
7778 typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
7779 typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
7780 typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
7781 typedArrayTags[uint32Tag] = true;
7782 typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
7783 typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
7784 typedArrayTags[dateTag] = typedArrayTags[errorTag] =
7785 typedArrayTags[funcTag] = typedArrayTags[mapTag] =
7786 typedArrayTags[numberTag] = typedArrayTags[objectTag] =
7787 typedArrayTags[regexpTag] = typedArrayTags[setTag] =
7788 typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
7790 /** Used to identify `toStringTag` values supported by `_.clone`. */
7791 var cloneableTags = {};
7792 cloneableTags[argsTag] = cloneableTags[arrayTag] =
7793 cloneableTags[arrayBufferTag] = cloneableTags[boolTag] =
7794 cloneableTags[dateTag] = cloneableTags[float32Tag] =
7795 cloneableTags[float64Tag] = cloneableTags[int8Tag] =
7796 cloneableTags[int16Tag] = cloneableTags[int32Tag] =
7797 cloneableTags[numberTag] = cloneableTags[objectTag] =
7798 cloneableTags[regexpTag] = cloneableTags[stringTag] =
7799 cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
7800 cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
7801 cloneableTags[errorTag] = cloneableTags[funcTag] =
7802 cloneableTags[mapTag] = cloneableTags[setTag] =
7803 cloneableTags[weakMapTag] = false;
7805 /** Used as an internal `_.debounce` options object by `_.throttle`. */
7806 var debounceOptions = {
7812 /** Used to determine if values are of the language type `Object`. */
7818 /** Detect free variable `exports`. */
7819 var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
7821 /** Detect free variable `module`. */
7822 var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
7824 /** Detect free variable `global` from Node.js. */
7825 var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global;
7827 /** Detect free variable `self`. */
7828 var freeSelf = objectTypes[typeof self] && self && self.Object && self;
7830 /** Detect free variable `window`. */
7831 var freeWindow = objectTypes[typeof window] && window && window.Object && window;
7833 /** Detect the popular CommonJS extension `module.exports`. */
7834 var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
7837 * Used as a reference to the global object.
7839 * The `this` value is used if it's the global object to avoid Greasemonkey's
7840 * restricted `window` object, otherwise the `window` object is used.
7842 var root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this;
7844 /*--------------------------------------------------------------------------*/
7847 * The base implementation of `_.findIndex` and `_.findLastIndex` without
7848 * support for callback shorthands and `this` binding.
7851 * @param {Array} array The array to search.
7852 * @param {Function} predicate The function invoked per iteration.
7853 * @param {boolean} [fromRight] Specify iterating from right to left.
7854 * @returns {number} Returns the index of the matched value, else `-1`.
7856 function baseFindIndex(array, predicate, fromRight) {
7857 var length = array.length,
7858 index = fromRight ? length : -1;
7860 while ((fromRight ? index-- : ++index < length)) {
7861 if (predicate(array[index], index, array)) {
7869 * The base implementation of `_.indexOf` without support for binary searches.
7872 * @param {Array} array The array to search.
7873 * @param {*} value The value to search for.
7874 * @param {number} fromIndex The index to search from.
7875 * @returns {number} Returns the index of the matched value, else `-1`.
7877 function baseIndexOf(array, value, fromIndex) {
7878 if (value !== value) {
7879 return indexOfNaN(array, fromIndex);
7881 var index = fromIndex - 1,
7882 length = array.length;
7884 while (++index < length) {
7885 if (array[index] === value) {
7893 * The base implementation of `_.isFunction` without support for environments
7894 * with incorrect `typeof` results.
7897 * @param {*} value The value to check.
7898 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
7900 function baseIsFunction(value) {
7901 // Avoid a Chakra JIT bug in compatibility modes of IE 11.
7902 // See https://github.com/jashkenas/underscore/issues/1621 for more details.
7903 return typeof value == 'function' || false;
7907 * Converts `value` to a string if it's not one. An empty string is returned
7908 * for `null` or `undefined` values.
7911 * @param {*} value The value to process.
7912 * @returns {string} Returns the string.
7914 function baseToString(value) {
7915 if (typeof value == 'string') {
7918 return value == null ? '' : (value + '');
7922 * Gets the index at which the first occurrence of `NaN` is found in `array`.
7925 * @param {Array} array The array to search.
7926 * @param {number} fromIndex The index to search from.
7927 * @param {boolean} [fromRight] Specify iterating from right to left.
7928 * @returns {number} Returns the index of the matched `NaN`, else `-1`.
7930 function indexOfNaN(array, fromIndex, fromRight) {
7931 var length = array.length,
7932 index = fromIndex + (fromRight ? 0 : -1);
7934 while ((fromRight ? index-- : ++index < length)) {
7935 var other = array[index];
7936 if (other !== other) {
7944 * Checks if `value` is a host object in IE < 9.
7947 * @param {*} value The value to check.
7948 * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
7950 var isHostObject = (function() {
7952 Object({ 'toString': 0 } + '');
7954 return function() { return false; };
7956 return function(value) {
7957 // IE < 9 presents many host objects as `Object` objects that can coerce
7958 // to strings despite having improperly defined `toString` methods.
7959 return typeof value.toString != 'function' && typeof (value + '') == 'string';
7964 * Checks if `value` is object-like.
7967 * @param {*} value The value to check.
7968 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
7970 function isObjectLike(value) {
7971 return !!value && typeof value == 'object';
7975 * Replaces all `placeholder` elements in `array` with an internal placeholder
7976 * and returns an array of their indexes.
7979 * @param {Array} array The array to modify.
7980 * @param {*} placeholder The placeholder to replace.
7981 * @returns {Array} Returns the new array of placeholder indexes.
7983 function replaceHolders(array, placeholder) {
7985 length = array.length,
7989 while (++index < length) {
7990 if (array[index] === placeholder) {
7991 array[index] = PLACEHOLDER;
7992 result[++resIndex] = index;
7999 * An implementation of `_.uniq` optimized for sorted arrays without support
8000 * for callback shorthands and `this` binding.
8003 * @param {Array} array The array to inspect.
8004 * @param {Function} [iteratee] The function invoked per iteration.
8005 * @returns {Array} Returns the new duplicate-value-free array.
8007 function sortedUniq(array, iteratee) {
8010 length = array.length,
8014 while (++index < length) {
8015 var value = array[index],
8016 computed = iteratee ? iteratee(value, index, array) : value;
8018 if (!index || seen !== computed) {
8020 result[++resIndex] = value;
8026 /*--------------------------------------------------------------------------*/
8028 /** Used for native method references. */
8029 var arrayProto = Array.prototype,
8030 errorProto = Error.prototype,
8031 objectProto = Object.prototype,
8032 stringProto = String.prototype;
8034 /** Used to resolve the decompiled source of functions. */
8035 var fnToString = Function.prototype.toString;
8037 /** Used to check objects for own properties. */
8038 var hasOwnProperty = objectProto.hasOwnProperty;
8041 * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
8044 var objToString = objectProto.toString;
8046 /** Used to detect if a method is native. */
8047 var reIsNative = RegExp('^' +
8048 escapeRegExp(fnToString.call(hasOwnProperty))
8049 .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
8052 /** Native method references. */
8053 var ArrayBuffer = getNative(root, 'ArrayBuffer'),
8054 bufferSlice = getNative(ArrayBuffer && new ArrayBuffer(0), 'slice'),
8057 getPrototypeOf = getNative(Object, 'getPrototypeOf'),
8058 push = arrayProto.push,
8059 propertyIsEnumerable = objectProto.propertyIsEnumerable,
8060 Set = getNative(root, 'Set'),
8061 splice = arrayProto.splice,
8062 Uint8Array = getNative(root, 'Uint8Array'),
8063 WeakMap = getNative(root, 'WeakMap');
8065 /** Used to clone array buffers. */
8066 var Float64Array = (function() {
8067 // Safari 5 errors when using an array buffer to initialize a typed array
8068 // where the array buffer's `byteLength` is not a multiple of the typed
8069 // array's `BYTES_PER_ELEMENT`.
8071 var func = getNative(root, 'Float64Array'),
8072 result = new func(new ArrayBuffer(10), 0, 1) && func;
8074 return result || null;
8077 /* Native method references for those with the same name as other `lodash` methods. */
8078 var nativeCreate = getNative(Object, 'create'),
8079 nativeIsArray = getNative(Array, 'isArray'),
8080 nativeKeys = getNative(Object, 'keys'),
8081 nativeMax = Math.max,
8082 nativeMin = Math.min,
8083 nativeNow = getNative(Date, 'now');
8085 /** Used as references for `-Infinity` and `Infinity`. */
8086 var POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
8088 /** Used as references for the maximum length and index of an array. */
8089 var MAX_ARRAY_LENGTH = 4294967295,
8090 MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
8091 HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
8093 /** Used as the size, in bytes, of each `Float64Array` element. */
8094 var FLOAT64_BYTES_PER_ELEMENT = Float64Array ? Float64Array.BYTES_PER_ELEMENT : 0;
8097 * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer)
8098 * of an array-like value.
8100 var MAX_SAFE_INTEGER = 9007199254740991;
8102 /** Used to store function metadata. */
8103 var metaMap = WeakMap && new WeakMap;
8105 /** Used to lookup unminified function names. */
8108 /** Used to lookup a type array constructors by `toStringTag`. */
8110 ctorByTag[float32Tag] = root.Float32Array;
8111 ctorByTag[float64Tag] = root.Float64Array;
8112 ctorByTag[int8Tag] = root.Int8Array;
8113 ctorByTag[int16Tag] = root.Int16Array;
8114 ctorByTag[int32Tag] = root.Int32Array;
8115 ctorByTag[uint8Tag] = root.Uint8Array;
8116 ctorByTag[uint8ClampedTag] = root.Uint8ClampedArray;
8117 ctorByTag[uint16Tag] = root.Uint16Array;
8118 ctorByTag[uint32Tag] = root.Uint32Array;
8120 /** Used to avoid iterating over non-enumerable properties in IE < 9. */
8121 var nonEnumProps = {};
8122 nonEnumProps[arrayTag] = nonEnumProps[dateTag] = nonEnumProps[numberTag] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
8123 nonEnumProps[boolTag] = nonEnumProps[stringTag] = { 'constructor': true, 'toString': true, 'valueOf': true };
8124 nonEnumProps[errorTag] = nonEnumProps[funcTag] = nonEnumProps[regexpTag] = { 'constructor': true, 'toString': true };
8125 nonEnumProps[objectTag] = { 'constructor': true };
8127 arrayEach(shadowProps, function(key) {
8128 for (var tag in nonEnumProps) {
8129 if (hasOwnProperty.call(nonEnumProps, tag)) {
8130 var props = nonEnumProps[tag];
8131 props[key] = hasOwnProperty.call(props, key);
8136 /*------------------------------------------------------------------------*/
8139 * Creates a `lodash` object which wraps `value` to enable implicit chaining.
8140 * Methods that operate on and return arrays, collections, and functions can
8141 * be chained together. Methods that return a boolean or single value will
8142 * automatically end the chain returning the unwrapped value. Explicit chaining
8143 * may be enabled using `_.chain`. The execution of chained methods is lazy,
8144 * that is, execution is deferred until `_#value` is implicitly or explicitly
8147 * Lazy evaluation allows several methods to support shortcut fusion. Shortcut
8148 * fusion is an optimization that merges iteratees to avoid creating intermediate
8149 * arrays and reduce the number of iteratee executions.
8151 * Chaining is supported in custom builds as long as the `_#value` method is
8152 * directly or indirectly included in the build.
8154 * In addition to lodash methods, wrappers have `Array` and `String` methods.
8156 * The wrapper `Array` methods are:
8157 * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`,
8158 * `splice`, and `unshift`
8160 * The wrapper `String` methods are:
8161 * `replace` and `split`
8163 * The wrapper methods that support shortcut fusion are:
8164 * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`,
8165 * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`,
8166 * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`,
8169 * The chainable wrapper methods are:
8170 * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`,
8171 * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`,
8172 * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defer`, `delay`,
8173 * `difference`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `fill`,
8174 * `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, `forEach`,
8175 * `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `functions`,
8176 * `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, `invoke`, `keys`,
8177 * `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
8178 * `memoize`, `merge`, `method`, `methodOf`, `mixin`, `negate`, `omit`, `once`,
8179 * `pairs`, `partial`, `partialRight`, `partition`, `pick`, `plant`, `pluck`,
8180 * `property`, `propertyOf`, `pull`, `pullAt`, `push`, `range`, `rearg`,
8181 * `reject`, `remove`, `rest`, `restParam`, `reverse`, `set`, `shuffle`,
8182 * `slice`, `sort`, `sortBy`, `sortByAll`, `sortByOrder`, `splice`, `spread`,
8183 * `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `tap`, `throttle`,
8184 * `thru`, `times`, `toArray`, `toPlainObject`, `transform`, `union`, `uniq`,
8185 * `unshift`, `unzip`, `unzipWith`, `values`, `valuesIn`, `where`, `without`,
8186 * `wrap`, `xor`, `zip`, `zipObject`, `zipWith`
8188 * The wrapper methods that are **not** chainable by default are:
8189 * `add`, `attempt`, `camelCase`, `capitalize`, `clone`, `cloneDeep`, `deburr`,
8190 * `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`,
8191 * `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, `get`,
8192 * `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`, `inRange`, `isArguments`,
8193 * `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`,
8194 * `isFinite` `isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`,
8195 * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`,
8196 * `isTypedArray`, `join`, `kebabCase`, `last`, `lastIndexOf`, `lt`, `lte`,
8197 * `max`, `min`, `noConflict`, `noop`, `now`, `pad`, `padLeft`, `padRight`,
8198 * `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`,
8199 * `runInContext`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`,
8200 * `sortedLastIndex`, `startCase`, `startsWith`, `sum`, `template`, `trim`,
8201 * `trimLeft`, `trimRight`, `trunc`, `unescape`, `uniqueId`, `value`, and `words`
8203 * The wrapper method `sample` will return a wrapped value when `n` is provided,
8204 * otherwise an unwrapped value is returned.
8209 * @param {*} value The value to wrap in a `lodash` instance.
8210 * @returns {Object} Returns the new `lodash` wrapper instance.
8213 * var wrapped = _([1, 2, 3]);
8215 * // returns an unwrapped value
8216 * wrapped.reduce(function(total, n) {
8221 * // returns a wrapped value
8222 * var squares = wrapped.map(function(n) {
8226 * _.isArray(squares);
8229 * _.isArray(squares.value());
8232 function lodash(value) {
8233 if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
8234 if (value instanceof LodashWrapper) {
8237 if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) {
8238 return wrapperClone(value);
8241 return new LodashWrapper(value);
8245 * The function whose prototype all chaining wrappers inherit from.
8249 function baseLodash() {
8250 // No operation performed.
8254 * The base constructor for creating `lodash` wrapper objects.
8257 * @param {*} value The value to wrap.
8258 * @param {boolean} [chainAll] Enable chaining for all wrapper methods.
8259 * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value.
8261 function LodashWrapper(value, chainAll, actions) {
8262 this.__wrapped__ = value;
8263 this.__actions__ = actions || [];
8264 this.__chain__ = !!chainAll;
8268 * An object environment feature flags.
8274 var support = lodash.support = {};
8277 var Ctor = function() { this.x = x; },
8278 object = { '0': x, 'length': x },
8281 Ctor.prototype = { 'valueOf': x, 'y': x };
8282 for (var key in new Ctor) { props.push(key); }
8285 * Detect if the `toStringTag` of `arguments` objects is resolvable
8286 * (all but Firefox < 4, IE < 9).
8288 * @memberOf _.support
8291 support.argsTag = objToString.call(arguments) == argsTag;
8294 * Detect if `name` or `message` properties of `Error.prototype` are
8295 * enumerable by default (IE < 9, Safari < 5.1).
8297 * @memberOf _.support
8300 support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') ||
8301 propertyIsEnumerable.call(errorProto, 'name');
8304 * Detect if `prototype` properties are enumerable by default.
8306 * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
8307 * (if the prototype or a property on the prototype has been set)
8308 * incorrectly set the `[[Enumerable]]` value of a function's `prototype`
8309 * property to `true`.
8311 * @memberOf _.support
8314 support.enumPrototypes = propertyIsEnumerable.call(Ctor, 'prototype');
8317 * Detect if properties shadowing those on `Object.prototype` are non-enumerable.
8319 * In IE < 9 an object's own properties, shadowing non-enumerable ones,
8320 * are made non-enumerable as well (a.k.a the JScript `[[DontEnum]]` bug).
8322 * @memberOf _.support
8325 support.nonEnumShadows = !/valueOf/.test(props);
8328 * Detect if own properties are iterated after inherited properties (IE < 9).
8330 * @memberOf _.support
8333 support.ownLast = props[0] != 'x';
8336 * Detect if `Array#shift` and `Array#splice` augment array-like objects
8339 * Firefox < 10, compatibility modes of IE 8, and IE < 9 have buggy Array
8340 * `shift()` and `splice()` functions that fail to remove the last element,
8341 * `value[0]`, of array-like objects even though the "length" property is
8342 * set to `0`. The `shift()` method is buggy in compatibility modes of IE 8,
8343 * while `splice()` is buggy regardless of mode in IE < 9.
8345 * @memberOf _.support
8348 support.spliceObjects = (splice.call(object, 0, 1), !object[0]);
8351 * Detect lack of support for accessing string characters by index.
8353 * IE < 8 can't access characters by index. IE 8 can only access characters
8354 * by index on string literals, not string objects.
8356 * @memberOf _.support
8359 support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
8362 /*------------------------------------------------------------------------*/
8365 * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
8368 * @param {*} value The value to wrap.
8370 function LazyWrapper(value) {
8371 this.__wrapped__ = value;
8372 this.__actions__ = null;
8374 this.__dropCount__ = 0;
8375 this.__filtered__ = false;
8376 this.__iteratees__ = null;
8377 this.__takeCount__ = POSITIVE_INFINITY;
8378 this.__views__ = null;
8382 * Creates a clone of the lazy wrapper object.
8386 * @memberOf LazyWrapper
8387 * @returns {Object} Returns the cloned `LazyWrapper` object.
8389 function lazyClone() {
8390 var actions = this.__actions__,
8391 iteratees = this.__iteratees__,
8392 views = this.__views__,
8393 result = new LazyWrapper(this.__wrapped__);
8395 result.__actions__ = actions ? arrayCopy(actions) : null;
8396 result.__dir__ = this.__dir__;
8397 result.__filtered__ = this.__filtered__;
8398 result.__iteratees__ = iteratees ? arrayCopy(iteratees) : null;
8399 result.__takeCount__ = this.__takeCount__;
8400 result.__views__ = views ? arrayCopy(views) : null;
8405 * Reverses the direction of lazy iteration.
8409 * @memberOf LazyWrapper
8410 * @returns {Object} Returns the new reversed `LazyWrapper` object.
8412 function lazyReverse() {
8413 if (this.__filtered__) {
8414 var result = new LazyWrapper(this);
8415 result.__dir__ = -1;
8416 result.__filtered__ = true;
8418 result = this.clone();
8419 result.__dir__ *= -1;
8425 * Extracts the unwrapped value from its lazy wrapper.
8429 * @memberOf LazyWrapper
8430 * @returns {*} Returns the unwrapped value.
8432 function lazyValue() {
8433 var array = this.__wrapped__.value();
8434 if (!isArray(array)) {
8435 return baseWrapperValue(array, this.__actions__);
8437 var dir = this.__dir__,
8439 view = getView(0, array.length, this.__views__),
8442 length = end - start,
8443 index = isRight ? end : (start - 1),
8444 takeCount = nativeMin(length, this.__takeCount__),
8445 iteratees = this.__iteratees__,
8446 iterLength = iteratees ? iteratees.length : 0,
8451 while (length-- && resIndex < takeCount) {
8455 value = array[index];
8457 while (++iterIndex < iterLength) {
8458 var data = iteratees[iterIndex],
8459 iteratee = data.iteratee,
8462 if (type == LAZY_DROP_WHILE_FLAG) {
8463 if (data.done && (isRight ? (index > data.index) : (index < data.index))) {
8469 var limit = data.limit;
8470 if (!(data.done = limit > -1 ? (data.count++ >= limit) : !iteratee(value))) {
8475 var computed = iteratee(value);
8476 if (type == LAZY_MAP_FLAG) {
8478 } else if (!computed) {
8479 if (type == LAZY_FILTER_FLAG) {
8487 result[resIndex++] = value;
8492 /*------------------------------------------------------------------------*/
8496 * Creates a cache object to store unique values.
8499 * @param {Array} [values] The values to cache.
8501 function SetCache(values) {
8502 var length = values ? values.length : 0;
8504 this.data = { 'hash': nativeCreate(null), 'set': new Set };
8506 this.push(values[length]);
8511 * Checks if `value` is in `cache` mimicking the return signature of
8512 * `_.indexOf` by returning `0` if the value is found, else `-1`.
8515 * @param {Object} cache The cache to search.
8516 * @param {*} value The value to search for.
8517 * @returns {number} Returns `0` if `value` is found, else `-1`.
8519 function cacheIndexOf(cache, value) {
8520 var data = cache.data,
8521 result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value];
8523 return result ? 0 : -1;
8527 * Adds `value` to the cache.
8531 * @memberOf SetCache
8532 * @param {*} value The value to cache.
8534 function cachePush(value) {
8535 var data = this.data;
8536 if (typeof value == 'string' || isObject(value)) {
8537 data.set.add(value);
8539 data.hash[value] = true;
8543 /*------------------------------------------------------------------------*/
8546 * Copies the values of `source` to `array`.
8549 * @param {Array} source The array to copy values from.
8550 * @param {Array} [array=[]] The array to copy values to.
8551 * @returns {Array} Returns `array`.
8553 function arrayCopy(source, array) {
8555 length = source.length;
8557 array || (array = Array(length));
8558 while (++index < length) {
8559 array[index] = source[index];
8565 * A specialized version of `_.forEach` for arrays without support for callback
8566 * shorthands and `this` binding.
8569 * @param {Array} array The array to iterate over.
8570 * @param {Function} iteratee The function invoked per iteration.
8571 * @returns {Array} Returns `array`.
8573 function arrayEach(array, iteratee) {
8575 length = array.length;
8577 while (++index < length) {
8578 if (iteratee(array[index], index, array) === false) {
8586 * A specialized version of `_.every` for arrays without support for callback
8587 * shorthands and `this` binding.
8590 * @param {Array} array The array to iterate over.
8591 * @param {Function} predicate The function invoked per iteration.
8592 * @returns {boolean} Returns `true` if all elements pass the predicate check,
8595 function arrayEvery(array, predicate) {
8597 length = array.length;
8599 while (++index < length) {
8600 if (!predicate(array[index], index, array)) {
8608 * A specialized version of `_.filter` for arrays without support for callback
8609 * shorthands and `this` binding.
8612 * @param {Array} array The array to iterate over.
8613 * @param {Function} predicate The function invoked per iteration.
8614 * @returns {Array} Returns the new filtered array.
8616 function arrayFilter(array, predicate) {
8618 length = array.length,
8622 while (++index < length) {
8623 var value = array[index];
8624 if (predicate(value, index, array)) {
8625 result[++resIndex] = value;
8632 * A specialized version of `_.map` for arrays without support for callback
8633 * shorthands and `this` binding.
8636 * @param {Array} array The array to iterate over.
8637 * @param {Function} iteratee The function invoked per iteration.
8638 * @returns {Array} Returns the new mapped array.
8640 function arrayMap(array, iteratee) {
8642 length = array.length,
8643 result = Array(length);
8645 while (++index < length) {
8646 result[index] = iteratee(array[index], index, array);
8652 * A specialized version of `_.reduce` for arrays without support for callback
8653 * shorthands and `this` binding.
8656 * @param {Array} array The array to iterate over.
8657 * @param {Function} iteratee The function invoked per iteration.
8658 * @param {*} [accumulator] The initial value.
8659 * @param {boolean} [initFromArray] Specify using the first element of `array`
8660 * as the initial value.
8661 * @returns {*} Returns the accumulated value.
8663 function arrayReduce(array, iteratee, accumulator, initFromArray) {
8665 length = array.length;
8667 if (initFromArray && length) {
8668 accumulator = array[++index];
8670 while (++index < length) {
8671 accumulator = iteratee(accumulator, array[index], index, array);
8677 * A specialized version of `_.some` for arrays without support for callback
8678 * shorthands and `this` binding.
8681 * @param {Array} array The array to iterate over.
8682 * @param {Function} predicate The function invoked per iteration.
8683 * @returns {boolean} Returns `true` if any element passes the predicate check,
8686 function arraySome(array, predicate) {
8688 length = array.length;
8690 while (++index < length) {
8691 if (predicate(array[index], index, array)) {
8699 * A specialized version of `_.assign` for customizing assigned values without
8700 * support for argument juggling, multiple sources, and `this` binding `customizer`
8704 * @param {Object} object The destination object.
8705 * @param {Object} source The source object.
8706 * @param {Function} customizer The function to customize assigned values.
8707 * @returns {Object} Returns `object`.
8709 function assignWith(object, source, customizer) {
8711 props = keys(source),
8712 length = props.length;
8714 while (++index < length) {
8715 var key = props[index],
8716 value = object[key],
8717 result = customizer(value, source[key], key, object, source);
8719 if ((result === result ? (result !== value) : (value === value)) ||
8720 (value === undefined && !(key in object))) {
8721 object[key] = result;
8728 * The base implementation of `_.assign` without support for argument juggling,
8729 * multiple sources, and `customizer` functions.
8732 * @param {Object} object The destination object.
8733 * @param {Object} source The source object.
8734 * @returns {Object} Returns `object`.
8736 function baseAssign(object, source) {
8737 return source == null
8739 : baseCopy(source, keys(source), object);
8743 * Copies properties of `source` to `object`.
8746 * @param {Object} source The object to copy properties from.
8747 * @param {Array} props The property names to copy.
8748 * @param {Object} [object={}] The object to copy properties to.
8749 * @returns {Object} Returns `object`.
8751 function baseCopy(source, props, object) {
8752 object || (object = {});
8755 length = props.length;
8757 while (++index < length) {
8758 var key = props[index];
8759 object[key] = source[key];
8765 * The base implementation of `_.callback` which supports specifying the
8766 * number of arguments to provide to `func`.
8769 * @param {*} [func=_.identity] The value to convert to a callback.
8770 * @param {*} [thisArg] The `this` binding of `func`.
8771 * @param {number} [argCount] The number of arguments to provide to `func`.
8772 * @returns {Function} Returns the callback.
8774 function baseCallback(func, thisArg, argCount) {
8775 var type = typeof func;
8776 if (type == 'function') {
8777 return thisArg === undefined
8779 : bindCallback(func, thisArg, argCount);
8784 if (type == 'object') {
8785 return baseMatches(func);
8787 return thisArg === undefined
8789 : baseMatchesProperty(func, thisArg);
8793 * The base implementation of `_.clone` without support for argument juggling
8794 * and `this` binding `customizer` functions.
8797 * @param {*} value The value to clone.
8798 * @param {boolean} [isDeep] Specify a deep clone.
8799 * @param {Function} [customizer] The function to customize cloning values.
8800 * @param {string} [key] The key of `value`.
8801 * @param {Object} [object] The object `value` belongs to.
8802 * @param {Array} [stackA=[]] Tracks traversed source objects.
8803 * @param {Array} [stackB=[]] Associates clones with source counterparts.
8804 * @returns {*} Returns the cloned value.
8806 function baseClone(value, isDeep, customizer, key, object, stackA, stackB) {
8809 result = object ? customizer(value, key, object) : customizer(value);
8811 if (result !== undefined) {
8814 if (!isObject(value)) {
8817 var isArr = isArray(value);
8819 result = initCloneArray(value);
8821 return arrayCopy(value, result);
8824 var tag = objToString.call(value),
8825 isFunc = tag == funcTag;
8827 if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
8828 if (isHostObject(value)) {
8829 return object ? value : {};
8831 result = initCloneObject(isFunc ? {} : value);
8833 return baseAssign(result, value);
8836 return cloneableTags[tag]
8837 ? initCloneByTag(value, tag, isDeep)
8838 : (object ? value : {});
8841 // Check for circular references and return corresponding clone.
8842 stackA || (stackA = []);
8843 stackB || (stackB = []);
8845 var length = stackA.length;
8847 if (stackA[length] == value) {
8848 return stackB[length];
8851 // Add the source value to the stack of traversed objects and associate it with its clone.
8853 stackB.push(result);
8855 // Recursively populate clone (susceptible to call stack limits).
8856 (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) {
8857 result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB);
8863 * The base implementation of `_.create` without support for assigning
8864 * properties to the created object.
8867 * @param {Object} prototype The object to inherit from.
8868 * @returns {Object} Returns the new object.
8870 var baseCreate = (function() {
8871 function object() {}
8872 return function(prototype) {
8873 if (isObject(prototype)) {
8874 object.prototype = prototype;
8875 var result = new object;
8876 object.prototype = null;
8878 return result || {};
8883 * The base implementation of `_.difference` which accepts a single array
8884 * of values to exclude.
8887 * @param {Array} array The array to inspect.
8888 * @param {Array} values The values to exclude.
8889 * @returns {Array} Returns the new array of filtered values.
8891 function baseDifference(array, values) {
8892 var length = array ? array.length : 0,
8899 indexOf = getIndexOf(),
8900 isCommon = indexOf == baseIndexOf,
8901 cache = (isCommon && values.length >= 200) ? createCache(values) : null,
8902 valuesLength = values.length;
8905 indexOf = cacheIndexOf;
8910 while (++index < length) {
8911 var value = array[index];
8913 if (isCommon && value === value) {
8914 var valuesIndex = valuesLength;
8915 while (valuesIndex--) {
8916 if (values[valuesIndex] === value) {
8922 else if (indexOf(values, value, 0) < 0) {
8930 * The base implementation of `_.forEach` without support for callback
8931 * shorthands and `this` binding.
8934 * @param {Array|Object|string} collection The collection to iterate over.
8935 * @param {Function} iteratee The function invoked per iteration.
8936 * @returns {Array|Object|string} Returns `collection`.
8938 var baseEach = createBaseEach(baseForOwn);
8941 * The base implementation of `_.every` without support for callback
8942 * shorthands and `this` binding.
8945 * @param {Array|Object|string} collection The collection to iterate over.
8946 * @param {Function} predicate The function invoked per iteration.
8947 * @returns {boolean} Returns `true` if all elements pass the predicate check,
8950 function baseEvery(collection, predicate) {
8952 baseEach(collection, function(value, index, collection) {
8953 result = !!predicate(value, index, collection);
8960 * The base implementation of `_.filter` without support for callback
8961 * shorthands and `this` binding.
8964 * @param {Array|Object|string} collection The collection to iterate over.
8965 * @param {Function} predicate The function invoked per iteration.
8966 * @returns {Array} Returns the new filtered array.
8968 function baseFilter(collection, predicate) {
8970 baseEach(collection, function(value, index, collection) {
8971 if (predicate(value, index, collection)) {
8979 * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`,
8980 * without support for callback shorthands and `this` binding, which iterates
8981 * over `collection` using the provided `eachFunc`.
8984 * @param {Array|Object|string} collection The collection to search.
8985 * @param {Function} predicate The function invoked per iteration.
8986 * @param {Function} eachFunc The function to iterate over `collection`.
8987 * @param {boolean} [retKey] Specify returning the key of the found element
8988 * instead of the element itself.
8989 * @returns {*} Returns the found element or its key, else `undefined`.
8991 function baseFind(collection, predicate, eachFunc, retKey) {
8993 eachFunc(collection, function(value, key, collection) {
8994 if (predicate(value, key, collection)) {
8995 result = retKey ? key : value;
9003 * The base implementation of `_.flatten` with added support for restricting
9004 * flattening and specifying the start index.
9007 * @param {Array} array The array to flatten.
9008 * @param {boolean} [isDeep] Specify a deep flatten.
9009 * @param {boolean} [isStrict] Restrict flattening to arrays-like objects.
9010 * @returns {Array} Returns the new flattened array.
9012 function baseFlatten(array, isDeep, isStrict) {
9014 length = array.length,
9018 while (++index < length) {
9019 var value = array[index];
9020 if (isObjectLike(value) && isArrayLike(value) &&
9021 (isStrict || isArray(value) || isArguments(value))) {
9023 // Recursively flatten arrays (susceptible to call stack limits).
9024 value = baseFlatten(value, isDeep, isStrict);
9027 valLength = value.length;
9029 while (++valIndex < valLength) {
9030 result[++resIndex] = value[valIndex];
9032 } else if (!isStrict) {
9033 result[++resIndex] = value;
9040 * The base implementation of `baseForIn` and `baseForOwn` which iterates
9041 * over `object` properties returned by `keysFunc` invoking `iteratee` for
9042 * each property. Iteratee functions may exit iteration early by explicitly
9043 * returning `false`.
9046 * @param {Object} object The object to iterate over.
9047 * @param {Function} iteratee The function invoked per iteration.
9048 * @param {Function} keysFunc The function to get the keys of `object`.
9049 * @returns {Object} Returns `object`.
9051 var baseFor = createBaseFor();
9054 * The base implementation of `_.forIn` without support for callback
9055 * shorthands and `this` binding.
9058 * @param {Object} object The object to iterate over.
9059 * @param {Function} iteratee The function invoked per iteration.
9060 * @returns {Object} Returns `object`.
9062 function baseForIn(object, iteratee) {
9063 return baseFor(object, iteratee, keysIn);
9067 * The base implementation of `_.forOwn` without support for callback
9068 * shorthands and `this` binding.
9071 * @param {Object} object The object to iterate over.
9072 * @param {Function} iteratee The function invoked per iteration.
9073 * @returns {Object} Returns `object`.
9075 function baseForOwn(object, iteratee) {
9076 return baseFor(object, iteratee, keys);
9080 * The base implementation of `_.functions` which creates an array of
9081 * `object` function property names filtered from those provided.
9084 * @param {Object} object The object to inspect.
9085 * @param {Array} props The property names to filter.
9086 * @returns {Array} Returns the new array of filtered property names.
9088 function baseFunctions(object, props) {
9090 length = props.length,
9094 while (++index < length) {
9095 var key = props[index];
9096 if (isFunction(object[key])) {
9097 result[++resIndex] = key;
9104 * The base implementation of `get` without support for string paths
9105 * and default values.
9108 * @param {Object} object The object to query.
9109 * @param {Array} path The path of the property to get.
9110 * @param {string} [pathKey] The key representation of path.
9111 * @returns {*} Returns the resolved value.
9113 function baseGet(object, path, pathKey) {
9114 if (object == null) {
9117 object = toObject(object);
9118 if (pathKey !== undefined && pathKey in object) {
9122 length = path.length;
9124 while (object != null && index < length) {
9125 object = toObject(object)[path[index++]];
9127 return (index && index == length) ? object : undefined;
9131 * The base implementation of `_.isEqual` without support for `this` binding
9132 * `customizer` functions.
9135 * @param {*} value The value to compare.
9136 * @param {*} other The other value to compare.
9137 * @param {Function} [customizer] The function to customize comparing values.
9138 * @param {boolean} [isLoose] Specify performing partial comparisons.
9139 * @param {Array} [stackA] Tracks traversed `value` objects.
9140 * @param {Array} [stackB] Tracks traversed `other` objects.
9141 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
9143 function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) {
9144 if (value === other) {
9147 if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
9148 return value !== value && other !== other;
9150 return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB);
9154 * A specialized version of `baseIsEqual` for arrays and objects which performs
9155 * deep comparisons and tracks traversed objects enabling objects with circular
9156 * references to be compared.
9159 * @param {Object} object The object to compare.
9160 * @param {Object} other The other object to compare.
9161 * @param {Function} equalFunc The function to determine equivalents of values.
9162 * @param {Function} [customizer] The function to customize comparing objects.
9163 * @param {boolean} [isLoose] Specify performing partial comparisons.
9164 * @param {Array} [stackA=[]] Tracks traversed `value` objects.
9165 * @param {Array} [stackB=[]] Tracks traversed `other` objects.
9166 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9168 function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
9169 var objIsArr = isArray(object),
9170 othIsArr = isArray(other),
9175 objTag = objToString.call(object);
9176 if (objTag == argsTag) {
9178 } else if (objTag != objectTag) {
9179 objIsArr = isTypedArray(object);
9183 othTag = objToString.call(other);
9184 if (othTag == argsTag) {
9186 } else if (othTag != objectTag) {
9187 othIsArr = isTypedArray(other);
9190 var objIsObj = objTag == objectTag && !isHostObject(object),
9191 othIsObj = othTag == objectTag && !isHostObject(other),
9192 isSameTag = objTag == othTag;
9194 if (isSameTag && !(objIsArr || objIsObj)) {
9195 return equalByTag(object, other, objTag);
9198 var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
9199 othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
9201 if (objIsWrapped || othIsWrapped) {
9202 return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB);
9208 // Assume cyclic values are equal.
9209 // For more information on detecting circular references see https://es5.github.io/#JO.
9210 stackA || (stackA = []);
9211 stackB || (stackB = []);
9213 var length = stackA.length;
9215 if (stackA[length] == object) {
9216 return stackB[length] == other;
9219 // Add `object` and `other` to the stack of traversed objects.
9220 stackA.push(object);
9223 var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB);
9232 * The base implementation of `_.isMatch` without support for callback
9233 * shorthands and `this` binding.
9236 * @param {Object} object The object to inspect.
9237 * @param {Array} matchData The propery names, values, and compare flags to match.
9238 * @param {Function} [customizer] The function to customize comparing objects.
9239 * @returns {boolean} Returns `true` if `object` is a match, else `false`.
9241 function baseIsMatch(object, matchData, customizer) {
9242 var index = matchData.length,
9244 noCustomizer = !customizer;
9246 if (object == null) {
9249 object = toObject(object);
9251 var data = matchData[index];
9252 if ((noCustomizer && data[2])
9253 ? data[1] !== object[data[0]]
9254 : !(data[0] in object)
9259 while (++index < length) {
9260 data = matchData[index];
9262 objValue = object[key],
9265 if (noCustomizer && data[2]) {
9266 if (objValue === undefined && !(key in object)) {
9270 var result = customizer ? customizer(objValue, srcValue, key) : undefined;
9271 if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, true) : result)) {
9280 * The base implementation of `_.map` without support for callback shorthands
9281 * and `this` binding.
9284 * @param {Array|Object|string} collection The collection to iterate over.
9285 * @param {Function} iteratee The function invoked per iteration.
9286 * @returns {Array} Returns the new mapped array.
9288 function baseMap(collection, iteratee) {
9290 result = isArrayLike(collection) ? Array(collection.length) : [];
9292 baseEach(collection, function(value, key, collection) {
9293 result[++index] = iteratee(value, key, collection);
9299 * The base implementation of `_.matches` which does not clone `source`.
9302 * @param {Object} source The object of property values to match.
9303 * @returns {Function} Returns the new function.
9305 function baseMatches(source) {
9306 var matchData = getMatchData(source);
9307 if (matchData.length == 1 && matchData[0][2]) {
9308 var key = matchData[0][0],
9309 value = matchData[0][1];
9311 return function(object) {
9312 if (object == null) {
9315 object = toObject(object);
9316 return object[key] === value && (value !== undefined || (key in object));
9319 return function(object) {
9320 return baseIsMatch(object, matchData);
9325 * The base implementation of `_.matchesProperty` which does not clone `srcValue`.
9328 * @param {string} path The path of the property to get.
9329 * @param {*} srcValue The value to compare.
9330 * @returns {Function} Returns the new function.
9332 function baseMatchesProperty(path, srcValue) {
9333 var isArr = isArray(path),
9334 isCommon = isKey(path) && isStrictComparable(srcValue),
9335 pathKey = (path + '');
9337 path = toPath(path);
9338 return function(object) {
9339 if (object == null) {
9343 object = toObject(object);
9344 if ((isArr || !isCommon) && !(key in object)) {
9345 object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
9346 if (object == null) {
9350 object = toObject(object);
9352 return object[key] === srcValue
9353 ? (srcValue !== undefined || (key in object))
9354 : baseIsEqual(srcValue, object[key], undefined, true);
9359 * The base implementation of `_.merge` without support for argument juggling,
9360 * multiple sources, and `this` binding `customizer` functions.
9363 * @param {Object} object The destination object.
9364 * @param {Object} source The source object.
9365 * @param {Function} [customizer] The function to customize merging properties.
9366 * @param {Array} [stackA=[]] Tracks traversed source objects.
9367 * @param {Array} [stackB=[]] Associates values with source counterparts.
9368 * @returns {Object} Returns `object`.
9370 function baseMerge(object, source, customizer, stackA, stackB) {
9371 if (!isObject(object)) {
9374 var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)),
9375 props = isSrcArr ? null : keys(source);
9377 arrayEach(props || source, function(srcValue, key) {
9380 srcValue = source[key];
9382 if (isObjectLike(srcValue)) {
9383 stackA || (stackA = []);
9384 stackB || (stackB = []);
9385 baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
9388 var value = object[key],
9389 result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
9390 isCommon = result === undefined;
9395 if ((result !== undefined || (isSrcArr && !(key in object))) &&
9396 (isCommon || (result === result ? (result !== value) : (value === value)))) {
9397 object[key] = result;
9405 * A specialized version of `baseMerge` for arrays and objects which performs
9406 * deep merges and tracks traversed objects enabling objects with circular
9407 * references to be merged.
9410 * @param {Object} object The destination object.
9411 * @param {Object} source The source object.
9412 * @param {string} key The key of the value to merge.
9413 * @param {Function} mergeFunc The function to merge values.
9414 * @param {Function} [customizer] The function to customize merging properties.
9415 * @param {Array} [stackA=[]] Tracks traversed source objects.
9416 * @param {Array} [stackB=[]] Associates values with source counterparts.
9417 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9419 function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) {
9420 var length = stackA.length,
9421 srcValue = source[key];
9424 if (stackA[length] == srcValue) {
9425 object[key] = stackB[length];
9429 var value = object[key],
9430 result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
9431 isCommon = result === undefined;
9435 if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) {
9436 result = isArray(value)
9438 : (isArrayLike(value) ? arrayCopy(value) : []);
9440 else if (isPlainObject(srcValue) || isArguments(srcValue)) {
9441 result = isArguments(value)
9442 ? toPlainObject(value)
9443 : (isPlainObject(value) ? value : {});
9449 // Add the source value to the stack of traversed objects and associate
9450 // it with its merged value.
9451 stackA.push(srcValue);
9452 stackB.push(result);
9455 // Recursively merge objects and arrays (susceptible to call stack limits).
9456 object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB);
9457 } else if (result === result ? (result !== value) : (value === value)) {
9458 object[key] = result;
9463 * The base implementation of `_.property` without support for deep paths.
9466 * @param {string} key The key of the property to get.
9467 * @returns {Function} Returns the new function.
9469 function baseProperty(key) {
9470 return function(object) {
9471 return object == null ? undefined : toObject(object)[key];
9476 * A specialized version of `baseProperty` which supports deep paths.
9479 * @param {Array|string} path The path of the property to get.
9480 * @returns {Function} Returns the new function.
9482 function basePropertyDeep(path) {
9483 var pathKey = (path + '');
9484 path = toPath(path);
9485 return function(object) {
9486 return baseGet(object, path, pathKey);
9491 * The base implementation of `_.reduce` and `_.reduceRight` without support
9492 * for callback shorthands and `this` binding, which iterates over `collection`
9493 * using the provided `eachFunc`.
9496 * @param {Array|Object|string} collection The collection to iterate over.
9497 * @param {Function} iteratee The function invoked per iteration.
9498 * @param {*} accumulator The initial value.
9499 * @param {boolean} initFromCollection Specify using the first or last element
9500 * of `collection` as the initial value.
9501 * @param {Function} eachFunc The function to iterate over `collection`.
9502 * @returns {*} Returns the accumulated value.
9504 function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) {
9505 eachFunc(collection, function(value, index, collection) {
9506 accumulator = initFromCollection
9507 ? (initFromCollection = false, value)
9508 : iteratee(accumulator, value, index, collection);
9514 * The base implementation of `setData` without support for hot loop detection.
9517 * @param {Function} func The function to associate metadata with.
9518 * @param {*} data The metadata.
9519 * @returns {Function} Returns `func`.
9521 var baseSetData = !metaMap ? identity : function(func, data) {
9522 metaMap.set(func, data);
9527 * The base implementation of `_.slice` without an iteratee call guard.
9530 * @param {Array} array The array to slice.
9531 * @param {number} [start=0] The start position.
9532 * @param {number} [end=array.length] The end position.
9533 * @returns {Array} Returns the slice of `array`.
9535 function baseSlice(array, start, end) {
9537 length = array.length;
9539 start = start == null ? 0 : (+start || 0);
9541 start = -start > length ? 0 : (length + start);
9543 end = (end === undefined || end > length) ? length : (+end || 0);
9547 length = start > end ? 0 : ((end - start) >>> 0);
9550 var result = Array(length);
9551 while (++index < length) {
9552 result[index] = array[index + start];
9558 * The base implementation of `_.some` without support for callback shorthands
9559 * and `this` binding.
9562 * @param {Array|Object|string} collection The collection to iterate over.
9563 * @param {Function} predicate The function invoked per iteration.
9564 * @returns {boolean} Returns `true` if any element passes the predicate check,
9567 function baseSome(collection, predicate) {
9570 baseEach(collection, function(value, index, collection) {
9571 result = predicate(value, index, collection);
9578 * The base implementation of `_.uniq` without support for callback shorthands
9579 * and `this` binding.
9582 * @param {Array} array The array to inspect.
9583 * @param {Function} [iteratee] The function invoked per iteration.
9584 * @returns {Array} Returns the new duplicate-value-free array.
9586 function baseUniq(array, iteratee) {
9588 indexOf = getIndexOf(),
9589 length = array.length,
9590 isCommon = indexOf == baseIndexOf,
9591 isLarge = isCommon && length >= 200,
9592 seen = isLarge ? createCache() : null,
9596 indexOf = cacheIndexOf;
9600 seen = iteratee ? [] : result;
9603 while (++index < length) {
9604 var value = array[index],
9605 computed = iteratee ? iteratee(value, index, array) : value;
9607 if (isCommon && value === value) {
9608 var seenIndex = seen.length;
9609 while (seenIndex--) {
9610 if (seen[seenIndex] === computed) {
9615 seen.push(computed);
9619 else if (indexOf(seen, computed, 0) < 0) {
9620 if (iteratee || isLarge) {
9621 seen.push(computed);
9630 * The base implementation of `_.values` and `_.valuesIn` which creates an
9631 * array of `object` property values corresponding to the property names
9635 * @param {Object} object The object to query.
9636 * @param {Array} props The property names to get values for.
9637 * @returns {Object} Returns the array of property values.
9639 function baseValues(object, props) {
9641 length = props.length,
9642 result = Array(length);
9644 while (++index < length) {
9645 result[index] = object[props[index]];
9651 * The base implementation of `wrapperValue` which returns the result of
9652 * performing a sequence of actions on the unwrapped `value`, where each
9653 * successive action is supplied the return value of the previous.
9656 * @param {*} value The unwrapped value.
9657 * @param {Array} actions Actions to peform to resolve the unwrapped value.
9658 * @returns {*} Returns the resolved value.
9660 function baseWrapperValue(value, actions) {
9662 if (result instanceof LazyWrapper) {
9663 result = result.value();
9666 length = actions.length;
9668 while (++index < length) {
9669 var args = [result],
9670 action = actions[index];
9672 push.apply(args, action.args);
9673 result = action.func.apply(action.thisArg, args);
9679 * Performs a binary search of `array` to determine the index at which `value`
9680 * should be inserted into `array` in order to maintain its sort order.
9683 * @param {Array} array The sorted array to inspect.
9684 * @param {*} value The value to evaluate.
9685 * @param {boolean} [retHighest] Specify returning the highest qualified index.
9686 * @returns {number} Returns the index at which `value` should be inserted
9689 function binaryIndex(array, value, retHighest) {
9691 high = array ? array.length : low;
9693 if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
9694 while (low < high) {
9695 var mid = (low + high) >>> 1,
9696 computed = array[mid];
9698 if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) {
9706 return binaryIndexBy(array, value, identity, retHighest);
9710 * This function is like `binaryIndex` except that it invokes `iteratee` for
9711 * `value` and each element of `array` to compute their sort ranking. The
9712 * iteratee is invoked with one argument; (value).
9715 * @param {Array} array The sorted array to inspect.
9716 * @param {*} value The value to evaluate.
9717 * @param {Function} iteratee The function invoked per iteration.
9718 * @param {boolean} [retHighest] Specify returning the highest qualified index.
9719 * @returns {number} Returns the index at which `value` should be inserted
9722 function binaryIndexBy(array, value, iteratee, retHighest) {
9723 value = iteratee(value);
9726 high = array ? array.length : 0,
9727 valIsNaN = value !== value,
9728 valIsNull = value === null,
9729 valIsUndef = value === undefined;
9731 while (low < high) {
9732 var mid = floor((low + high) / 2),
9733 computed = iteratee(array[mid]),
9734 isDef = computed !== undefined,
9735 isReflexive = computed === computed;
9738 var setLow = isReflexive || retHighest;
9739 } else if (valIsNull) {
9740 setLow = isReflexive && isDef && (retHighest || computed != null);
9741 } else if (valIsUndef) {
9742 setLow = isReflexive && (retHighest || isDef);
9743 } else if (computed == null) {
9746 setLow = retHighest ? (computed <= value) : (computed < value);
9754 return nativeMin(high, MAX_ARRAY_INDEX);
9758 * A specialized version of `baseCallback` which only supports `this` binding
9759 * and specifying the number of arguments to provide to `func`.
9762 * @param {Function} func The function to bind.
9763 * @param {*} thisArg The `this` binding of `func`.
9764 * @param {number} [argCount] The number of arguments to provide to `func`.
9765 * @returns {Function} Returns the callback.
9767 function bindCallback(func, thisArg, argCount) {
9768 if (typeof func != 'function') {
9771 if (thisArg === undefined) {
9775 case 1: return function(value) {
9776 return func.call(thisArg, value);
9778 case 3: return function(value, index, collection) {
9779 return func.call(thisArg, value, index, collection);
9781 case 4: return function(accumulator, value, index, collection) {
9782 return func.call(thisArg, accumulator, value, index, collection);
9784 case 5: return function(value, other, key, object, source) {
9785 return func.call(thisArg, value, other, key, object, source);
9789 return func.apply(thisArg, arguments);
9794 * Creates a clone of the given array buffer.
9797 * @param {ArrayBuffer} buffer The array buffer to clone.
9798 * @returns {ArrayBuffer} Returns the cloned array buffer.
9800 function bufferClone(buffer) {
9801 return bufferSlice.call(buffer, 0);
9804 // PhantomJS has `ArrayBuffer` and `Uint8Array` but not `Float64Array`.
9805 bufferClone = !(ArrayBuffer && Uint8Array) ? constant(null) : function(buffer) {
9806 var byteLength = buffer.byteLength,
9807 floatLength = Float64Array ? floor(byteLength / FLOAT64_BYTES_PER_ELEMENT) : 0,
9808 offset = floatLength * FLOAT64_BYTES_PER_ELEMENT,
9809 result = new ArrayBuffer(byteLength);
9812 var view = new Float64Array(result, 0, floatLength);
9813 view.set(new Float64Array(buffer, 0, floatLength));
9815 if (byteLength != offset) {
9816 view = new Uint8Array(result, offset);
9817 view.set(new Uint8Array(buffer, offset));
9824 * Creates an array that is the composition of partially applied arguments,
9825 * placeholders, and provided arguments into a single array of arguments.
9828 * @param {Array|Object} args The provided arguments.
9829 * @param {Array} partials The arguments to prepend to those provided.
9830 * @param {Array} holders The `partials` placeholder indexes.
9831 * @returns {Array} Returns the new array of composed arguments.
9833 function composeArgs(args, partials, holders) {
9834 var holdersLength = holders.length,
9836 argsLength = nativeMax(args.length - holdersLength, 0),
9838 leftLength = partials.length,
9839 result = Array(argsLength + leftLength);
9841 while (++leftIndex < leftLength) {
9842 result[leftIndex] = partials[leftIndex];
9844 while (++argsIndex < holdersLength) {
9845 result[holders[argsIndex]] = args[argsIndex];
9847 while (argsLength--) {
9848 result[leftIndex++] = args[argsIndex++];
9854 * This function is like `composeArgs` except that the arguments composition
9855 * is tailored for `_.partialRight`.
9858 * @param {Array|Object} args The provided arguments.
9859 * @param {Array} partials The arguments to append to those provided.
9860 * @param {Array} holders The `partials` placeholder indexes.
9861 * @returns {Array} Returns the new array of composed arguments.
9863 function composeArgsRight(args, partials, holders) {
9864 var holdersIndex = -1,
9865 holdersLength = holders.length,
9867 argsLength = nativeMax(args.length - holdersLength, 0),
9869 rightLength = partials.length,
9870 result = Array(argsLength + rightLength);
9872 while (++argsIndex < argsLength) {
9873 result[argsIndex] = args[argsIndex];
9875 var offset = argsIndex;
9876 while (++rightIndex < rightLength) {
9877 result[offset + rightIndex] = partials[rightIndex];
9879 while (++holdersIndex < holdersLength) {
9880 result[offset + holders[holdersIndex]] = args[argsIndex++];
9886 * Creates a function that aggregates a collection, creating an accumulator
9887 * object composed from the results of running each element in the collection
9888 * through an iteratee.
9890 * **Note:** This function is used to create `_.countBy`, `_.groupBy`, `_.indexBy`,
9891 * and `_.partition`.
9894 * @param {Function} setter The function to set keys and values of the accumulator object.
9895 * @param {Function} [initializer] The function to initialize the accumulator object.
9896 * @returns {Function} Returns the new aggregator function.
9898 function createAggregator(setter, initializer) {
9899 return function(collection, iteratee, thisArg) {
9900 var result = initializer ? initializer() : {};
9901 iteratee = getCallback(iteratee, thisArg, 3);
9903 if (isArray(collection)) {
9905 length = collection.length;
9907 while (++index < length) {
9908 var value = collection[index];
9909 setter(result, value, iteratee(value, index, collection), collection);
9912 baseEach(collection, function(value, key, collection) {
9913 setter(result, value, iteratee(value, key, collection), collection);
9921 * Creates a function that assigns properties of source object(s) to a given
9922 * destination object.
9924 * **Note:** This function is used to create `_.assign`, `_.defaults`, and `_.merge`.
9927 * @param {Function} assigner The function to assign values.
9928 * @returns {Function} Returns the new assigner function.
9930 function createAssigner(assigner) {
9931 return restParam(function(object, sources) {
9933 length = object == null ? 0 : sources.length,
9934 customizer = length > 2 ? sources[length - 2] : undefined,
9935 guard = length > 2 ? sources[2] : undefined,
9936 thisArg = length > 1 ? sources[length - 1] : undefined;
9938 if (typeof customizer == 'function') {
9939 customizer = bindCallback(customizer, thisArg, 5);
9942 customizer = typeof thisArg == 'function' ? thisArg : undefined;
9943 length -= (customizer ? 1 : 0);
9945 if (guard && isIterateeCall(sources[0], sources[1], guard)) {
9946 customizer = length < 3 ? undefined : customizer;
9949 while (++index < length) {
9950 var source = sources[index];
9952 assigner(object, source, customizer);
9960 * Creates a `baseEach` or `baseEachRight` function.
9963 * @param {Function} eachFunc The function to iterate over a collection.
9964 * @param {boolean} [fromRight] Specify iterating from right to left.
9965 * @returns {Function} Returns the new base function.
9967 function createBaseEach(eachFunc, fromRight) {
9968 return function(collection, iteratee) {
9969 var length = collection ? getLength(collection) : 0;
9970 if (!isLength(length)) {
9971 return eachFunc(collection, iteratee);
9973 var index = fromRight ? length : -1,
9974 iterable = toObject(collection);
9976 while ((fromRight ? index-- : ++index < length)) {
9977 if (iteratee(iterable[index], index, iterable) === false) {
9986 * Creates a base function for `_.forIn` or `_.forInRight`.
9989 * @param {boolean} [fromRight] Specify iterating from right to left.
9990 * @returns {Function} Returns the new base function.
9992 function createBaseFor(fromRight) {
9993 return function(object, iteratee, keysFunc) {
9994 var iterable = toObject(object),
9995 props = keysFunc(object),
9996 length = props.length,
9997 index = fromRight ? length : -1;
9999 while ((fromRight ? index-- : ++index < length)) {
10000 var key = props[index];
10001 if (iteratee(iterable[key], key, iterable) === false) {
10010 * Creates a function that wraps `func` and invokes it with the `this`
10011 * binding of `thisArg`.
10014 * @param {Function} func The function to bind.
10015 * @param {*} [thisArg] The `this` binding of `func`.
10016 * @returns {Function} Returns the new bound function.
10018 function createBindWrapper(func, thisArg) {
10019 var Ctor = createCtorWrapper(func);
10021 function wrapper() {
10022 var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10023 return fn.apply(thisArg, arguments);
10029 * Creates a `Set` cache object to optimize linear searches of large arrays.
10032 * @param {Array} [values] The values to cache.
10033 * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`.
10035 var createCache = !(nativeCreate && Set) ? constant(null) : function(values) {
10036 return new SetCache(values);
10040 * Creates a function that produces an instance of `Ctor` regardless of
10041 * whether it was invoked as part of a `new` expression or by `call` or `apply`.
10044 * @param {Function} Ctor The constructor to wrap.
10045 * @returns {Function} Returns the new wrapped function.
10047 function createCtorWrapper(Ctor) {
10048 return function() {
10049 // Use a `switch` statement to work with class constructors.
10050 // See https://people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-function-objects-call-thisargument-argumentslist
10051 // for more details.
10052 var args = arguments;
10053 switch (args.length) {
10054 case 0: return new Ctor;
10055 case 1: return new Ctor(args[0]);
10056 case 2: return new Ctor(args[0], args[1]);
10057 case 3: return new Ctor(args[0], args[1], args[2]);
10058 case 4: return new Ctor(args[0], args[1], args[2], args[3]);
10059 case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
10061 var thisBinding = baseCreate(Ctor.prototype),
10062 result = Ctor.apply(thisBinding, args);
10064 // Mimic the constructor's `return` behavior.
10065 // See https://es5.github.io/#x13.2.2 for more details.
10066 return isObject(result) ? result : thisBinding;
10071 * Creates a `_.find` or `_.findLast` function.
10074 * @param {Function} eachFunc The function to iterate over a collection.
10075 * @param {boolean} [fromRight] Specify iterating from right to left.
10076 * @returns {Function} Returns the new find function.
10078 function createFind(eachFunc, fromRight) {
10079 return function(collection, predicate, thisArg) {
10080 predicate = getCallback(predicate, thisArg, 3);
10081 if (isArray(collection)) {
10082 var index = baseFindIndex(collection, predicate, fromRight);
10083 return index > -1 ? collection[index] : undefined;
10085 return baseFind(collection, predicate, eachFunc);
10090 * Creates a function for `_.forEach` or `_.forEachRight`.
10093 * @param {Function} arrayFunc The function to iterate over an array.
10094 * @param {Function} eachFunc The function to iterate over a collection.
10095 * @returns {Function} Returns the new each function.
10097 function createForEach(arrayFunc, eachFunc) {
10098 return function(collection, iteratee, thisArg) {
10099 return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
10100 ? arrayFunc(collection, iteratee)
10101 : eachFunc(collection, bindCallback(iteratee, thisArg, 3));
10106 * Creates a function for `_.forOwn` or `_.forOwnRight`.
10109 * @param {Function} objectFunc The function to iterate over an object.
10110 * @returns {Function} Returns the new each function.
10112 function createForOwn(objectFunc) {
10113 return function(object, iteratee, thisArg) {
10114 if (typeof iteratee != 'function' || thisArg !== undefined) {
10115 iteratee = bindCallback(iteratee, thisArg, 3);
10117 return objectFunc(object, iteratee);
10122 * Creates a function for `_.reduce` or `_.reduceRight`.
10125 * @param {Function} arrayFunc The function to iterate over an array.
10126 * @param {Function} eachFunc The function to iterate over a collection.
10127 * @returns {Function} Returns the new each function.
10129 function createReduce(arrayFunc, eachFunc) {
10130 return function(collection, iteratee, accumulator, thisArg) {
10131 var initFromArray = arguments.length < 3;
10132 return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
10133 ? arrayFunc(collection, iteratee, accumulator, initFromArray)
10134 : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc);
10139 * Creates a function that wraps `func` and invokes it with optional `this`
10140 * binding of, partial application, and currying.
10143 * @param {Function|string} func The function or method name to reference.
10144 * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
10145 * @param {*} [thisArg] The `this` binding of `func`.
10146 * @param {Array} [partials] The arguments to prepend to those provided to the new function.
10147 * @param {Array} [holders] The `partials` placeholder indexes.
10148 * @param {Array} [partialsRight] The arguments to append to those provided to the new function.
10149 * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
10150 * @param {Array} [argPos] The argument positions of the new function.
10151 * @param {number} [ary] The arity cap of `func`.
10152 * @param {number} [arity] The arity of `func`.
10153 * @returns {Function} Returns the new wrapped function.
10155 function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
10156 var isAry = bitmask & ARY_FLAG,
10157 isBind = bitmask & BIND_FLAG,
10158 isBindKey = bitmask & BIND_KEY_FLAG,
10159 isCurry = bitmask & CURRY_FLAG,
10160 isCurryBound = bitmask & CURRY_BOUND_FLAG,
10161 isCurryRight = bitmask & CURRY_RIGHT_FLAG,
10162 Ctor = isBindKey ? null : createCtorWrapper(func);
10164 function wrapper() {
10165 // Avoid `arguments` object use disqualifying optimizations by
10166 // converting it to an array before providing it to other functions.
10167 var length = arguments.length,
10169 args = Array(length);
10172 args[index] = arguments[index];
10175 args = composeArgs(args, partials, holders);
10177 if (partialsRight) {
10178 args = composeArgsRight(args, partialsRight, holdersRight);
10180 if (isCurry || isCurryRight) {
10181 var placeholder = wrapper.placeholder,
10182 argsHolders = replaceHolders(args, placeholder);
10184 length -= argsHolders.length;
10185 if (length < arity) {
10186 var newArgPos = argPos ? arrayCopy(argPos) : null,
10187 newArity = nativeMax(arity - length, 0),
10188 newsHolders = isCurry ? argsHolders : null,
10189 newHoldersRight = isCurry ? null : argsHolders,
10190 newPartials = isCurry ? args : null,
10191 newPartialsRight = isCurry ? null : args;
10193 bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
10194 bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
10196 if (!isCurryBound) {
10197 bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
10199 var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity],
10200 result = createHybridWrapper.apply(undefined, newData);
10202 if (isLaziable(func)) {
10203 setData(result, newData);
10205 result.placeholder = placeholder;
10209 var thisBinding = isBind ? thisArg : this,
10210 fn = isBindKey ? thisBinding[func] : func;
10213 args = reorder(args, argPos);
10215 if (isAry && ary < args.length) {
10218 if (this && this !== root && this instanceof wrapper) {
10219 fn = Ctor || createCtorWrapper(func);
10221 return fn.apply(thisBinding, args);
10227 * Creates a function that wraps `func` and invokes it with the optional `this`
10228 * binding of `thisArg` and the `partials` prepended to those provided to
10232 * @param {Function} func The function to partially apply arguments to.
10233 * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
10234 * @param {*} thisArg The `this` binding of `func`.
10235 * @param {Array} partials The arguments to prepend to those provided to the new function.
10236 * @returns {Function} Returns the new bound function.
10238 function createPartialWrapper(func, bitmask, thisArg, partials) {
10239 var isBind = bitmask & BIND_FLAG,
10240 Ctor = createCtorWrapper(func);
10242 function wrapper() {
10243 // Avoid `arguments` object use disqualifying optimizations by
10244 // converting it to an array before providing it `func`.
10245 var argsIndex = -1,
10246 argsLength = arguments.length,
10248 leftLength = partials.length,
10249 args = Array(argsLength + leftLength);
10251 while (++leftIndex < leftLength) {
10252 args[leftIndex] = partials[leftIndex];
10254 while (argsLength--) {
10255 args[leftIndex++] = arguments[++argsIndex];
10257 var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10258 return fn.apply(isBind ? thisArg : this, args);
10264 * Creates a function that either curries or invokes `func` with optional
10265 * `this` binding and partially applied arguments.
10268 * @param {Function|string} func The function or method name to reference.
10269 * @param {number} bitmask The bitmask of flags.
10270 * The bitmask may be composed of the following flags:
10273 * 4 - `_.curry` or `_.curryRight` of a bound function
10275 * 16 - `_.curryRight`
10277 * 64 - `_.partialRight`
10280 * @param {*} [thisArg] The `this` binding of `func`.
10281 * @param {Array} [partials] The arguments to be partially applied.
10282 * @param {Array} [holders] The `partials` placeholder indexes.
10283 * @param {Array} [argPos] The argument positions of the new function.
10284 * @param {number} [ary] The arity cap of `func`.
10285 * @param {number} [arity] The arity of `func`.
10286 * @returns {Function} Returns the new wrapped function.
10288 function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
10289 var isBindKey = bitmask & BIND_KEY_FLAG;
10290 if (!isBindKey && typeof func != 'function') {
10291 throw new TypeError(FUNC_ERROR_TEXT);
10293 var length = partials ? partials.length : 0;
10295 bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
10296 partials = holders = null;
10298 length -= (holders ? holders.length : 0);
10299 if (bitmask & PARTIAL_RIGHT_FLAG) {
10300 var partialsRight = partials,
10301 holdersRight = holders;
10303 partials = holders = null;
10305 var data = isBindKey ? null : getData(func),
10306 newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
10309 mergeData(newData, data);
10310 bitmask = newData[1];
10311 arity = newData[9];
10313 newData[9] = arity == null
10314 ? (isBindKey ? 0 : func.length)
10315 : (nativeMax(arity - length, 0) || 0);
10317 if (bitmask == BIND_FLAG) {
10318 var result = createBindWrapper(newData[0], newData[2]);
10319 } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) {
10320 result = createPartialWrapper.apply(undefined, newData);
10322 result = createHybridWrapper.apply(undefined, newData);
10324 var setter = data ? baseSetData : setData;
10325 return setter(result, newData);
10329 * A specialized version of `baseIsEqualDeep` for arrays with support for
10330 * partial deep comparisons.
10333 * @param {Array} array The array to compare.
10334 * @param {Array} other The other array to compare.
10335 * @param {Function} equalFunc The function to determine equivalents of values.
10336 * @param {Function} [customizer] The function to customize comparing arrays.
10337 * @param {boolean} [isLoose] Specify performing partial comparisons.
10338 * @param {Array} [stackA] Tracks traversed `value` objects.
10339 * @param {Array} [stackB] Tracks traversed `other` objects.
10340 * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
10342 function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) {
10344 arrLength = array.length,
10345 othLength = other.length;
10347 if (arrLength != othLength && !(isLoose && othLength > arrLength)) {
10350 // Ignore non-index properties.
10351 while (++index < arrLength) {
10352 var arrValue = array[index],
10353 othValue = other[index],
10354 result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined;
10356 if (result !== undefined) {
10362 // Recursively compare arrays (susceptible to call stack limits).
10364 if (!arraySome(other, function(othValue) {
10365 return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB);
10369 } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) {
10377 * A specialized version of `baseIsEqualDeep` for comparing objects of
10378 * the same `toStringTag`.
10380 * **Note:** This function only supports comparing values with tags of
10381 * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
10384 * @param {Object} value The object to compare.
10385 * @param {Object} other The other object to compare.
10386 * @param {string} tag The `toStringTag` of the objects to compare.
10387 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
10389 function equalByTag(object, other, tag) {
10393 // Coerce dates and booleans to numbers, dates to milliseconds and booleans
10394 // to `1` or `0` treating invalid dates coerced to `NaN` as not equal.
10395 return +object == +other;
10398 return object.name == other.name && object.message == other.message;
10401 // Treat `NaN` vs. `NaN` as equal.
10402 return (object != +object)
10404 : object == +other;
10408 // Coerce regexes to strings and treat strings primitives and string
10409 // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details.
10410 return object == (other + '');
10416 * A specialized version of `baseIsEqualDeep` for objects with support for
10417 * partial deep comparisons.
10420 * @param {Object} object The object to compare.
10421 * @param {Object} other The other object to compare.
10422 * @param {Function} equalFunc The function to determine equivalents of values.
10423 * @param {Function} [customizer] The function to customize comparing values.
10424 * @param {boolean} [isLoose] Specify performing partial comparisons.
10425 * @param {Array} [stackA] Tracks traversed `value` objects.
10426 * @param {Array} [stackB] Tracks traversed `other` objects.
10427 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
10429 function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
10430 var objProps = keys(object),
10431 objLength = objProps.length,
10432 othProps = keys(other),
10433 othLength = othProps.length;
10435 if (objLength != othLength && !isLoose) {
10438 var index = objLength;
10440 var key = objProps[index];
10441 if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) {
10445 var skipCtor = isLoose;
10446 while (++index < objLength) {
10447 key = objProps[index];
10448 var objValue = object[key],
10449 othValue = other[key],
10450 result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined;
10452 // Recursively compare objects (susceptible to call stack limits).
10453 if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) {
10456 skipCtor || (skipCtor = key == 'constructor');
10459 var objCtor = object.constructor,
10460 othCtor = other.constructor;
10462 // Non `Object` object instances with different constructors are not equal.
10463 if (objCtor != othCtor &&
10464 ('constructor' in object && 'constructor' in other) &&
10465 !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
10466 typeof othCtor == 'function' && othCtor instanceof othCtor)) {
10474 * Gets the appropriate "callback" function. If the `_.callback` method is
10475 * customized this function returns the custom method, otherwise it returns
10476 * the `baseCallback` function. If arguments are provided the chosen function
10477 * is invoked with them and its result is returned.
10480 * @returns {Function} Returns the chosen function or its result.
10482 function getCallback(func, thisArg, argCount) {
10483 var result = lodash.callback || callback;
10484 result = result === callback ? baseCallback : result;
10485 return argCount ? result(func, thisArg, argCount) : result;
10489 * Gets metadata for `func`.
10492 * @param {Function} func The function to query.
10493 * @returns {*} Returns the metadata for `func`.
10495 var getData = !metaMap ? noop : function(func) {
10496 return metaMap.get(func);
10500 * Gets the name of `func`.
10503 * @param {Function} func The function to query.
10504 * @returns {string} Returns the function name.
10506 function getFuncName(func) {
10507 var result = func.name,
10508 array = realNames[result],
10509 length = array ? array.length : 0;
10512 var data = array[length],
10513 otherFunc = data.func;
10514 if (otherFunc == null || otherFunc == func) {
10522 * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
10523 * customized this function returns the custom method, otherwise it returns
10524 * the `baseIndexOf` function. If arguments are provided the chosen function
10525 * is invoked with them and its result is returned.
10528 * @returns {Function|number} Returns the chosen function or its result.
10530 function getIndexOf(collection, target, fromIndex) {
10531 var result = lodash.indexOf || indexOf;
10532 result = result === indexOf ? baseIndexOf : result;
10533 return collection ? result(collection, target, fromIndex) : result;
10537 * Gets the "length" property value of `object`.
10539 * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792)
10540 * that affects Safari on at least iOS 8.1-8.3 ARM64.
10543 * @param {Object} object The object to query.
10544 * @returns {*} Returns the "length" value.
10546 var getLength = baseProperty('length');
10549 * Gets the propery names, values, and compare flags of `object`.
10552 * @param {Object} object The object to query.
10553 * @returns {Array} Returns the match data of `object`.
10555 function getMatchData(object) {
10556 var result = pairs(object),
10557 length = result.length;
10560 result[length][2] = isStrictComparable(result[length][1]);
10566 * Gets the native function at `key` of `object`.
10569 * @param {Object} object The object to query.
10570 * @param {string} key The key of the method to get.
10571 * @returns {*} Returns the function if it's native, else `undefined`.
10573 function getNative(object, key) {
10574 var value = object == null ? undefined : object[key];
10575 return isNative(value) ? value : undefined;
10579 * Gets the view, applying any `transforms` to the `start` and `end` positions.
10582 * @param {number} start The start of the view.
10583 * @param {number} end The end of the view.
10584 * @param {Array} [transforms] The transformations to apply to the view.
10585 * @returns {Object} Returns an object containing the `start` and `end`
10586 * positions of the view.
10588 function getView(start, end, transforms) {
10590 length = transforms ? transforms.length : 0;
10592 while (++index < length) {
10593 var data = transforms[index],
10596 switch (data.type) {
10597 case 'drop': start += size; break;
10598 case 'dropRight': end -= size; break;
10599 case 'take': end = nativeMin(end, start + size); break;
10600 case 'takeRight': start = nativeMax(start, end - size); break;
10603 return { 'start': start, 'end': end };
10607 * Initializes an array clone.
10610 * @param {Array} array The array to clone.
10611 * @returns {Array} Returns the initialized clone.
10613 function initCloneArray(array) {
10614 var length = array.length,
10615 result = new array.constructor(length);
10617 // Add array properties assigned by `RegExp#exec`.
10618 if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
10619 result.index = array.index;
10620 result.input = array.input;
10626 * Initializes an object clone.
10629 * @param {Object} object The object to clone.
10630 * @returns {Object} Returns the initialized clone.
10632 function initCloneObject(object) {
10633 var Ctor = object.constructor;
10634 if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) {
10641 * Initializes an object clone based on its `toStringTag`.
10643 * **Note:** This function only supports cloning values with tags of
10644 * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
10647 * @param {Object} object The object to clone.
10648 * @param {string} tag The `toStringTag` of the object to clone.
10649 * @param {boolean} [isDeep] Specify a deep clone.
10650 * @returns {Object} Returns the initialized clone.
10652 function initCloneByTag(object, tag, isDeep) {
10653 var Ctor = object.constructor;
10655 case arrayBufferTag:
10656 return bufferClone(object);
10660 return new Ctor(+object);
10662 case float32Tag: case float64Tag:
10663 case int8Tag: case int16Tag: case int32Tag:
10664 case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
10665 // Safari 5 mobile incorrectly has `Object` as the constructor of typed arrays.
10666 if (Ctor instanceof Ctor) {
10667 Ctor = ctorByTag[tag];
10669 var buffer = object.buffer;
10670 return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length);
10674 return new Ctor(object);
10677 var result = new Ctor(object.source, reFlags.exec(object));
10678 result.lastIndex = object.lastIndex;
10684 * Checks if `value` is array-like.
10687 * @param {*} value The value to check.
10688 * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
10690 function isArrayLike(value) {
10691 return value != null && isLength(getLength(value));
10695 * Checks if `value` is a valid array-like index.
10698 * @param {*} value The value to check.
10699 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
10700 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
10702 function isIndex(value, length) {
10703 value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1;
10704 length = length == null ? MAX_SAFE_INTEGER : length;
10705 return value > -1 && value % 1 == 0 && value < length;
10709 * Checks if the provided arguments are from an iteratee call.
10712 * @param {*} value The potential iteratee value argument.
10713 * @param {*} index The potential iteratee index or key argument.
10714 * @param {*} object The potential iteratee object argument.
10715 * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`.
10717 function isIterateeCall(value, index, object) {
10718 if (!isObject(object)) {
10721 var type = typeof index;
10722 if (type == 'number'
10723 ? (isArrayLike(object) && isIndex(index, object.length))
10724 : (type == 'string' && index in object)) {
10725 var other = object[index];
10726 return value === value ? (value === other) : (other !== other);
10732 * Checks if `value` is a property name and not a property path.
10735 * @param {*} value The value to check.
10736 * @param {Object} [object] The object to query keys on.
10737 * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
10739 function isKey(value, object) {
10740 var type = typeof value;
10741 if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') {
10744 if (isArray(value)) {
10747 var result = !reIsDeepProp.test(value);
10748 return result || (object != null && value in toObject(object));
10752 * Checks if `func` has a lazy counterpart.
10755 * @param {Function} func The function to check.
10756 * @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`.
10758 function isLaziable(func) {
10759 var funcName = getFuncName(func);
10760 if (!(funcName in LazyWrapper.prototype)) {
10763 var other = lodash[funcName];
10764 if (func === other) {
10767 var data = getData(other);
10768 return !!data && func === data[0];
10772 * Checks if `value` is a valid array-like length.
10774 * **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength).
10777 * @param {*} value The value to check.
10778 * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
10780 function isLength(value) {
10781 return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
10785 * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
10788 * @param {*} value The value to check.
10789 * @returns {boolean} Returns `true` if `value` if suitable for strict
10790 * equality comparisons, else `false`.
10792 function isStrictComparable(value) {
10793 return value === value && !isObject(value);
10797 * Merges the function metadata of `source` into `data`.
10799 * Merging metadata reduces the number of wrappers required to invoke a function.
10800 * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
10801 * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg`
10802 * augment function arguments, making the order in which they are executed important,
10803 * preventing the merging of metadata. However, we make an exception for a safe
10804 * common case where curried functions have `_.ary` and or `_.rearg` applied.
10807 * @param {Array} data The destination metadata.
10808 * @param {Array} source The source metadata.
10809 * @returns {Array} Returns `data`.
10811 function mergeData(data, source) {
10812 var bitmask = data[1],
10813 srcBitmask = source[1],
10814 newBitmask = bitmask | srcBitmask,
10815 isCommon = newBitmask < ARY_FLAG;
10818 (srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) ||
10819 (srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) ||
10820 (srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG);
10822 // Exit early if metadata can't be merged.
10823 if (!(isCommon || isCombo)) {
10826 // Use source `thisArg` if available.
10827 if (srcBitmask & BIND_FLAG) {
10828 data[2] = source[2];
10829 // Set when currying a bound function.
10830 newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG;
10832 // Compose partial arguments.
10833 var value = source[3];
10835 var partials = data[3];
10836 data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value);
10837 data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]);
10839 // Compose partial right arguments.
10842 partials = data[5];
10843 data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value);
10844 data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]);
10846 // Use source `argPos` if available.
10849 data[7] = arrayCopy(value);
10851 // Use source `ary` if it's smaller.
10852 if (srcBitmask & ARY_FLAG) {
10853 data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
10855 // Use source `arity` if one is not provided.
10856 if (data[9] == null) {
10857 data[9] = source[9];
10859 // Use source `func` and merge bitmasks.
10860 data[0] = source[0];
10861 data[1] = newBitmask;
10867 * A specialized version of `_.pick` which picks `object` properties specified
10871 * @param {Object} object The source object.
10872 * @param {string[]} props The property names to pick.
10873 * @returns {Object} Returns the new object.
10875 function pickByArray(object, props) {
10876 object = toObject(object);
10879 length = props.length,
10882 while (++index < length) {
10883 var key = props[index];
10884 if (key in object) {
10885 result[key] = object[key];
10892 * A specialized version of `_.pick` which picks `object` properties `predicate`
10893 * returns truthy for.
10896 * @param {Object} object The source object.
10897 * @param {Function} predicate The function invoked per iteration.
10898 * @returns {Object} Returns the new object.
10900 function pickByCallback(object, predicate) {
10902 baseForIn(object, function(value, key, object) {
10903 if (predicate(value, key, object)) {
10904 result[key] = value;
10911 * Reorder `array` according to the specified indexes where the element at
10912 * the first index is assigned as the first element, the element at
10913 * the second index is assigned as the second element, and so on.
10916 * @param {Array} array The array to reorder.
10917 * @param {Array} indexes The arranged array indexes.
10918 * @returns {Array} Returns `array`.
10920 function reorder(array, indexes) {
10921 var arrLength = array.length,
10922 length = nativeMin(indexes.length, arrLength),
10923 oldArray = arrayCopy(array);
10926 var index = indexes[length];
10927 array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
10933 * Sets metadata for `func`.
10935 * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
10936 * period of time, it will trip its breaker and transition to an identity function
10937 * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070)
10938 * for more details.
10941 * @param {Function} func The function to associate metadata with.
10942 * @param {*} data The metadata.
10943 * @returns {Function} Returns `func`.
10945 var setData = (function() {
10949 return function(key, value) {
10951 remaining = HOT_SPAN - (stamp - lastCalled);
10953 lastCalled = stamp;
10954 if (remaining > 0) {
10955 if (++count >= HOT_COUNT) {
10961 return baseSetData(key, value);
10966 * A fallback implementation of `_.isPlainObject` which checks if `value`
10967 * is an object created by the `Object` constructor or has a `[[Prototype]]`
10971 * @param {*} value The value to check.
10972 * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
10974 function shimIsPlainObject(value) {
10976 support = lodash.support;
10978 // Exit early for non `Object` objects.
10979 if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isHostObject(value)) ||
10980 (!hasOwnProperty.call(value, 'constructor') &&
10981 (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor))) ||
10982 (!support.argsTag && isArguments(value))) {
10985 // IE < 9 iterates inherited properties before own properties. If the first
10986 // iterated property is an object's own property then there are no inherited
10987 // enumerable properties.
10989 if (support.ownLast) {
10990 baseForIn(value, function(subValue, key, object) {
10991 result = hasOwnProperty.call(object, key);
10994 return result !== false;
10996 // In most environments an object's own properties are iterated before
10997 // its inherited properties. If the last iterated property is an object's
10998 // own property then there are no inherited enumerable properties.
10999 baseForIn(value, function(subValue, key) {
11002 return result === undefined || hasOwnProperty.call(value, result);
11006 * A fallback implementation of `Object.keys` which creates an array of the
11007 * own enumerable property names of `object`.
11010 * @param {Object} object The object to query.
11011 * @returns {Array} Returns the array of property names.
11013 function shimKeys(object) {
11014 var props = keysIn(object),
11015 propsLength = props.length,
11016 length = propsLength && object.length;
11018 var allowIndexes = !!length && isLength(length) &&
11019 (isArray(object) || isArguments(object) || isString(object));
11024 while (++index < propsLength) {
11025 var key = props[index];
11026 if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) {
11034 * Converts `value` to an object if it's not one.
11037 * @param {*} value The value to process.
11038 * @returns {Object} Returns the object.
11040 function toObject(value) {
11041 if (lodash.support.unindexedChars && isString(value)) {
11043 length = value.length,
11044 result = Object(value);
11046 while (++index < length) {
11047 result[index] = value.charAt(index);
11051 return isObject(value) ? value : Object(value);
11055 * Converts `value` to property path array if it's not one.
11058 * @param {*} value The value to process.
11059 * @returns {Array} Returns the property path array.
11061 function toPath(value) {
11062 if (isArray(value)) {
11066 baseToString(value).replace(rePropName, function(match, number, quote, string) {
11067 result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
11073 * Creates a clone of `wrapper`.
11076 * @param {Object} wrapper The wrapper to clone.
11077 * @returns {Object} Returns the cloned wrapper.
11079 function wrapperClone(wrapper) {
11080 return wrapper instanceof LazyWrapper
11082 : new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__, arrayCopy(wrapper.__actions__));
11085 /*------------------------------------------------------------------------*/
11088 * Creates an array of elements split into groups the length of `size`.
11089 * If `collection` can't be split evenly, the final chunk will be the remaining
11095 * @param {Array} array The array to process.
11096 * @param {number} [size=1] The length of each chunk.
11097 * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
11098 * @returns {Array} Returns the new array containing chunks.
11101 * _.chunk(['a', 'b', 'c', 'd'], 2);
11102 * // => [['a', 'b'], ['c', 'd']]
11104 * _.chunk(['a', 'b', 'c', 'd'], 3);
11105 * // => [['a', 'b', 'c'], ['d']]
11107 function chunk(array, size, guard) {
11108 if (guard ? isIterateeCall(array, size, guard) : size == null) {
11111 size = nativeMax(+size || 1, 1);
11114 length = array ? array.length : 0,
11116 result = Array(ceil(length / size));
11118 while (index < length) {
11119 result[++resIndex] = baseSlice(array, index, (index += size));
11125 * Creates an array with all falsey values removed. The values `false`, `null`,
11126 * `0`, `""`, `undefined`, and `NaN` are falsey.
11131 * @param {Array} array The array to compact.
11132 * @returns {Array} Returns the new array of filtered values.
11135 * _.compact([0, 1, false, 2, '', 3]);
11138 function compact(array) {
11140 length = array ? array.length : 0,
11144 while (++index < length) {
11145 var value = array[index];
11147 result[++resIndex] = value;
11154 * Creates an array of unique `array` values not included in the other
11155 * provided arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11156 * for equality comparisons.
11161 * @param {Array} array The array to inspect.
11162 * @param {...Array} [values] The arrays of values to exclude.
11163 * @returns {Array} Returns the new array of filtered values.
11166 * _.difference([1, 2, 3], [4, 2]);
11169 var difference = restParam(function(array, values) {
11170 return isArrayLike(array)
11171 ? baseDifference(array, baseFlatten(values, false, true))
11176 * Gets the first element of `array`.
11182 * @param {Array} array The array to query.
11183 * @returns {*} Returns the first element of `array`.
11186 * _.first([1, 2, 3]);
11192 function first(array) {
11193 return array ? array[0] : undefined;
11197 * Flattens a nested array. If `isDeep` is `true` the array is recursively
11198 * flattened, otherwise it is only flattened a single level.
11203 * @param {Array} array The array to flatten.
11204 * @param {boolean} [isDeep] Specify a deep flatten.
11205 * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
11206 * @returns {Array} Returns the new flattened array.
11209 * _.flatten([1, [2, 3, [4]]]);
11210 * // => [1, 2, 3, [4]]
11212 * // using `isDeep`
11213 * _.flatten([1, [2, 3, [4]]], true);
11214 * // => [1, 2, 3, 4]
11216 function flatten(array, isDeep, guard) {
11217 var length = array ? array.length : 0;
11218 if (guard && isIterateeCall(array, isDeep, guard)) {
11221 return length ? baseFlatten(array, isDeep) : [];
11225 * Gets the index at which the first occurrence of `value` is found in `array`
11226 * using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11227 * for equality comparisons. If `fromIndex` is negative, it is used as the offset
11228 * from the end of `array`. If `array` is sorted providing `true` for `fromIndex`
11229 * performs a faster binary search.
11234 * @param {Array} array The array to search.
11235 * @param {*} value The value to search for.
11236 * @param {boolean|number} [fromIndex=0] The index to search from or `true`
11237 * to perform a binary search on a sorted array.
11238 * @returns {number} Returns the index of the matched value, else `-1`.
11241 * _.indexOf([1, 2, 1, 2], 2);
11244 * // using `fromIndex`
11245 * _.indexOf([1, 2, 1, 2], 2, 2);
11248 * // performing a binary search
11249 * _.indexOf([1, 1, 2, 2], 2, true);
11252 function indexOf(array, value, fromIndex) {
11253 var length = array ? array.length : 0;
11257 if (typeof fromIndex == 'number') {
11258 fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex;
11259 } else if (fromIndex) {
11260 var index = binaryIndex(array, value),
11261 other = array[index];
11263 if (value === value ? (value === other) : (other !== other)) {
11268 return baseIndexOf(array, value, fromIndex || 0);
11272 * Creates an array of unique values that are included in all of the provided
11273 * arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11274 * for equality comparisons.
11279 * @param {...Array} [arrays] The arrays to inspect.
11280 * @returns {Array} Returns the new array of shared values.
11282 * _.intersection([1, 2], [4, 2], [2, 1]);
11285 var intersection = restParam(function(arrays) {
11286 var othLength = arrays.length,
11287 othIndex = othLength,
11288 caches = Array(length),
11289 indexOf = getIndexOf(),
11290 isCommon = indexOf == baseIndexOf,
11293 while (othIndex--) {
11294 var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : [];
11295 caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null;
11297 var array = arrays[0],
11299 length = array ? array.length : 0,
11303 while (++index < length) {
11304 value = array[index];
11305 if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) {
11306 var othIndex = othLength;
11307 while (--othIndex) {
11308 var cache = caches[othIndex];
11309 if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) {
11316 result.push(value);
11323 * Gets the last element of `array`.
11328 * @param {Array} array The array to query.
11329 * @returns {*} Returns the last element of `array`.
11332 * _.last([1, 2, 3]);
11335 function last(array) {
11336 var length = array ? array.length : 0;
11337 return length ? array[length - 1] : undefined;
11341 * Creates an array of unique values, in order, from all of the provided arrays
11342 * using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11343 * for equality comparisons.
11348 * @param {...Array} [arrays] The arrays to inspect.
11349 * @returns {Array} Returns the new array of combined values.
11352 * _.union([1, 2], [4, 2], [2, 1]);
11355 var union = restParam(function(arrays) {
11356 return baseUniq(baseFlatten(arrays, false, true));
11360 * Creates a duplicate-free version of an array, using
11361 * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11362 * for equality comparisons, in which only the first occurence of each element
11363 * is kept. Providing `true` for `isSorted` performs a faster search algorithm
11364 * for sorted arrays. If an iteratee function is provided it is invoked for
11365 * each element in the array to generate the criterion by which uniqueness
11366 * is computed. The `iteratee` is bound to `thisArg` and invoked with three
11367 * arguments: (value, index, array).
11369 * If a property name is provided for `iteratee` the created `_.property`
11370 * style callback returns the property value of the given element.
11372 * If a value is also provided for `thisArg` the created `_.matchesProperty`
11373 * style callback returns `true` for elements that have a matching property
11374 * value, else `false`.
11376 * If an object is provided for `iteratee` the created `_.matches` style
11377 * callback returns `true` for elements that have the properties of the given
11378 * object, else `false`.
11384 * @param {Array} array The array to inspect.
11385 * @param {boolean} [isSorted] Specify the array is sorted.
11386 * @param {Function|Object|string} [iteratee] The function invoked per iteration.
11387 * @param {*} [thisArg] The `this` binding of `iteratee`.
11388 * @returns {Array} Returns the new duplicate-value-free array.
11391 * _.uniq([2, 1, 2]);
11394 * // using `isSorted`
11395 * _.uniq([1, 1, 2], true);
11398 * // using an iteratee function
11399 * _.uniq([1, 2.5, 1.5, 2], function(n) {
11400 * return this.floor(n);
11404 * // using the `_.property` callback shorthand
11405 * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
11406 * // => [{ 'x': 1 }, { 'x': 2 }]
11408 function uniq(array, isSorted, iteratee, thisArg) {
11409 var length = array ? array.length : 0;
11413 if (isSorted != null && typeof isSorted != 'boolean') {
11414 thisArg = iteratee;
11415 iteratee = isIterateeCall(array, isSorted, thisArg) ? null : isSorted;
11418 var callback = getCallback();
11419 if (!(iteratee == null && callback === baseCallback)) {
11420 iteratee = callback(iteratee, thisArg, 3);
11422 return (isSorted && getIndexOf() == baseIndexOf)
11423 ? sortedUniq(array, iteratee)
11424 : baseUniq(array, iteratee);
11428 * Creates an array excluding all provided values using
11429 * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11430 * for equality comparisons.
11435 * @param {Array} array The array to filter.
11436 * @param {...*} [values] The values to exclude.
11437 * @returns {Array} Returns the new array of filtered values.
11440 * _.without([1, 2, 1, 3], 1, 2);
11443 var without = restParam(function(array, values) {
11444 return isArrayLike(array)
11445 ? baseDifference(array, values)
11449 /*------------------------------------------------------------------------*/
11452 * Creates a `lodash` object that wraps `value` with explicit method
11453 * chaining enabled.
11458 * @param {*} value The value to wrap.
11459 * @returns {Object} Returns the new `lodash` wrapper instance.
11463 * { 'user': 'barney', 'age': 36 },
11464 * { 'user': 'fred', 'age': 40 },
11465 * { 'user': 'pebbles', 'age': 1 }
11468 * var youngest = _.chain(users)
11470 * .map(function(chr) {
11471 * return chr.user + ' is ' + chr.age;
11475 * // => 'pebbles is 1'
11477 function chain(value) {
11478 var result = lodash(value);
11479 result.__chain__ = true;
11484 * This method invokes `interceptor` and returns `value`. The interceptor is
11485 * bound to `thisArg` and invoked with one argument; (value). The purpose of
11486 * this method is to "tap into" a method chain in order to perform operations
11487 * on intermediate results within the chain.
11492 * @param {*} value The value to provide to `interceptor`.
11493 * @param {Function} interceptor The function to invoke.
11494 * @param {*} [thisArg] The `this` binding of `interceptor`.
11495 * @returns {*} Returns `value`.
11499 * .tap(function(array) {
11506 function tap(value, interceptor, thisArg) {
11507 interceptor.call(thisArg, value);
11512 * This method is like `_.tap` except that it returns the result of `interceptor`.
11517 * @param {*} value The value to provide to `interceptor`.
11518 * @param {Function} interceptor The function to invoke.
11519 * @param {*} [thisArg] The `this` binding of `interceptor`.
11520 * @returns {*} Returns the result of `interceptor`.
11526 * .thru(function(value) {
11532 function thru(value, interceptor, thisArg) {
11533 return interceptor.call(thisArg, value);
11537 * Enables explicit method chaining on the wrapper object.
11542 * @returns {Object} Returns the new `lodash` wrapper instance.
11546 * { 'user': 'barney', 'age': 36 },
11547 * { 'user': 'fred', 'age': 40 }
11550 * // without explicit chaining
11551 * _(users).first();
11552 * // => { 'user': 'barney', 'age': 36 }
11554 * // with explicit chaining
11559 * // => { 'user': 'barney' }
11561 function wrapperChain() {
11562 return chain(this);
11566 * Executes the chained sequence and returns the wrapped result.
11571 * @returns {Object} Returns the new `lodash` wrapper instance.
11574 * var array = [1, 2];
11575 * var wrapper = _(array).push(3);
11577 * console.log(array);
11580 * wrapper = wrapper.commit();
11581 * console.log(array);
11587 * console.log(array);
11590 function wrapperCommit() {
11591 return new LodashWrapper(this.value(), this.__chain__);
11595 * Creates a clone of the chained sequence planting `value` as the wrapped value.
11600 * @returns {Object} Returns the new `lodash` wrapper instance.
11603 * var array = [1, 2];
11604 * var wrapper = _(array).map(function(value) {
11605 * return Math.pow(value, 2);
11608 * var other = [3, 4];
11609 * var otherWrapper = wrapper.plant(other);
11611 * otherWrapper.value();
11617 function wrapperPlant(value) {
11621 while (parent instanceof baseLodash) {
11622 var clone = wrapperClone(parent);
11624 previous.__wrapped__ = clone;
11628 var previous = clone;
11629 parent = parent.__wrapped__;
11631 previous.__wrapped__ = value;
11636 * Reverses the wrapped array so the first element becomes the last, the
11637 * second element becomes the second to last, and so on.
11639 * **Note:** This method mutates the wrapped array.
11644 * @returns {Object} Returns the new reversed `lodash` wrapper instance.
11647 * var array = [1, 2, 3];
11649 * _(array).reverse().value()
11652 * console.log(array);
11655 function wrapperReverse() {
11656 var value = this.__wrapped__;
11657 if (value instanceof LazyWrapper) {
11658 if (this.__actions__.length) {
11659 value = new LazyWrapper(this);
11661 return new LodashWrapper(value.reverse(), this.__chain__);
11663 return this.thru(function(value) {
11664 return value.reverse();
11669 * Produces the result of coercing the unwrapped value to a string.
11674 * @returns {string} Returns the coerced string value.
11677 * _([1, 2, 3]).toString();
11680 function wrapperToString() {
11681 return (this.value() + '');
11685 * Executes the chained sequence to extract the unwrapped value.
11689 * @alias run, toJSON, valueOf
11691 * @returns {*} Returns the resolved unwrapped value.
11694 * _([1, 2, 3]).value();
11697 function wrapperValue() {
11698 return baseWrapperValue(this.__wrapped__, this.__actions__);
11701 /*------------------------------------------------------------------------*/
11704 * Checks if `predicate` returns truthy for **all** elements of `collection`.
11705 * The predicate is bound to `thisArg` and invoked with three arguments:
11706 * (value, index|key, collection).
11708 * If a property name is provided for `predicate` the created `_.property`
11709 * style callback returns the property value of the given element.
11711 * If a value is also provided for `thisArg` the created `_.matchesProperty`
11712 * style callback returns `true` for elements that have a matching property
11713 * value, else `false`.
11715 * If an object is provided for `predicate` the created `_.matches` style
11716 * callback returns `true` for elements that have the properties of the given
11717 * object, else `false`.
11722 * @category Collection
11723 * @param {Array|Object|string} collection The collection to iterate over.
11724 * @param {Function|Object|string} [predicate=_.identity] The function invoked
11726 * @param {*} [thisArg] The `this` binding of `predicate`.
11727 * @returns {boolean} Returns `true` if all elements pass the predicate check,
11731 * _.every([true, 1, null, 'yes'], Boolean);
11735 * { 'user': 'barney', 'active': false },
11736 * { 'user': 'fred', 'active': false }
11739 * // using the `_.matches` callback shorthand
11740 * _.every(users, { 'user': 'barney', 'active': false });
11743 * // using the `_.matchesProperty` callback shorthand
11744 * _.every(users, 'active', false);
11747 * // using the `_.property` callback shorthand
11748 * _.every(users, 'active');
11751 function every(collection, predicate, thisArg) {
11752 var func = isArray(collection) ? arrayEvery : baseEvery;
11753 if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
11756 if (typeof predicate != 'function' || thisArg !== undefined) {
11757 predicate = getCallback(predicate, thisArg, 3);
11759 return func(collection, predicate);
11763 * Iterates over elements of `collection`, returning an array of all elements
11764 * `predicate` returns truthy for. The predicate is bound to `thisArg` and
11765 * invoked with three arguments: (value, index|key, collection).
11767 * If a property name is provided for `predicate` the created `_.property`
11768 * style callback returns the property value of the given element.
11770 * If a value is also provided for `thisArg` the created `_.matchesProperty`
11771 * style callback returns `true` for elements that have a matching property
11772 * value, else `false`.
11774 * If an object is provided for `predicate` the created `_.matches` style
11775 * callback returns `true` for elements that have the properties of the given
11776 * object, else `false`.
11781 * @category Collection
11782 * @param {Array|Object|string} collection The collection to iterate over.
11783 * @param {Function|Object|string} [predicate=_.identity] The function invoked
11785 * @param {*} [thisArg] The `this` binding of `predicate`.
11786 * @returns {Array} Returns the new filtered array.
11789 * _.filter([4, 5, 6], function(n) {
11790 * return n % 2 == 0;
11795 * { 'user': 'barney', 'age': 36, 'active': true },
11796 * { 'user': 'fred', 'age': 40, 'active': false }
11799 * // using the `_.matches` callback shorthand
11800 * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user');
11803 * // using the `_.matchesProperty` callback shorthand
11804 * _.pluck(_.filter(users, 'active', false), 'user');
11807 * // using the `_.property` callback shorthand
11808 * _.pluck(_.filter(users, 'active'), 'user');
11811 function filter(collection, predicate, thisArg) {
11812 var func = isArray(collection) ? arrayFilter : baseFilter;
11813 predicate = getCallback(predicate, thisArg, 3);
11814 return func(collection, predicate);
11818 * Iterates over elements of `collection`, returning the first element
11819 * `predicate` returns truthy for. The predicate is bound to `thisArg` and
11820 * invoked with three arguments: (value, index|key, collection).
11822 * If a property name is provided for `predicate` the created `_.property`
11823 * style callback returns the property value of the given element.
11825 * If a value is also provided for `thisArg` the created `_.matchesProperty`
11826 * style callback returns `true` for elements that have a matching property
11827 * value, else `false`.
11829 * If an object is provided for `predicate` the created `_.matches` style
11830 * callback returns `true` for elements that have the properties of the given
11831 * object, else `false`.
11836 * @category Collection
11837 * @param {Array|Object|string} collection The collection to search.
11838 * @param {Function|Object|string} [predicate=_.identity] The function invoked
11840 * @param {*} [thisArg] The `this` binding of `predicate`.
11841 * @returns {*} Returns the matched element, else `undefined`.
11845 * { 'user': 'barney', 'age': 36, 'active': true },
11846 * { 'user': 'fred', 'age': 40, 'active': false },
11847 * { 'user': 'pebbles', 'age': 1, 'active': true }
11850 * _.result(_.find(users, function(chr) {
11851 * return chr.age < 40;
11855 * // using the `_.matches` callback shorthand
11856 * _.result(_.find(users, { 'age': 1, 'active': true }), 'user');
11859 * // using the `_.matchesProperty` callback shorthand
11860 * _.result(_.find(users, 'active', false), 'user');
11863 * // using the `_.property` callback shorthand
11864 * _.result(_.find(users, 'active'), 'user');
11867 var find = createFind(baseEach);
11870 * Iterates over elements of `collection` invoking `iteratee` for each element.
11871 * The `iteratee` is bound to `thisArg` and invoked with three arguments:
11872 * (value, index|key, collection). Iteratee functions may exit iteration early
11873 * by explicitly returning `false`.
11875 * **Note:** As with other "Collections" methods, objects with a "length" property
11876 * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
11877 * may be used for object iteration.
11882 * @category Collection
11883 * @param {Array|Object|string} collection The collection to iterate over.
11884 * @param {Function} [iteratee=_.identity] The function invoked per iteration.
11885 * @param {*} [thisArg] The `this` binding of `iteratee`.
11886 * @returns {Array|Object|string} Returns `collection`.
11889 * _([1, 2]).forEach(function(n) {
11892 * // => logs each value from left to right and returns the array
11894 * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) {
11895 * console.log(n, key);
11897 * // => logs each value-key pair and returns the object (iteration order is not guaranteed)
11899 var forEach = createForEach(arrayEach, baseEach);
11902 * Creates an object composed of keys generated from the results of running
11903 * each element of `collection` through `iteratee`. The corresponding value
11904 * of each key is an array of the elements responsible for generating the key.
11905 * The `iteratee` is bound to `thisArg` and invoked with three arguments:
11906 * (value, index|key, collection).
11908 * If a property name is provided for `iteratee` the created `_.property`
11909 * style callback returns the property value of the given element.
11911 * If a value is also provided for `thisArg` the created `_.matchesProperty`
11912 * style callback returns `true` for elements that have a matching property
11913 * value, else `false`.
11915 * If an object is provided for `iteratee` the created `_.matches` style
11916 * callback returns `true` for elements that have the properties of the given
11917 * object, else `false`.
11921 * @category Collection
11922 * @param {Array|Object|string} collection The collection to iterate over.
11923 * @param {Function|Object|string} [iteratee=_.identity] The function invoked
11925 * @param {*} [thisArg] The `this` binding of `iteratee`.
11926 * @returns {Object} Returns the composed aggregate object.
11929 * _.groupBy([4.2, 6.1, 6.4], function(n) {
11930 * return Math.floor(n);
11932 * // => { '4': [4.2], '6': [6.1, 6.4] }
11934 * _.groupBy([4.2, 6.1, 6.4], function(n) {
11935 * return this.floor(n);
11937 * // => { '4': [4.2], '6': [6.1, 6.4] }
11939 * // using the `_.property` callback shorthand
11940 * _.groupBy(['one', 'two', 'three'], 'length');
11941 * // => { '3': ['one', 'two'], '5': ['three'] }
11943 var groupBy = createAggregator(function(result, value, key) {
11944 if (hasOwnProperty.call(result, key)) {
11945 result[key].push(value);
11947 result[key] = [value];
11952 * Checks if `value` is in `collection` using
11953 * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11954 * for equality comparisons. If `fromIndex` is negative, it is used as the offset
11955 * from the end of `collection`.
11959 * @alias contains, include
11960 * @category Collection
11961 * @param {Array|Object|string} collection The collection to search.
11962 * @param {*} target The value to search for.
11963 * @param {number} [fromIndex=0] The index to search from.
11964 * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`.
11965 * @returns {boolean} Returns `true` if a matching element is found, else `false`.
11968 * _.includes([1, 2, 3], 1);
11971 * _.includes([1, 2, 3], 1, 2);
11974 * _.includes({ 'user': 'fred', 'age': 40 }, 'fred');
11977 * _.includes('pebbles', 'eb');
11980 function includes(collection, target, fromIndex, guard) {
11981 var length = collection ? getLength(collection) : 0;
11982 if (!isLength(length)) {
11983 collection = values(collection);
11984 length = collection.length;
11989 if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) {
11992 fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
11994 return (typeof collection == 'string' || !isArray(collection) && isString(collection))
11995 ? (fromIndex < length && collection.indexOf(target, fromIndex) > -1)
11996 : (getIndexOf(collection, target, fromIndex) > -1);
12000 * Creates an array of values by running each element in `collection` through
12001 * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three
12002 * arguments: (value, index|key, collection).
12004 * If a property name is provided for `iteratee` the created `_.property`
12005 * style callback returns the property value of the given element.
12007 * If a value is also provided for `thisArg` the created `_.matchesProperty`
12008 * style callback returns `true` for elements that have a matching property
12009 * value, else `false`.
12011 * If an object is provided for `iteratee` the created `_.matches` style
12012 * callback returns `true` for elements that have the properties of the given
12013 * object, else `false`.
12015 * Many lodash methods are guarded to work as iteratees for methods like
12016 * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
12018 * The guarded methods are:
12019 * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`,
12020 * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`,
12021 * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`,
12022 * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`,
12023 * `sum`, `uniq`, and `words`
12028 * @category Collection
12029 * @param {Array|Object|string} collection The collection to iterate over.
12030 * @param {Function|Object|string} [iteratee=_.identity] The function invoked
12032 * @param {*} [thisArg] The `this` binding of `iteratee`.
12033 * @returns {Array} Returns the new mapped array.
12036 * function timesThree(n) {
12040 * _.map([1, 2], timesThree);
12043 * _.map({ 'a': 1, 'b': 2 }, timesThree);
12044 * // => [3, 6] (iteration order is not guaranteed)
12047 * { 'user': 'barney' },
12048 * { 'user': 'fred' }
12051 * // using the `_.property` callback shorthand
12052 * _.map(users, 'user');
12053 * // => ['barney', 'fred']
12055 function map(collection, iteratee, thisArg) {
12056 var func = isArray(collection) ? arrayMap : baseMap;
12057 iteratee = getCallback(iteratee, thisArg, 3);
12058 return func(collection, iteratee);
12062 * Gets the property value of `path` from all elements in `collection`.
12066 * @category Collection
12067 * @param {Array|Object|string} collection The collection to iterate over.
12068 * @param {Array|string} path The path of the property to pluck.
12069 * @returns {Array} Returns the property values.
12073 * { 'user': 'barney', 'age': 36 },
12074 * { 'user': 'fred', 'age': 40 }
12077 * _.pluck(users, 'user');
12078 * // => ['barney', 'fred']
12080 * var userIndex = _.indexBy(users, 'user');
12081 * _.pluck(userIndex, 'age');
12082 * // => [36, 40] (iteration order is not guaranteed)
12084 function pluck(collection, path) {
12085 return map(collection, property(path));
12089 * Reduces `collection` to a value which is the accumulated result of running
12090 * each element in `collection` through `iteratee`, where each successive
12091 * invocation is supplied the return value of the previous. If `accumulator`
12092 * is not provided the first element of `collection` is used as the initial
12093 * value. The `iteratee` is bound to `thisArg` and invoked with four arguments:
12094 * (accumulator, value, index|key, collection).
12096 * Many lodash methods are guarded to work as iteratees for methods like
12097 * `_.reduce`, `_.reduceRight`, and `_.transform`.
12099 * The guarded methods are:
12100 * `assign`, `defaults`, `includes`, `merge`, `sortByAll`, and `sortByOrder`
12104 * @alias foldl, inject
12105 * @category Collection
12106 * @param {Array|Object|string} collection The collection to iterate over.
12107 * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12108 * @param {*} [accumulator] The initial value.
12109 * @param {*} [thisArg] The `this` binding of `iteratee`.
12110 * @returns {*} Returns the accumulated value.
12113 * _.reduce([1, 2], function(total, n) {
12114 * return total + n;
12118 * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) {
12119 * result[key] = n * 3;
12122 * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed)
12124 var reduce = createReduce(arrayReduce, baseEach);
12127 * The opposite of `_.filter`; this method returns the elements of `collection`
12128 * that `predicate` does **not** return truthy for.
12132 * @category Collection
12133 * @param {Array|Object|string} collection The collection to iterate over.
12134 * @param {Function|Object|string} [predicate=_.identity] The function invoked
12136 * @param {*} [thisArg] The `this` binding of `predicate`.
12137 * @returns {Array} Returns the new filtered array.
12140 * _.reject([1, 2, 3, 4], function(n) {
12141 * return n % 2 == 0;
12146 * { 'user': 'barney', 'age': 36, 'active': false },
12147 * { 'user': 'fred', 'age': 40, 'active': true }
12150 * // using the `_.matches` callback shorthand
12151 * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user');
12154 * // using the `_.matchesProperty` callback shorthand
12155 * _.pluck(_.reject(users, 'active', false), 'user');
12158 * // using the `_.property` callback shorthand
12159 * _.pluck(_.reject(users, 'active'), 'user');
12162 function reject(collection, predicate, thisArg) {
12163 var func = isArray(collection) ? arrayFilter : baseFilter;
12164 predicate = getCallback(predicate, thisArg, 3);
12165 return func(collection, function(value, index, collection) {
12166 return !predicate(value, index, collection);
12171 * Checks if `predicate` returns truthy for **any** element of `collection`.
12172 * The function returns as soon as it finds a passing value and does not iterate
12173 * over the entire collection. The predicate is bound to `thisArg` and invoked
12174 * with three arguments: (value, index|key, collection).
12176 * If a property name is provided for `predicate` the created `_.property`
12177 * style callback returns the property value of the given element.
12179 * If a value is also provided for `thisArg` the created `_.matchesProperty`
12180 * style callback returns `true` for elements that have a matching property
12181 * value, else `false`.
12183 * If an object is provided for `predicate` the created `_.matches` style
12184 * callback returns `true` for elements that have the properties of the given
12185 * object, else `false`.
12190 * @category Collection
12191 * @param {Array|Object|string} collection The collection to iterate over.
12192 * @param {Function|Object|string} [predicate=_.identity] The function invoked
12194 * @param {*} [thisArg] The `this` binding of `predicate`.
12195 * @returns {boolean} Returns `true` if any element passes the predicate check,
12199 * _.some([null, 0, 'yes', false], Boolean);
12203 * { 'user': 'barney', 'active': true },
12204 * { 'user': 'fred', 'active': false }
12207 * // using the `_.matches` callback shorthand
12208 * _.some(users, { 'user': 'barney', 'active': false });
12211 * // using the `_.matchesProperty` callback shorthand
12212 * _.some(users, 'active', false);
12215 * // using the `_.property` callback shorthand
12216 * _.some(users, 'active');
12219 function some(collection, predicate, thisArg) {
12220 var func = isArray(collection) ? arraySome : baseSome;
12221 if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
12224 if (typeof predicate != 'function' || thisArg !== undefined) {
12225 predicate = getCallback(predicate, thisArg, 3);
12227 return func(collection, predicate);
12230 /*------------------------------------------------------------------------*/
12233 * Gets the number of milliseconds that have elapsed since the Unix epoch
12234 * (1 January 1970 00:00:00 UTC).
12241 * _.defer(function(stamp) {
12242 * console.log(_.now() - stamp);
12244 * // => logs the number of milliseconds it took for the deferred function to be invoked
12246 var now = nativeNow || function() {
12247 return new Date().getTime();
12250 /*------------------------------------------------------------------------*/
12253 * Creates a function that invokes `func` with the `this` binding of `thisArg`
12254 * and prepends any additional `_.bind` arguments to those provided to the
12257 * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
12258 * may be used as a placeholder for partially applied arguments.
12260 * **Note:** Unlike native `Function#bind` this method does not set the "length"
12261 * property of bound functions.
12265 * @category Function
12266 * @param {Function} func The function to bind.
12267 * @param {*} thisArg The `this` binding of `func`.
12268 * @param {...*} [partials] The arguments to be partially applied.
12269 * @returns {Function} Returns the new bound function.
12272 * var greet = function(greeting, punctuation) {
12273 * return greeting + ' ' + this.user + punctuation;
12276 * var object = { 'user': 'fred' };
12278 * var bound = _.bind(greet, object, 'hi');
12282 * // using placeholders
12283 * var bound = _.bind(greet, object, _, '!');
12287 var bind = restParam(function(func, thisArg, partials) {
12288 var bitmask = BIND_FLAG;
12289 if (partials.length) {
12290 var holders = replaceHolders(partials, bind.placeholder);
12291 bitmask |= PARTIAL_FLAG;
12293 return createWrapper(func, bitmask, thisArg, partials, holders);
12297 * Creates a debounced function that delays invoking `func` until after `wait`
12298 * milliseconds have elapsed since the last time the debounced function was
12299 * invoked. The debounced function comes with a `cancel` method to cancel
12300 * delayed invocations. Provide an options object to indicate that `func`
12301 * should be invoked on the leading and/or trailing edge of the `wait` timeout.
12302 * Subsequent calls to the debounced function return the result of the last
12303 * `func` invocation.
12305 * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
12306 * on the trailing edge of the timeout only if the the debounced function is
12307 * invoked more than once during the `wait` timeout.
12309 * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
12310 * for details over the differences between `_.debounce` and `_.throttle`.
12314 * @category Function
12315 * @param {Function} func The function to debounce.
12316 * @param {number} [wait=0] The number of milliseconds to delay.
12317 * @param {Object} [options] The options object.
12318 * @param {boolean} [options.leading=false] Specify invoking on the leading
12319 * edge of the timeout.
12320 * @param {number} [options.maxWait] The maximum time `func` is allowed to be
12321 * delayed before it is invoked.
12322 * @param {boolean} [options.trailing=true] Specify invoking on the trailing
12323 * edge of the timeout.
12324 * @returns {Function} Returns the new debounced function.
12327 * // avoid costly calculations while the window size is in flux
12328 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
12330 * // invoke `sendMail` when the click event is fired, debouncing subsequent calls
12331 * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
12333 * 'trailing': false
12336 * // ensure `batchLog` is invoked once after 1 second of debounced calls
12337 * var source = new EventSource('/stream');
12338 * jQuery(source).on('message', _.debounce(batchLog, 250, {
12342 * // cancel a debounced call
12343 * var todoChanges = _.debounce(batchLog, 1000);
12344 * Object.observe(models.todo, todoChanges);
12346 * Object.observe(models, function(changes) {
12347 * if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
12348 * todoChanges.cancel();
12352 * // ...at some point `models.todo` is changed
12353 * models.todo.completed = true;
12355 * // ...before 1 second has passed `models.todo` is deleted
12356 * // which cancels the debounced `todoChanges` call
12357 * delete models.todo;
12359 function debounce(func, wait, options) {
12371 if (typeof func != 'function') {
12372 throw new TypeError(FUNC_ERROR_TEXT);
12374 wait = wait < 0 ? 0 : (+wait || 0);
12375 if (options === true) {
12376 var leading = true;
12378 } else if (isObject(options)) {
12379 leading = options.leading;
12380 maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
12381 trailing = 'trailing' in options ? options.trailing : trailing;
12384 function cancel() {
12386 clearTimeout(timeoutId);
12388 if (maxTimeoutId) {
12389 clearTimeout(maxTimeoutId);
12391 maxTimeoutId = timeoutId = trailingCall = undefined;
12394 function delayed() {
12395 var remaining = wait - (now() - stamp);
12396 if (remaining <= 0 || remaining > wait) {
12397 if (maxTimeoutId) {
12398 clearTimeout(maxTimeoutId);
12400 var isCalled = trailingCall;
12401 maxTimeoutId = timeoutId = trailingCall = undefined;
12403 lastCalled = now();
12404 result = func.apply(thisArg, args);
12405 if (!timeoutId && !maxTimeoutId) {
12406 args = thisArg = null;
12410 timeoutId = setTimeout(delayed, remaining);
12414 function maxDelayed() {
12416 clearTimeout(timeoutId);
12418 maxTimeoutId = timeoutId = trailingCall = undefined;
12419 if (trailing || (maxWait !== wait)) {
12420 lastCalled = now();
12421 result = func.apply(thisArg, args);
12422 if (!timeoutId && !maxTimeoutId) {
12423 args = thisArg = null;
12428 function debounced() {
12432 trailingCall = trailing && (timeoutId || !leading);
12434 if (maxWait === false) {
12435 var leadingCall = leading && !timeoutId;
12437 if (!maxTimeoutId && !leading) {
12438 lastCalled = stamp;
12440 var remaining = maxWait - (stamp - lastCalled),
12441 isCalled = remaining <= 0 || remaining > maxWait;
12444 if (maxTimeoutId) {
12445 maxTimeoutId = clearTimeout(maxTimeoutId);
12447 lastCalled = stamp;
12448 result = func.apply(thisArg, args);
12450 else if (!maxTimeoutId) {
12451 maxTimeoutId = setTimeout(maxDelayed, remaining);
12454 if (isCalled && timeoutId) {
12455 timeoutId = clearTimeout(timeoutId);
12457 else if (!timeoutId && wait !== maxWait) {
12458 timeoutId = setTimeout(delayed, wait);
12462 result = func.apply(thisArg, args);
12464 if (isCalled && !timeoutId && !maxTimeoutId) {
12465 args = thisArg = null;
12469 debounced.cancel = cancel;
12474 * Creates a function that invokes `func` with the `this` binding of the
12475 * created function and arguments from `start` and beyond provided as an array.
12477 * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
12481 * @category Function
12482 * @param {Function} func The function to apply a rest parameter to.
12483 * @param {number} [start=func.length-1] The start position of the rest parameter.
12484 * @returns {Function} Returns the new function.
12487 * var say = _.restParam(function(what, names) {
12488 * return what + ' ' + _.initial(names).join(', ') +
12489 * (_.size(names) > 1 ? ', & ' : '') + _.last(names);
12492 * say('hello', 'fred', 'barney', 'pebbles');
12493 * // => 'hello fred, barney, & pebbles'
12495 function restParam(func, start) {
12496 if (typeof func != 'function') {
12497 throw new TypeError(FUNC_ERROR_TEXT);
12499 start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0);
12500 return function() {
12501 var args = arguments,
12503 length = nativeMax(args.length - start, 0),
12504 rest = Array(length);
12506 while (++index < length) {
12507 rest[index] = args[start + index];
12510 case 0: return func.call(this, rest);
12511 case 1: return func.call(this, args[0], rest);
12512 case 2: return func.call(this, args[0], args[1], rest);
12514 var otherArgs = Array(start + 1);
12516 while (++index < start) {
12517 otherArgs[index] = args[index];
12519 otherArgs[start] = rest;
12520 return func.apply(this, otherArgs);
12525 * Creates a throttled function that only invokes `func` at most once per
12526 * every `wait` milliseconds. The throttled function comes with a `cancel`
12527 * method to cancel delayed invocations. Provide an options object to indicate
12528 * that `func` should be invoked on the leading and/or trailing edge of the
12529 * `wait` timeout. Subsequent calls to the throttled function return the
12530 * result of the last `func` call.
12532 * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
12533 * on the trailing edge of the timeout only if the the throttled function is
12534 * invoked more than once during the `wait` timeout.
12536 * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
12537 * for details over the differences between `_.throttle` and `_.debounce`.
12541 * @category Function
12542 * @param {Function} func The function to throttle.
12543 * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
12544 * @param {Object} [options] The options object.
12545 * @param {boolean} [options.leading=true] Specify invoking on the leading
12546 * edge of the timeout.
12547 * @param {boolean} [options.trailing=true] Specify invoking on the trailing
12548 * edge of the timeout.
12549 * @returns {Function} Returns the new throttled function.
12552 * // avoid excessively updating the position while scrolling
12553 * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
12555 * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes
12556 * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
12557 * 'trailing': false
12560 * // cancel a trailing throttled call
12561 * jQuery(window).on('popstate', throttled.cancel);
12563 function throttle(func, wait, options) {
12564 var leading = true,
12567 if (typeof func != 'function') {
12568 throw new TypeError(FUNC_ERROR_TEXT);
12570 if (options === false) {
12572 } else if (isObject(options)) {
12573 leading = 'leading' in options ? !!options.leading : leading;
12574 trailing = 'trailing' in options ? !!options.trailing : trailing;
12576 debounceOptions.leading = leading;
12577 debounceOptions.maxWait = +wait;
12578 debounceOptions.trailing = trailing;
12579 return debounce(func, wait, debounceOptions);
12582 /*------------------------------------------------------------------------*/
12585 * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned,
12586 * otherwise they are assigned by reference. If `customizer` is provided it is
12587 * invoked to produce the cloned values. If `customizer` returns `undefined`
12588 * cloning is handled by the method instead. The `customizer` is bound to
12589 * `thisArg` and invoked with two argument; (value [, index|key, object]).
12591 * **Note:** This method is loosely based on the
12592 * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
12593 * The enumerable properties of `arguments` objects and objects created by
12594 * constructors other than `Object` are cloned to plain `Object` objects. An
12595 * empty object is returned for uncloneable values such as functions, DOM nodes,
12596 * Maps, Sets, and WeakMaps.
12601 * @param {*} value The value to clone.
12602 * @param {boolean} [isDeep] Specify a deep clone.
12603 * @param {Function} [customizer] The function to customize cloning values.
12604 * @param {*} [thisArg] The `this` binding of `customizer`.
12605 * @returns {*} Returns the cloned value.
12609 * { 'user': 'barney' },
12610 * { 'user': 'fred' }
12613 * var shallow = _.clone(users);
12614 * shallow[0] === users[0];
12617 * var deep = _.clone(users, true);
12618 * deep[0] === users[0];
12621 * // using a customizer callback
12622 * var el = _.clone(document.body, function(value) {
12623 * if (_.isElement(value)) {
12624 * return value.cloneNode(false);
12628 * el === document.body
12632 * el.childNodes.length;
12635 function clone(value, isDeep, customizer, thisArg) {
12636 if (isDeep && typeof isDeep != 'boolean' && isIterateeCall(value, isDeep, customizer)) {
12639 else if (typeof isDeep == 'function') {
12640 thisArg = customizer;
12641 customizer = isDeep;
12644 return typeof customizer == 'function'
12645 ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 1))
12646 : baseClone(value, isDeep);
12650 * Creates a deep clone of `value`. If `customizer` is provided it is invoked
12651 * to produce the cloned values. If `customizer` returns `undefined` cloning
12652 * is handled by the method instead. The `customizer` is bound to `thisArg`
12653 * and invoked with two argument; (value [, index|key, object]).
12655 * **Note:** This method is loosely based on the
12656 * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
12657 * The enumerable properties of `arguments` objects and objects created by
12658 * constructors other than `Object` are cloned to plain `Object` objects. An
12659 * empty object is returned for uncloneable values such as functions, DOM nodes,
12660 * Maps, Sets, and WeakMaps.
12665 * @param {*} value The value to deep clone.
12666 * @param {Function} [customizer] The function to customize cloning values.
12667 * @param {*} [thisArg] The `this` binding of `customizer`.
12668 * @returns {*} Returns the deep cloned value.
12672 * { 'user': 'barney' },
12673 * { 'user': 'fred' }
12676 * var deep = _.cloneDeep(users);
12677 * deep[0] === users[0];
12680 * // using a customizer callback
12681 * var el = _.cloneDeep(document.body, function(value) {
12682 * if (_.isElement(value)) {
12683 * return value.cloneNode(true);
12687 * el === document.body
12691 * el.childNodes.length;
12694 function cloneDeep(value, customizer, thisArg) {
12695 return typeof customizer == 'function'
12696 ? baseClone(value, true, bindCallback(customizer, thisArg, 1))
12697 : baseClone(value, true);
12701 * Checks if `value` is classified as an `arguments` object.
12706 * @param {*} value The value to check.
12707 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12710 * _.isArguments(function() { return arguments; }());
12713 * _.isArguments([1, 2, 3]);
12716 function isArguments(value) {
12717 return isObjectLike(value) && isArrayLike(value) && objToString.call(value) == argsTag;
12719 // Fallback for environments without a `toStringTag` for `arguments` objects.
12720 if (!support.argsTag) {
12721 isArguments = function(value) {
12722 return isObjectLike(value) && isArrayLike(value) &&
12723 hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');
12728 * Checks if `value` is classified as an `Array` object.
12733 * @param {*} value The value to check.
12734 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12737 * _.isArray([1, 2, 3]);
12740 * _.isArray(function() { return arguments; }());
12743 var isArray = nativeIsArray || function(value) {
12744 return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag;
12748 * Checks if `value` is empty. A value is considered empty unless it is an
12749 * `arguments` object, array, string, or jQuery-like collection with a length
12750 * greater than `0` or an object with own enumerable properties.
12755 * @param {Array|Object|string} value The value to inspect.
12756 * @returns {boolean} Returns `true` if `value` is empty, else `false`.
12768 * _.isEmpty([1, 2, 3]);
12771 * _.isEmpty({ 'a': 1 });
12774 function isEmpty(value) {
12775 if (value == null) {
12778 if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) ||
12779 (isObjectLike(value) && isFunction(value.splice)))) {
12780 return !value.length;
12782 return !keys(value).length;
12786 * Performs a deep comparison between two values to determine if they are
12787 * equivalent. If `customizer` is provided it is invoked to compare values.
12788 * If `customizer` returns `undefined` comparisons are handled by the method
12789 * instead. The `customizer` is bound to `thisArg` and invoked with three
12790 * arguments: (value, other [, index|key]).
12792 * **Note:** This method supports comparing arrays, booleans, `Date` objects,
12793 * numbers, `Object` objects, regexes, and strings. Objects are compared by
12794 * their own, not inherited, enumerable properties. Functions and DOM nodes
12795 * are **not** supported. Provide a customizer function to extend support
12796 * for comparing other values.
12802 * @param {*} value The value to compare.
12803 * @param {*} other The other value to compare.
12804 * @param {Function} [customizer] The function to customize value comparisons.
12805 * @param {*} [thisArg] The `this` binding of `customizer`.
12806 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
12809 * var object = { 'user': 'fred' };
12810 * var other = { 'user': 'fred' };
12815 * _.isEqual(object, other);
12818 * // using a customizer callback
12819 * var array = ['hello', 'goodbye'];
12820 * var other = ['hi', 'goodbye'];
12822 * _.isEqual(array, other, function(value, other) {
12823 * if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) {
12829 function isEqual(value, other, customizer, thisArg) {
12830 customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined;
12831 var result = customizer ? customizer(value, other) : undefined;
12832 return result === undefined ? baseIsEqual(value, other, customizer) : !!result;
12836 * Checks if `value` is classified as a `Function` object.
12841 * @param {*} value The value to check.
12842 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12848 * _.isFunction(/abc/);
12851 var isFunction = !(baseIsFunction(/x/) || (Uint8Array && !baseIsFunction(Uint8Array))) ? baseIsFunction : function(value) {
12852 // The use of `Object#toString` avoids issues with the `typeof` operator
12853 // in older versions of Chrome and Safari which return 'function' for regexes
12854 // and Safari 8 equivalents which return 'object' for typed array constructors.
12855 return objToString.call(value) == funcTag;
12859 * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
12860 * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
12865 * @param {*} value The value to check.
12866 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
12872 * _.isObject([1, 2, 3]);
12878 function isObject(value) {
12879 // Avoid a V8 JIT bug in Chrome 19-20.
12880 // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
12881 var type = typeof value;
12882 return !!value && (type == 'object' || type == 'function');
12886 * Checks if `value` is a native function.
12891 * @param {*} value The value to check.
12892 * @returns {boolean} Returns `true` if `value` is a native function, else `false`.
12895 * _.isNative(Array.prototype.push);
12901 function isNative(value) {
12902 if (value == null) {
12905 if (objToString.call(value) == funcTag) {
12906 return reIsNative.test(fnToString.call(value));
12908 return isObjectLike(value) && (isHostObject(value) ? reIsNative : reIsHostCtor).test(value);
12912 * Checks if `value` is a plain object, that is, an object created by the
12913 * `Object` constructor or one with a `[[Prototype]]` of `null`.
12915 * **Note:** This method assumes objects created by the `Object` constructor
12916 * have no inherited enumerable properties.
12921 * @param {*} value The value to check.
12922 * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
12929 * _.isPlainObject(new Foo);
12932 * _.isPlainObject([1, 2, 3]);
12935 * _.isPlainObject({ 'x': 0, 'y': 0 });
12938 * _.isPlainObject(Object.create(null));
12941 var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
12942 if (!(value && objToString.call(value) == objectTag) || (!lodash.support.argsTag && isArguments(value))) {
12945 var valueOf = getNative(value, 'valueOf'),
12946 objProto = valueOf && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
12949 ? (value == objProto || getPrototypeOf(value) == objProto)
12950 : shimIsPlainObject(value);
12954 * Checks if `value` is classified as a `String` primitive or object.
12959 * @param {*} value The value to check.
12960 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12963 * _.isString('abc');
12969 function isString(value) {
12970 return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag);
12974 * Checks if `value` is classified as a typed array.
12979 * @param {*} value The value to check.
12980 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12983 * _.isTypedArray(new Uint8Array);
12986 * _.isTypedArray([]);
12989 function isTypedArray(value) {
12990 return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
12994 * Converts `value` to a plain object flattening inherited enumerable
12995 * properties of `value` to own properties of the plain object.
13000 * @param {*} value The value to convert.
13001 * @returns {Object} Returns the converted plain object.
13008 * Foo.prototype.c = 3;
13010 * _.assign({ 'a': 1 }, new Foo);
13011 * // => { 'a': 1, 'b': 2 }
13013 * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
13014 * // => { 'a': 1, 'b': 2, 'c': 3 }
13016 function toPlainObject(value) {
13017 return baseCopy(value, keysIn(value));
13020 /*------------------------------------------------------------------------*/
13023 * Assigns own enumerable properties of source object(s) to the destination
13024 * object. Subsequent sources overwrite property assignments of previous sources.
13025 * If `customizer` is provided it is invoked to produce the assigned values.
13026 * The `customizer` is bound to `thisArg` and invoked with five arguments:
13027 * (objectValue, sourceValue, key, object, source).
13029 * **Note:** This method mutates `object` and is based on
13030 * [`Object.assign`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign).
13036 * @param {Object} object The destination object.
13037 * @param {...Object} [sources] The source objects.
13038 * @param {Function} [customizer] The function to customize assigned values.
13039 * @param {*} [thisArg] The `this` binding of `customizer`.
13040 * @returns {Object} Returns `object`.
13043 * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' });
13044 * // => { 'user': 'fred', 'age': 40 }
13046 * // using a customizer callback
13047 * var defaults = _.partialRight(_.assign, function(value, other) {
13048 * return _.isUndefined(value) ? other : value;
13051 * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
13052 * // => { 'user': 'barney', 'age': 36 }
13054 var assign = createAssigner(function(object, source, customizer) {
13056 ? assignWith(object, source, customizer)
13057 : baseAssign(object, source);
13061 * Iterates over own enumerable properties of an object invoking `iteratee`
13062 * for each property. The `iteratee` is bound to `thisArg` and invoked with
13063 * three arguments: (value, key, object). Iteratee functions may exit iteration
13064 * early by explicitly returning `false`.
13069 * @param {Object} object The object to iterate over.
13070 * @param {Function} [iteratee=_.identity] The function invoked per iteration.
13071 * @param {*} [thisArg] The `this` binding of `iteratee`.
13072 * @returns {Object} Returns `object`.
13080 * Foo.prototype.c = 3;
13082 * _.forOwn(new Foo, function(value, key) {
13083 * console.log(key);
13085 * // => logs 'a' and 'b' (iteration order is not guaranteed)
13087 var forOwn = createForOwn(baseForOwn);
13090 * Creates an array of the own enumerable property names of `object`.
13092 * **Note:** Non-object values are coerced to objects. See the
13093 * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys)
13094 * for more details.
13099 * @param {Object} object The object to query.
13100 * @returns {Array} Returns the array of property names.
13108 * Foo.prototype.c = 3;
13111 * // => ['a', 'b'] (iteration order is not guaranteed)
13116 var keys = !nativeKeys ? shimKeys : function(object) {
13117 var Ctor = object == null ? null : object.constructor;
13118 if ((typeof Ctor == 'function' && Ctor.prototype === object) ||
13119 (typeof object == 'function' ? lodash.support.enumPrototypes : isArrayLike(object))) {
13120 return shimKeys(object);
13122 return isObject(object) ? nativeKeys(object) : [];
13126 * Creates an array of the own and inherited enumerable property names of `object`.
13128 * **Note:** Non-object values are coerced to objects.
13133 * @param {Object} object The object to query.
13134 * @returns {Array} Returns the array of property names.
13142 * Foo.prototype.c = 3;
13144 * _.keysIn(new Foo);
13145 * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
13147 function keysIn(object) {
13148 if (object == null) {
13151 if (!isObject(object)) {
13152 object = Object(object);
13154 var length = object.length,
13155 support = lodash.support;
13157 length = (length && isLength(length) &&
13158 (isArray(object) || isArguments(object) || isString(object)) && length) || 0;
13160 var Ctor = object.constructor,
13162 proto = (isFunction(Ctor) && Ctor.prototype) || objectProto,
13163 isProto = proto === object,
13164 result = Array(length),
13165 skipIndexes = length > 0,
13166 skipErrorProps = support.enumErrorProps && (object === errorProto || object instanceof Error),
13167 skipProto = support.enumPrototypes && isFunction(object);
13169 while (++index < length) {
13170 result[index] = (index + '');
13172 // lodash skips the `constructor` property when it infers it is iterating
13173 // over a `prototype` object because IE < 9 can't set the `[[Enumerable]]`
13174 // attribute of an existing property and the `constructor` property of a
13175 // prototype defaults to non-enumerable.
13176 for (var key in object) {
13177 if (!(skipProto && key == 'prototype') &&
13178 !(skipErrorProps && (key == 'message' || key == 'name')) &&
13179 !(skipIndexes && isIndex(key, length)) &&
13180 !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
13184 if (support.nonEnumShadows && object !== objectProto) {
13185 var tag = object === stringProto ? stringTag : (object === errorProto ? errorTag : objToString.call(object)),
13186 nonEnums = nonEnumProps[tag] || nonEnumProps[objectTag];
13188 if (tag == objectTag) {
13189 proto = objectProto;
13191 length = shadowProps.length;
13193 key = shadowProps[length];
13194 var nonEnum = nonEnums[key];
13195 if (!(isProto && nonEnum) &&
13196 (nonEnum ? hasOwnProperty.call(object, key) : object[key] !== proto[key])) {
13205 * Recursively merges own enumerable properties of the source object(s), that
13206 * don't resolve to `undefined` into the destination object. Subsequent sources
13207 * overwrite property assignments of previous sources. If `customizer` is
13208 * provided it is invoked to produce the merged values of the destination and
13209 * source properties. If `customizer` returns `undefined` merging is handled
13210 * by the method instead. The `customizer` is bound to `thisArg` and invoked
13211 * with five arguments: (objectValue, sourceValue, key, object, source).
13216 * @param {Object} object The destination object.
13217 * @param {...Object} [sources] The source objects.
13218 * @param {Function} [customizer] The function to customize assigned values.
13219 * @param {*} [thisArg] The `this` binding of `customizer`.
13220 * @returns {Object} Returns `object`.
13224 * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
13228 * 'data': [{ 'age': 36 }, { 'age': 40 }]
13231 * _.merge(users, ages);
13232 * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
13234 * // using a customizer callback
13236 * 'fruits': ['apple'],
13237 * 'vegetables': ['beet']
13241 * 'fruits': ['banana'],
13242 * 'vegetables': ['carrot']
13245 * _.merge(object, other, function(a, b) {
13246 * if (_.isArray(a)) {
13247 * return a.concat(b);
13250 * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
13252 var merge = createAssigner(baseMerge);
13255 * The opposite of `_.pick`; this method creates an object composed of the
13256 * own and inherited enumerable properties of `object` that are not omitted.
13261 * @param {Object} object The source object.
13262 * @param {Function|...(string|string[])} [predicate] The function invoked per
13263 * iteration or property names to omit, specified as individual property
13264 * names or arrays of property names.
13265 * @param {*} [thisArg] The `this` binding of `predicate`.
13266 * @returns {Object} Returns the new object.
13269 * var object = { 'user': 'fred', 'age': 40 };
13271 * _.omit(object, 'age');
13272 * // => { 'user': 'fred' }
13274 * _.omit(object, _.isNumber);
13275 * // => { 'user': 'fred' }
13277 var omit = restParam(function(object, props) {
13278 if (object == null) {
13281 if (typeof props[0] != 'function') {
13282 var props = arrayMap(baseFlatten(props), String);
13283 return pickByArray(object, baseDifference(keysIn(object), props));
13285 var predicate = bindCallback(props[0], props[1], 3);
13286 return pickByCallback(object, function(value, key, object) {
13287 return !predicate(value, key, object);
13292 * Creates a two dimensional array of the key-value pairs for `object`,
13293 * e.g. `[[key1, value1], [key2, value2]]`.
13298 * @param {Object} object The object to query.
13299 * @returns {Array} Returns the new array of key-value pairs.
13302 * _.pairs({ 'barney': 36, 'fred': 40 });
13303 * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed)
13305 function pairs(object) {
13306 object = toObject(object);
13309 props = keys(object),
13310 length = props.length,
13311 result = Array(length);
13313 while (++index < length) {
13314 var key = props[index];
13315 result[index] = [key, object[key]];
13321 * Creates an object composed of the picked `object` properties. Property
13322 * names may be specified as individual arguments or as arrays of property
13323 * names. If `predicate` is provided it is invoked for each property of `object`
13324 * picking the properties `predicate` returns truthy for. The predicate is
13325 * bound to `thisArg` and invoked with three arguments: (value, key, object).
13330 * @param {Object} object The source object.
13331 * @param {Function|...(string|string[])} [predicate] The function invoked per
13332 * iteration or property names to pick, specified as individual property
13333 * names or arrays of property names.
13334 * @param {*} [thisArg] The `this` binding of `predicate`.
13335 * @returns {Object} Returns the new object.
13338 * var object = { 'user': 'fred', 'age': 40 };
13340 * _.pick(object, 'user');
13341 * // => { 'user': 'fred' }
13343 * _.pick(object, _.isString);
13344 * // => { 'user': 'fred' }
13346 var pick = restParam(function(object, props) {
13347 if (object == null) {
13350 return typeof props[0] == 'function'
13351 ? pickByCallback(object, bindCallback(props[0], props[1], 3))
13352 : pickByArray(object, baseFlatten(props));
13356 * Creates an array of the own enumerable property values of `object`.
13358 * **Note:** Non-object values are coerced to objects.
13363 * @param {Object} object The object to query.
13364 * @returns {Array} Returns the array of property values.
13372 * Foo.prototype.c = 3;
13374 * _.values(new Foo);
13375 * // => [1, 2] (iteration order is not guaranteed)
13380 function values(object) {
13381 return baseValues(object, keys(object));
13384 /*------------------------------------------------------------------------*/
13387 * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?",
13388 * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`.
13393 * @param {string} [string=''] The string to escape.
13394 * @returns {string} Returns the escaped string.
13397 * _.escapeRegExp('[lodash](https://lodash.com/)');
13398 * // => '\[lodash\]\(https:\/\/lodash\.com\/\)'
13400 function escapeRegExp(string) {
13401 string = baseToString(string);
13402 return (string && reHasRegExpChars.test(string))
13403 ? string.replace(reRegExpChars, '\\$&')
13407 /*------------------------------------------------------------------------*/
13410 * Creates a function that invokes `func` with the `this` binding of `thisArg`
13411 * and arguments of the created function. If `func` is a property name the
13412 * created callback returns the property value for a given element. If `func`
13413 * is an object the created callback returns `true` for elements that contain
13414 * the equivalent object properties, otherwise it returns `false`.
13419 * @category Utility
13420 * @param {*} [func=_.identity] The value to convert to a callback.
13421 * @param {*} [thisArg] The `this` binding of `func`.
13422 * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
13423 * @returns {Function} Returns the callback.
13427 * { 'user': 'barney', 'age': 36 },
13428 * { 'user': 'fred', 'age': 40 }
13431 * // wrap to create custom callback shorthands
13432 * _.callback = _.wrap(_.callback, function(callback, func, thisArg) {
13433 * var match = /^(.+?)__([gl]t)(.+)$/.exec(func);
13435 * return callback(func, thisArg);
13437 * return function(object) {
13438 * return match[2] == 'gt'
13439 * ? object[match[1]] > match[3]
13440 * : object[match[1]] < match[3];
13444 * _.filter(users, 'age__gt36');
13445 * // => [{ 'user': 'fred', 'age': 40 }]
13447 function callback(func, thisArg, guard) {
13448 if (guard && isIterateeCall(func, thisArg, guard)) {
13451 return isObjectLike(func)
13453 : baseCallback(func, thisArg);
13457 * Creates a function that returns `value`.
13461 * @category Utility
13462 * @param {*} value The value to return from the new function.
13463 * @returns {Function} Returns the new function.
13466 * var object = { 'user': 'fred' };
13467 * var getter = _.constant(object);
13469 * getter() === object;
13472 function constant(value) {
13473 return function() {
13479 * This method returns the first argument provided to it.
13483 * @category Utility
13484 * @param {*} value Any value.
13485 * @returns {*} Returns `value`.
13488 * var object = { 'user': 'fred' };
13490 * _.identity(object) === object;
13493 function identity(value) {
13498 * Creates a function that performs a deep comparison between a given object
13499 * and `source`, returning `true` if the given object has equivalent property
13500 * values, else `false`.
13502 * **Note:** This method supports comparing arrays, booleans, `Date` objects,
13503 * numbers, `Object` objects, regexes, and strings. Objects are compared by
13504 * their own, not inherited, enumerable properties. For comparing a single
13505 * own or inherited property value see `_.matchesProperty`.
13509 * @category Utility
13510 * @param {Object} source The object of property values to match.
13511 * @returns {Function} Returns the new function.
13515 * { 'user': 'barney', 'age': 36, 'active': true },
13516 * { 'user': 'fred', 'age': 40, 'active': false }
13519 * _.filter(users, _.matches({ 'age': 40, 'active': false }));
13520 * // => [{ 'user': 'fred', 'age': 40, 'active': false }]
13522 function matches(source) {
13523 return baseMatches(baseClone(source, true));
13527 * Adds all own enumerable function properties of a source object to the
13528 * destination object. If `object` is a function then methods are added to
13529 * its prototype as well.
13531 * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
13532 * avoid conflicts caused by modifying the original.
13536 * @category Utility
13537 * @param {Function|Object} [object=lodash] The destination object.
13538 * @param {Object} source The object of functions to add.
13539 * @param {Object} [options] The options object.
13540 * @param {boolean} [options.chain=true] Specify whether the functions added
13542 * @returns {Function|Object} Returns `object`.
13545 * function vowels(string) {
13546 * return _.filter(string, function(v) {
13547 * return /[aeiou]/i.test(v);
13551 * _.mixin({ 'vowels': vowels });
13552 * _.vowels('fred');
13555 * _('fred').vowels().value();
13558 * _.mixin({ 'vowels': vowels }, { 'chain': false });
13559 * _('fred').vowels();
13562 function mixin(object, source, options) {
13563 if (options == null) {
13564 var isObj = isObject(source),
13565 props = isObj ? keys(source) : null,
13566 methodNames = (props && props.length) ? baseFunctions(source, props) : null;
13568 if (!(methodNames ? methodNames.length : isObj)) {
13569 methodNames = false;
13575 if (!methodNames) {
13576 methodNames = baseFunctions(source, keys(source));
13580 isFunc = isFunction(object),
13581 length = methodNames.length;
13583 if (options === false) {
13585 } else if (isObject(options) && 'chain' in options) {
13586 chain = options.chain;
13588 while (++index < length) {
13589 var methodName = methodNames[index],
13590 func = source[methodName];
13592 object[methodName] = func;
13594 object.prototype[methodName] = (function(func) {
13595 return function() {
13596 var chainAll = this.__chain__;
13597 if (chain || chainAll) {
13598 var result = object(this.__wrapped__),
13599 actions = result.__actions__ = arrayCopy(this.__actions__);
13601 actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
13602 result.__chain__ = chainAll;
13605 var args = [this.value()];
13606 push.apply(args, arguments);
13607 return func.apply(object, args);
13616 * A no-operation function that returns `undefined` regardless of the
13617 * arguments it receives.
13621 * @category Utility
13624 * var object = { 'user': 'fred' };
13626 * _.noop(object) === undefined;
13630 // No operation performed.
13634 * Creates a function that returns the property value at `path` on a
13639 * @category Utility
13640 * @param {Array|string} path The path of the property to get.
13641 * @returns {Function} Returns the new function.
13645 * { 'a': { 'b': { 'c': 2 } } },
13646 * { 'a': { 'b': { 'c': 1 } } }
13649 * _.map(objects, _.property('a.b.c'));
13652 * _.pluck(_.sortBy(objects, _.property(['a', 'b', 'c'])), 'a.b.c');
13655 function property(path) {
13656 return isKey(path) ? baseProperty(path) : basePropertyDeep(path);
13659 /*------------------------------------------------------------------------*/
13661 // Ensure wrappers are instances of `baseLodash`.
13662 lodash.prototype = baseLodash.prototype;
13664 LodashWrapper.prototype = baseCreate(baseLodash.prototype);
13665 LodashWrapper.prototype.constructor = LodashWrapper;
13667 LazyWrapper.prototype = baseCreate(baseLodash.prototype);
13668 LazyWrapper.prototype.constructor = LazyWrapper;
13670 // Add functions to the `Set` cache.
13671 SetCache.prototype.push = cachePush;
13673 // Add functions that return wrapped values when chaining.
13674 lodash.assign = assign;
13675 lodash.bind = bind;
13676 lodash.callback = callback;
13677 lodash.chain = chain;
13678 lodash.chunk = chunk;
13679 lodash.compact = compact;
13680 lodash.constant = constant;
13681 lodash.debounce = debounce;
13682 lodash.difference = difference;
13683 lodash.filter = filter;
13684 lodash.flatten = flatten;
13685 lodash.forEach = forEach;
13686 lodash.forOwn = forOwn;
13687 lodash.groupBy = groupBy;
13688 lodash.intersection = intersection;
13689 lodash.keys = keys;
13690 lodash.keysIn = keysIn;
13692 lodash.matches = matches;
13693 lodash.merge = merge;
13694 lodash.mixin = mixin;
13695 lodash.omit = omit;
13696 lodash.pairs = pairs;
13697 lodash.pick = pick;
13698 lodash.pluck = pluck;
13699 lodash.property = property;
13700 lodash.reject = reject;
13701 lodash.restParam = restParam;
13703 lodash.throttle = throttle;
13704 lodash.thru = thru;
13705 lodash.toPlainObject = toPlainObject;
13706 lodash.union = union;
13707 lodash.uniq = uniq;
13708 lodash.values = values;
13709 lodash.without = without;
13712 lodash.collect = map;
13713 lodash.each = forEach;
13714 lodash.extend = assign;
13715 lodash.iteratee = callback;
13716 lodash.select = filter;
13717 lodash.unique = uniq;
13719 // Add functions to `lodash.prototype`.
13720 mixin(lodash, lodash);
13722 /*------------------------------------------------------------------------*/
13724 // Add functions that return unwrapped values when chaining.
13725 lodash.clone = clone;
13726 lodash.cloneDeep = cloneDeep;
13727 lodash.escapeRegExp = escapeRegExp;
13728 lodash.every = every;
13729 lodash.find = find;
13730 lodash.first = first;
13731 lodash.identity = identity;
13732 lodash.includes = includes;
13733 lodash.indexOf = indexOf;
13734 lodash.isArguments = isArguments;
13735 lodash.isArray = isArray;
13736 lodash.isEmpty = isEmpty;
13737 lodash.isEqual = isEqual;
13738 lodash.isFunction = isFunction;
13739 lodash.isNative = isNative;
13740 lodash.isObject = isObject;
13741 lodash.isPlainObject = isPlainObject;
13742 lodash.isString = isString;
13743 lodash.isTypedArray = isTypedArray;
13744 lodash.last = last;
13745 lodash.noop = noop;
13747 lodash.reduce = reduce;
13748 lodash.some = some;
13751 lodash.all = every;
13753 lodash.contains = includes;
13754 lodash.eq = isEqual;
13755 lodash.detect = find;
13756 lodash.foldl = reduce;
13757 lodash.head = first;
13758 lodash.include = includes;
13759 lodash.inject = reduce;
13761 mixin(lodash, (function() {
13763 baseForOwn(lodash, function(func, methodName) {
13764 if (!lodash.prototype[methodName]) {
13765 source[methodName] = func;
13771 /*------------------------------------------------------------------------*/
13773 lodash.prototype.sample = function(n) {
13774 if (!this.__chain__ && n == null) {
13775 return sample(this.value());
13777 return this.thru(function(value) {
13778 return sample(value, n);
13782 /*------------------------------------------------------------------------*/
13785 * The semantic version number.
13791 lodash.VERSION = VERSION;
13793 // Assign default placeholders.
13794 bind.placeholder = lodash;
13796 // Add `LazyWrapper` methods that accept an `iteratee` value.
13797 arrayEach(['dropWhile', 'filter', 'map', 'takeWhile'], function(methodName, type) {
13798 var isFilter = type != LAZY_MAP_FLAG,
13799 isDropWhile = type == LAZY_DROP_WHILE_FLAG;
13801 LazyWrapper.prototype[methodName] = function(iteratee, thisArg) {
13802 var filtered = this.__filtered__,
13803 result = (filtered && isDropWhile) ? new LazyWrapper(this) : this.clone(),
13804 iteratees = result.__iteratees__ || (result.__iteratees__ = []);
13810 'iteratee': getCallback(iteratee, thisArg, 1),
13815 result.__filtered__ = filtered || isFilter;
13820 // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
13821 arrayEach(['drop', 'take'], function(methodName, index) {
13822 var whileName = methodName + 'While';
13824 LazyWrapper.prototype[methodName] = function(n) {
13825 var filtered = this.__filtered__,
13826 result = (filtered && !index) ? this.dropWhile() : this.clone();
13828 n = n == null ? 1 : nativeMax(floor(n) || 0, 0);
13831 result.__takeCount__ = nativeMin(result.__takeCount__, n);
13833 last(result.__iteratees__).limit = n;
13836 var views = result.__views__ || (result.__views__ = []);
13837 views.push({ 'size': n, 'type': methodName + (result.__dir__ < 0 ? 'Right' : '') });
13842 LazyWrapper.prototype[methodName + 'Right'] = function(n) {
13843 return this.reverse()[methodName](n).reverse();
13846 LazyWrapper.prototype[methodName + 'RightWhile'] = function(predicate, thisArg) {
13847 return this.reverse()[whileName](predicate, thisArg).reverse();
13851 // Add `LazyWrapper` methods for `_.first` and `_.last`.
13852 arrayEach(['first', 'last'], function(methodName, index) {
13853 var takeName = 'take' + (index ? 'Right' : '');
13855 LazyWrapper.prototype[methodName] = function() {
13856 return this[takeName](1).value()[0];
13860 // Add `LazyWrapper` methods for `_.initial` and `_.rest`.
13861 arrayEach(['initial', 'rest'], function(methodName, index) {
13862 var dropName = 'drop' + (index ? '' : 'Right');
13864 LazyWrapper.prototype[methodName] = function() {
13865 return this[dropName](1);
13869 // Add `LazyWrapper` methods for `_.pluck` and `_.where`.
13870 arrayEach(['pluck', 'where'], function(methodName, index) {
13871 var operationName = index ? 'filter' : 'map',
13872 createCallback = index ? baseMatches : property;
13874 LazyWrapper.prototype[methodName] = function(value) {
13875 return this[operationName](createCallback(value));
13879 LazyWrapper.prototype.compact = function() {
13880 return this.filter(identity);
13883 LazyWrapper.prototype.reject = function(predicate, thisArg) {
13884 predicate = getCallback(predicate, thisArg, 1);
13885 return this.filter(function(value) {
13886 return !predicate(value);
13890 LazyWrapper.prototype.slice = function(start, end) {
13891 start = start == null ? 0 : (+start || 0);
13895 result = this.takeRight(-start);
13896 } else if (start) {
13897 result = this.drop(start);
13899 if (end !== undefined) {
13901 result = end < 0 ? result.dropRight(-end) : result.take(end - start);
13906 LazyWrapper.prototype.toArray = function() {
13907 return this.drop(0);
13910 // Add `LazyWrapper` methods to `lodash.prototype`.
13911 baseForOwn(LazyWrapper.prototype, function(func, methodName) {
13912 var lodashFunc = lodash[methodName];
13916 var checkIteratee = /^(?:filter|map|reject)|While$/.test(methodName),
13917 retUnwrapped = /^(?:first|last)$/.test(methodName);
13919 lodash.prototype[methodName] = function() {
13920 var args = arguments,
13921 chainAll = this.__chain__,
13922 value = this.__wrapped__,
13923 isHybrid = !!this.__actions__.length,
13924 isLazy = value instanceof LazyWrapper,
13925 iteratee = args[0],
13926 useLazy = isLazy || isArray(value);
13928 if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
13929 // avoid lazy use if the iteratee has a "length" value other than `1`
13930 isLazy = useLazy = false;
13932 var onlyLazy = isLazy && !isHybrid;
13933 if (retUnwrapped && !chainAll) {
13936 : lodashFunc.call(lodash, this.value());
13938 var interceptor = function(value) {
13939 var otherArgs = [value];
13940 push.apply(otherArgs, args);
13941 return lodashFunc.apply(lodash, otherArgs);
13944 var wrapper = onlyLazy ? value : new LazyWrapper(this),
13945 result = func.apply(wrapper, args);
13947 if (!retUnwrapped && (isHybrid || result.__actions__)) {
13948 var actions = result.__actions__ || (result.__actions__ = []);
13949 actions.push({ 'func': thru, 'args': [interceptor], 'thisArg': lodash });
13951 return new LodashWrapper(result, chainAll);
13953 return this.thru(interceptor);
13957 // Add `Array` and `String` methods to `lodash.prototype`.
13958 arrayEach(['concat', 'join', 'pop', 'push', 'replace', 'shift', 'sort', 'splice', 'split', 'unshift'], function(methodName) {
13959 var protoFunc = (/^(?:replace|split)$/.test(methodName) ? stringProto : arrayProto)[methodName],
13960 chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
13961 fixObjects = !support.spliceObjects && /^(?:pop|shift|splice)$/.test(methodName),
13962 retUnwrapped = /^(?:join|pop|replace|shift)$/.test(methodName);
13964 // Avoid array-like object bugs with `Array#shift` and `Array#splice` in
13965 // IE < 9, Firefox < 10, and RingoJS.
13966 var func = !fixObjects ? protoFunc : function() {
13967 var result = protoFunc.apply(this, arguments);
13968 if (this.length === 0) {
13974 lodash.prototype[methodName] = function() {
13975 var args = arguments;
13976 if (retUnwrapped && !this.__chain__) {
13977 return func.apply(this.value(), args);
13979 return this[chainName](function(value) {
13980 return func.apply(value, args);
13985 // Map minified function names to their real names.
13986 baseForOwn(LazyWrapper.prototype, function(func, methodName) {
13987 var lodashFunc = lodash[methodName];
13989 var key = lodashFunc.name,
13990 names = realNames[key] || (realNames[key] = []);
13992 names.push({ 'name': methodName, 'func': lodashFunc });
13996 realNames[createHybridWrapper(null, BIND_KEY_FLAG).name] = [{ 'name': 'wrapper', 'func': null }];
13998 // Add functions to the lazy wrapper.
13999 LazyWrapper.prototype.clone = lazyClone;
14000 LazyWrapper.prototype.reverse = lazyReverse;
14001 LazyWrapper.prototype.value = lazyValue;
14003 // Add chaining functions to the `lodash` wrapper.
14004 lodash.prototype.chain = wrapperChain;
14005 lodash.prototype.commit = wrapperCommit;
14006 lodash.prototype.plant = wrapperPlant;
14007 lodash.prototype.reverse = wrapperReverse;
14008 lodash.prototype.toString = wrapperToString;
14009 lodash.prototype.run = lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
14011 // Add function aliases to the `lodash` wrapper.
14012 lodash.prototype.collect = lodash.prototype.map;
14013 lodash.prototype.head = lodash.prototype.first;
14014 lodash.prototype.select = lodash.prototype.filter;
14015 lodash.prototype.tail = lodash.prototype.rest;
14017 /*--------------------------------------------------------------------------*/
14019 if (freeExports && freeModule) {
14020 // Export for Node.js or RingoJS.
14021 if (moduleExports) {
14022 (freeModule.exports = lodash)._ = lodash;
14026 // Export for a browser or Rhino.
14030 (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;
14031 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){
14034 var ohauth = require('ohauth'),
14035 xtend = require('xtend'),
14036 store = require('store');
14040 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
14041 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
14042 // does not support custom headers, which this uses everywhere.
14043 module.exports = function(o) {
14047 // authenticated users will also have a request token secret, but it's
14048 // not used in transactions with the server
14049 oauth.authenticated = function() {
14050 return !!(token('oauth_token') && token('oauth_token_secret'));
14053 oauth.logout = function() {
14054 token('oauth_token', '');
14055 token('oauth_token_secret', '');
14056 token('oauth_request_token_secret', '');
14060 // TODO: detect lack of click event
14061 oauth.authenticate = function(callback) {
14062 if (oauth.authenticated()) return callback();
14066 // ## Getting a request token
14067 var params = timenonce(getAuth(o)),
14068 url = o.url + '/oauth/request_token',
14071 params.oauth_signature = ohauth.signature(
14072 o.oauth_secret, '',
14073 ohauth.baseString('POST', url, params));
14075 if (!o.singlepage) {
14076 // Create a 600x550 popup window in the center of the screen
14077 var w = 600, h = 550,
14079 ['width', w], ['height', h],
14080 ['left', screen.width / 2 - w / 2],
14081 ['top', screen.height / 2 - h / 2]].map(function(x) {
14082 return x.join('=');
14084 popup = window.open('about:blank', 'oauth_window', settings);
14087 timer = setInterval(function() {
14088 if (popup.closed) {
14090 clearInterval(timer);
14091 callback('not authenticated', null);
14096 // Request a request token. When this is complete, the popup
14097 // window is redirected to OSM's authorization page.
14098 ohauth.xhr('POST', url, params, null, {}, reqTokenDone);
14101 function reqTokenDone(err, xhr) {
14104 if (timer) clearInterval(timer);
14105 return callback(err);
14107 var resp = ohauth.stringQs(xhr.response);
14108 token('oauth_request_token_secret', resp.oauth_token_secret);
14109 var authorize_url = o.url + '/oauth/authorize?' + ohauth.qsString({
14110 oauth_token: resp.oauth_token,
14111 oauth_callback: location.href.replace('index.html', '')
14112 .replace(/#.*/, '') + o.landing
14115 if (o.singlepage) {
14116 location.href = authorize_url;
14118 popup.location = authorize_url;
14122 // Called by a function in a landing page, in the popup window. The
14123 // window closes itself.
14124 window.authComplete = function(token) {
14125 if (timer) clearInterval(timer);
14126 var oauth_token = ohauth.stringQs(token.split('?')[1]);
14127 get_access_token(oauth_token.oauth_token);
14128 delete window.authComplete;
14131 // ## Getting an request token
14133 // At this point we have an `oauth_token`, brought in from a function
14134 // call on a landing page popup.
14135 function get_access_token(oauth_token) {
14136 var url = o.url + '/oauth/access_token',
14137 params = timenonce(getAuth(o)),
14138 request_token_secret = token('oauth_request_token_secret');
14139 params.oauth_token = oauth_token;
14140 params.oauth_signature = ohauth.signature(
14142 request_token_secret,
14143 ohauth.baseString('POST', url, params));
14145 // ## Getting an access token
14147 // The final token required for authentication. At this point
14148 // we have a `request token secret`
14149 ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
14153 function accessTokenDone(err, xhr) {
14155 if (timer) clearInterval(timer);
14156 if (err) return callback(err);
14157 var access_token = ohauth.stringQs(xhr.response);
14158 token('oauth_token', access_token.oauth_token);
14159 token('oauth_token_secret', access_token.oauth_token_secret);
14160 callback(null, oauth);
14164 oauth.bootstrapToken = function(oauth_token, callback) {
14165 // ## Getting an request token
14166 // At this point we have an `oauth_token`, brought in from a function
14167 // call on a landing page popup.
14168 function get_access_token(oauth_token) {
14169 var url = o.url + '/oauth/access_token',
14170 params = timenonce(getAuth(o)),
14171 request_token_secret = token('oauth_request_token_secret');
14172 params.oauth_token = oauth_token;
14173 params.oauth_signature = ohauth.signature(
14175 request_token_secret,
14176 ohauth.baseString('POST', url, params));
14178 // ## Getting an access token
14179 // The final token required for authentication. At this point
14180 // we have a `request token secret`
14181 ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
14185 function accessTokenDone(err, xhr) {
14187 if (err) return callback(err);
14188 var access_token = ohauth.stringQs(xhr.response);
14189 token('oauth_token', access_token.oauth_token);
14190 token('oauth_token_secret', access_token.oauth_token_secret);
14191 callback(null, oauth);
14194 get_access_token(oauth_token);
14199 // A single XMLHttpRequest wrapper that does authenticated calls if the
14200 // user has logged in.
14201 oauth.xhr = function(options, callback) {
14202 if (!oauth.authenticated()) {
14203 if (o.auto) return oauth.authenticate(run);
14204 else return callback('not authenticated', null);
14205 } else return run();
14208 var params = timenonce(getAuth(o)),
14209 url = o.url + options.path,
14210 oauth_token_secret = token('oauth_token_secret');
14212 // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
14213 if ((!options.options || !options.options.header ||
14214 options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
14216 params = xtend(params, ohauth.stringQs(options.content));
14219 params.oauth_token = token('oauth_token');
14220 params.oauth_signature = ohauth.signature(
14222 oauth_token_secret,
14223 ohauth.baseString(options.method, url, params));
14225 ohauth.xhr(options.method,
14226 url, params, options.content, options.options, done);
14229 function done(err, xhr) {
14230 if (err) return callback(err);
14231 else if (xhr.responseXML) return callback(err, xhr.responseXML);
14232 else return callback(err, xhr.response);
14236 // pre-authorize this object, if we can just get a token and token_secret
14238 oauth.preauth = function(c) {
14240 if (c.oauth_token) token('oauth_token', c.oauth_token);
14241 if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
14245 oauth.options = function(_) {
14246 if (!arguments.length) return o;
14250 o.url = o.url || 'http://www.openstreetmap.org';
14251 o.landing = o.landing || 'land.html';
14253 o.singlepage = o.singlepage || false;
14255 // Optional loading and loading-done functions for nice UI feedback.
14256 // by default, no-ops
14257 o.loading = o.loading || function() {};
14258 o.done = o.done || function() {};
14260 return oauth.preauth(o);
14263 // 'stamp' an authentication object from `getAuth()`
14264 // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
14266 function timenonce(o) {
14267 o.oauth_timestamp = ohauth.timestamp();
14268 o.oauth_nonce = ohauth.nonce();
14272 // get/set tokens. These are prefixed with the base URL so that `osm-auth`
14273 // can be used with multiple APIs and the keys in `localStorage`
14277 if (store.enabled) {
14278 token = function (x, y) {
14279 if (arguments.length === 1) return store.get(o.url + x);
14280 else if (arguments.length === 2) return store.set(o.url + x, y);
14284 token = function (x, y) {
14285 if (arguments.length === 1) return storage[o.url + x];
14286 else if (arguments.length === 2) return storage[o.url + x] = y;
14290 // Get an authentication object. If you just add and remove properties
14291 // from a single object, you'll need to use `delete` to make sure that
14292 // it doesn't contain undesired properties for authentication
14293 function getAuth(o) {
14295 oauth_consumer_key: o.oauth_consumer_key,
14296 oauth_signature_method: "HMAC-SHA1"
14300 // potentially pre-authorize
14306 },{"ohauth":2,"store":3,"xtend":4}],3:[function(require,module,exports){
14307 (function(global){;(function(win){
14309 doc = win.document,
14310 localStorageName = 'localStorage',
14313 store.disabled = false
14314 store.set = function(key, value) {}
14315 store.get = function(key) {}
14316 store.remove = function(key) {}
14317 store.clear = function() {}
14318 store.transact = function(key, defaultVal, transactionFn) {
14319 var val = store.get(key)
14320 if (transactionFn == null) {
14321 transactionFn = defaultVal
14324 if (typeof val == 'undefined') { val = defaultVal || {} }
14326 store.set(key, val)
14328 store.getAll = function() {}
14329 store.forEach = function() {}
14331 store.serialize = function(value) {
14332 return JSON.stringify(value)
14334 store.deserialize = function(value) {
14335 if (typeof value != 'string') { return undefined }
14336 try { return JSON.parse(value) }
14337 catch(e) { return value || undefined }
14340 // Functions to encapsulate questionable FireFox 3.6.13 behavior
14341 // when about.config::dom.storage.enabled === false
14342 // See https://github.com/marcuswestin/store.js/issues#issue/13
14343 function isLocalStorageNameSupported() {
14344 try { return (localStorageName in win && win[localStorageName]) }
14345 catch(err) { return false }
14348 if (isLocalStorageNameSupported()) {
14349 storage = win[localStorageName]
14350 store.set = function(key, val) {
14351 if (val === undefined) { return store.remove(key) }
14352 storage.setItem(key, store.serialize(val))
14355 store.get = function(key) { return store.deserialize(storage.getItem(key)) }
14356 store.remove = function(key) { storage.removeItem(key) }
14357 store.clear = function() { storage.clear() }
14358 store.getAll = function() {
14360 store.forEach(function(key, val) {
14365 store.forEach = function(callback) {
14366 for (var i=0; i<storage.length; i++) {
14367 var key = storage.key(i)
14368 callback(key, store.get(key))
14371 } else if (doc.documentElement.addBehavior) {
14374 // Since #userData storage applies only to specific paths, we need to
14375 // somehow link our data to a specific path. We choose /favicon.ico
14376 // as a pretty safe option, since all browsers already make a request to
14377 // this URL anyway and being a 404 will not hurt us here. We wrap an
14378 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
14379 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
14380 // since the iframe access rules appear to allow direct access and
14381 // manipulation of the document element, even for a 404 page. This
14382 // document can be used instead of the current document (which would
14383 // have been limited to the current path) to perform #userData storage.
14385 storageContainer = new ActiveXObject('htmlfile')
14386 storageContainer.open()
14387 storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></iframe>')
14388 storageContainer.close()
14389 storageOwner = storageContainer.w.frames[0].document
14390 storage = storageOwner.createElement('div')
14392 // somehow ActiveXObject instantiation failed (perhaps some special
14393 // security settings or otherwse), fall back to per-path storage
14394 storage = doc.createElement('div')
14395 storageOwner = doc.body
14397 function withIEStorage(storeFunction) {
14398 return function() {
14399 var args = Array.prototype.slice.call(arguments, 0)
14400 args.unshift(storage)
14401 // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
14402 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
14403 storageOwner.appendChild(storage)
14404 storage.addBehavior('#default#userData')
14405 storage.load(localStorageName)
14406 var result = storeFunction.apply(store, args)
14407 storageOwner.removeChild(storage)
14412 // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
14413 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
14414 function ieKeyFix(key) {
14415 return key.replace(forbiddenCharsRegex, '___')
14417 store.set = withIEStorage(function(storage, key, val) {
14418 key = ieKeyFix(key)
14419 if (val === undefined) { return store.remove(key) }
14420 storage.setAttribute(key, store.serialize(val))
14421 storage.save(localStorageName)
14424 store.get = withIEStorage(function(storage, key) {
14425 key = ieKeyFix(key)
14426 return store.deserialize(storage.getAttribute(key))
14428 store.remove = withIEStorage(function(storage, key) {
14429 key = ieKeyFix(key)
14430 storage.removeAttribute(key)
14431 storage.save(localStorageName)
14433 store.clear = withIEStorage(function(storage) {
14434 var attributes = storage.XMLDocument.documentElement.attributes
14435 storage.load(localStorageName)
14436 for (var i=0, attr; attr=attributes[i]; i++) {
14437 storage.removeAttribute(attr.name)
14439 storage.save(localStorageName)
14441 store.getAll = function(storage) {
14443 store.forEach(function(key, val) {
14448 store.forEach = withIEStorage(function(storage, callback) {
14449 var attributes = storage.XMLDocument.documentElement.attributes
14450 for (var i=0, attr; attr=attributes[i]; ++i) {
14451 callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
14457 var testKey = '__storejs__'
14458 store.set(testKey, testKey)
14459 if (store.get(testKey) != testKey) { store.disabled = true }
14460 store.remove(testKey)
14462 store.disabled = true
14464 store.enabled = !store.disabled
14466 if (typeof module != 'undefined' && module.exports) { module.exports = store }
14467 else if (typeof define === 'function' && define.amd) { define(store) }
14468 else { win.store = store }
14470 })(this.window || global);
14473 },{}],5:[function(require,module,exports){
14474 module.exports = hasKeys
14476 function hasKeys(source) {
14477 return source !== null &&
14478 (typeof source === "object" ||
14479 typeof source === "function")
14482 },{}],4:[function(require,module,exports){
14483 var Keys = require("object-keys")
14484 var hasKeys = require("./has-keys")
14486 module.exports = extend
14488 function extend() {
14491 for (var i = 0; i < arguments.length; i++) {
14492 var source = arguments[i]
14494 if (!hasKeys(source)) {
14498 var keys = Keys(source)
14500 for (var j = 0; j < keys.length; j++) {
14502 target[name] = source[name]
14509 },{"./has-keys":5,"object-keys":6}],7:[function(require,module,exports){
14510 (function(global){/**
14511 * jsHashes - A fast and independent hashing library pure JavaScript implemented (ES3 compliant) for both server and client side
14514 * @author Tomas Aparicio <tomas@rijndael-project.com>
14515 * @license New BSD (see LICENSE file)
14518 * Algorithms specification:
14520 * MD5 <http://www.ietf.org/rfc/rfc1321.txt>
14521 * RIPEMD-160 <http://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
14522 * SHA1 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14523 * SHA256 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14524 * SHA512 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14525 * HMAC <http://www.ietf.org/rfc/rfc2104.txt>
14531 // private helper methods
14532 function utf8Encode(str) {
14533 var x, y, output = '', i = -1, l;
14535 if (str && str.length) {
14537 while ((i+=1) < l) {
14538 /* Decode utf-16 surrogate pairs */
14539 x = str.charCodeAt(i);
14540 y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
14541 if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
14542 x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
14545 /* Encode output as utf-8 */
14547 output += String.fromCharCode(x);
14548 } else if (x <= 0x7FF) {
14549 output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
14550 0x80 | ( x & 0x3F));
14551 } else if (x <= 0xFFFF) {
14552 output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
14553 0x80 | ((x >>> 6 ) & 0x3F),
14554 0x80 | ( x & 0x3F));
14555 } else if (x <= 0x1FFFFF) {
14556 output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
14557 0x80 | ((x >>> 12) & 0x3F),
14558 0x80 | ((x >>> 6 ) & 0x3F),
14559 0x80 | ( x & 0x3F));
14566 function utf8Decode(str) {
14567 var i, ac, c1, c2, c3, arr = [], l;
14568 i = ac = c1 = c2 = c3 = 0;
14570 if (str && str.length) {
14575 c1 = str.charCodeAt(i);
14578 arr[ac] = String.fromCharCode(c1);
14580 } else if (c1 > 191 && c1 < 224) {
14581 c2 = str.charCodeAt(i + 1);
14582 arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
14585 c2 = str.charCodeAt(i + 1);
14586 c3 = str.charCodeAt(i + 2);
14587 arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
14592 return arr.join('');
14596 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
14597 * to work around bugs in some JS interpreters.
14599 function safe_add(x, y) {
14600 var lsw = (x & 0xFFFF) + (y & 0xFFFF),
14601 msw = (x >> 16) + (y >> 16) + (lsw >> 16);
14602 return (msw << 16) | (lsw & 0xFFFF);
14606 * Bitwise rotate a 32-bit number to the left.
14608 function bit_rol(num, cnt) {
14609 return (num << cnt) | (num >>> (32 - cnt));
14613 * Convert a raw string to a hex string
14615 function rstr2hex(input, hexcase) {
14616 var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
14617 output = '', x, i = 0, l = input.length;
14618 for (; i < l; i+=1) {
14619 x = input.charCodeAt(i);
14620 output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
14626 * Encode a string as utf-16
14628 function str2rstr_utf16le(input) {
14629 var i, l = input.length, output = '';
14630 for (i = 0; i < l; i+=1) {
14631 output += String.fromCharCode( input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF);
14636 function str2rstr_utf16be(input) {
14637 var i, l = input.length, output = '';
14638 for (i = 0; i < l; i+=1) {
14639 output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF);
14645 * Convert an array of big-endian words to a string
14647 function binb2rstr(input) {
14648 var i, l = input.length * 32, output = '';
14649 for (i = 0; i < l; i += 8) {
14650 output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
14656 * Convert an array of little-endian words to a string
14658 function binl2rstr(input) {
14659 var i, l = input.length * 32, output = '';
14660 for (i = 0;i < l; i += 8) {
14661 output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
14667 * Convert a raw string to an array of little-endian words
14668 * Characters >255 have their high-byte silently ignored.
14670 function rstr2binl(input) {
14671 var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
14672 for (i = 0; i < lo; i+=1) {
14675 for (i = 0; i < l; i += 8) {
14676 output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
14682 * Convert a raw string to an array of big-endian words
14683 * Characters >255 have their high-byte silently ignored.
14685 function rstr2binb(input) {
14686 var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
14687 for (i = 0; i < lo; i+=1) {
14690 for (i = 0; i < l; i += 8) {
14691 output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
14697 * Convert a raw string to an arbitrary string encoding
14699 function rstr2any(input, encoding) {
14700 var divisor = encoding.length,
14701 remainders = Array(),
14702 i, q, x, ld, quotient, dividend, output, full_length;
14704 /* Convert to an array of 16-bit big-endian values, forming the dividend */
14705 dividend = Array(Math.ceil(input.length / 2));
14706 ld = dividend.length;
14707 for (i = 0; i < ld; i+=1) {
14708 dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
14712 * Repeatedly perform a long division. The binary array forms the dividend,
14713 * the length of the encoding is the divisor. Once computed, the quotient
14714 * forms the dividend for the next step. We stop when the dividend is zerHashes.
14715 * All remainders are stored for later use.
14717 while(dividend.length > 0) {
14718 quotient = Array();
14720 for (i = 0; i < dividend.length; i+=1) {
14721 x = (x << 16) + dividend[i];
14722 q = Math.floor(x / divisor);
14724 if (quotient.length > 0 || q > 0) {
14725 quotient[quotient.length] = q;
14728 remainders[remainders.length] = x;
14729 dividend = quotient;
14732 /* Convert the remainders to the output string */
14734 for (i = remainders.length - 1; i >= 0; i--) {
14735 output += encoding.charAt(remainders[i]);
14738 /* Append leading zero equivalents */
14739 full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
14740 for (i = output.length; i < full_length; i+=1) {
14741 output = encoding[0] + output;
14747 * Convert a raw string to a base-64 string
14749 function rstr2b64(input, b64pad) {
14750 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
14752 len = input.length, i, j, triplet;
14753 b64pad= b64pad || '=';
14754 for (i = 0; i < len; i += 3) {
14755 triplet = (input.charCodeAt(i) << 16)
14756 | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
14757 | (i + 2 < len ? input.charCodeAt(i+2) : 0);
14758 for (j = 0; j < 4; j+=1) {
14759 if (i * 8 + j * 6 > input.length * 8) {
14762 output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
14771 * @property {String} version
14780 Base64 : function () {
14781 // private properties
14782 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
14783 pad = '=', // default pad according with the RFC standard
14784 url = false, // URL encoding support @todo
14785 utf8 = true; // by default enable UTF-8 support encoding
14787 // public method for encoding
14788 this.encode = function (input) {
14791 len = input.length;
14794 input = (utf8) ? utf8Encode(input) : input;
14796 for (i = 0; i < len; i += 3) {
14797 triplet = (input.charCodeAt(i) << 16)
14798 | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
14799 | (i + 2 < len ? input.charCodeAt(i+2) : 0);
14800 for (j = 0; j < 4; j+=1) {
14801 if (i * 8 + j * 6 > len * 8) {
14804 output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
14811 // public method for decoding
14812 this.decode = function (input) {
14813 // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
14814 var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
14817 if (!input) { return input; }
14820 input = input.replace(new RegExp('\\'+pad,'gi'),''); // use '='
14823 do { // unpack four hexets into three octets using index points in b64
14824 h1 = tab.indexOf(input.charAt(i+=1));
14825 h2 = tab.indexOf(input.charAt(i+=1));
14826 h3 = tab.indexOf(input.charAt(i+=1));
14827 h4 = tab.indexOf(input.charAt(i+=1));
14829 bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
14831 o1 = bits >> 16 & 0xff;
14832 o2 = bits >> 8 & 0xff;
14837 arr[ac] = String.fromCharCode(o1);
14838 } else if (h4 === 64) {
14839 arr[ac] = String.fromCharCode(o1, o2);
14841 arr[ac] = String.fromCharCode(o1, o2, o3);
14843 } while (i < input.length);
14845 dec = arr.join('');
14846 dec = (utf8) ? utf8Decode(dec) : dec;
14851 // set custom pad string
14852 this.setPad = function (str) {
14856 // set custom tab string characters
14857 this.setTab = function (str) {
14861 this.setUTF8 = function (bool) {
14862 if (typeof bool === 'boolean') {
14870 * CRC-32 calculation
14874 * @param {String} str Input String
14877 CRC32 : function (str) {
14878 var crc = 0, x = 0, y = 0, table, i, iTop;
14879 str = utf8Encode(str);
14882 '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
14883 '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
14884 '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
14885 '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
14886 'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
14887 '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
14888 'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
14889 '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
14890 'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
14891 '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
14892 'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
14893 '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
14894 'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
14895 '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
14896 '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
14897 '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
14898 '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
14899 'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
14900 '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
14901 'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
14902 '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
14903 'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
14904 '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
14905 'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
14906 '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
14907 'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
14911 for (i = 0, iTop = str.length; i < iTop; i+=1 ) {
14912 y = ( crc ^ str.charCodeAt( i ) ) & 0xFF;
14913 x = '0x' + table.substr( y * 9, 8 );
14914 crc = ( crc >>> 8 ) ^ x;
14916 // always return a positive number (that's what >>> 0 does)
14917 return (crc ^ (-1)) >>> 0;
14923 * @param {Object} [config]
14925 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
14926 * Digest Algorithm, as defined in RFC 1321.
14927 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
14928 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
14929 * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
14931 MD5 : function (options) {
14933 * Private config properties. You may need to tweak these to be compatible with
14934 * the server-side, but the defaults work in most cases.
14935 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
14937 var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
14938 b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
14939 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
14941 // privileged (public) methods
14942 this.hex = function (s) {
14943 return rstr2hex(rstr(s, utf8), hexcase);
14945 this.b64 = function (s) {
14946 return rstr2b64(rstr(s), b64pad);
14948 this.any = function(s, e) {
14949 return rstr2any(rstr(s, utf8), e);
14951 this.hex_hmac = function (k, d) {
14952 return rstr2hex(rstr_hmac(k, d), hexcase);
14954 this.b64_hmac = function (k, d) {
14955 return rstr2b64(rstr_hmac(k,d), b64pad);
14957 this.any_hmac = function (k, d, e) {
14958 return rstr2any(rstr_hmac(k, d), e);
14961 * Perform a simple self-test to see if the VM is working
14962 * @return {String} Hexadecimal hash sample
14964 this.vm_test = function () {
14965 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
14968 * Enable/disable uppercase hexadecimal returned string
14970 * @return {Object} this
14972 this.setUpperCase = function (a) {
14973 if (typeof a === 'boolean' ) {
14979 * Defines a base64 pad string
14980 * @param {String} Pad
14981 * @return {Object} this
14983 this.setPad = function (a) {
14984 b64pad = a || b64pad;
14988 * Defines a base64 pad string
14990 * @return {Object} [this]
14992 this.setUTF8 = function (a) {
14993 if (typeof a === 'boolean') {
15002 * Calculate the MD5 of a raw string
15005 s = (utf8) ? utf8Encode(s): s;
15006 return binl2rstr(binl(rstr2binl(s), s.length * 8));
15010 * Calculate the HMAC-MD5, of a key and some data (raw strings)
15012 function rstr_hmac(key, data) {
15013 var bkey, ipad, opad, hash, i;
15015 key = (utf8) ? utf8Encode(key) : key;
15016 data = (utf8) ? utf8Encode(data) : data;
15017 bkey = rstr2binl(key);
15018 if (bkey.length > 16) {
15019 bkey = binl(bkey, key.length * 8);
15022 ipad = Array(16), opad = Array(16);
15023 for (i = 0; i < 16; i+=1) {
15024 ipad[i] = bkey[i] ^ 0x36363636;
15025 opad[i] = bkey[i] ^ 0x5C5C5C5C;
15027 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
15028 return binl2rstr(binl(opad.concat(hash), 512 + 128));
15032 * Calculate the MD5 of an array of little-endian words, and a bit length.
15034 function binl(x, len) {
15035 var i, olda, oldb, oldc, oldd,
15041 /* append padding */
15042 x[len >> 5] |= 0x80 << ((len) % 32);
15043 x[(((len + 64) >>> 9) << 4) + 14] = len;
15045 for (i = 0; i < x.length; i += 16) {
15051 a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
15052 d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
15053 c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
15054 b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
15055 a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
15056 d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
15057 c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
15058 b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
15059 a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
15060 d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
15061 c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
15062 b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
15063 a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
15064 d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
15065 c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
15066 b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
15068 a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
15069 d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
15070 c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
15071 b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
15072 a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
15073 d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
15074 c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
15075 b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
15076 a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
15077 d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
15078 c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
15079 b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
15080 a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
15081 d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
15082 c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
15083 b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
15085 a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
15086 d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
15087 c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
15088 b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
15089 a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
15090 d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
15091 c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
15092 b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
15093 a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
15094 d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
15095 c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
15096 b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
15097 a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
15098 d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
15099 c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
15100 b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
15102 a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
15103 d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
15104 c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
15105 b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
15106 a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
15107 d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
15108 c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
15109 b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
15110 a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
15111 d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
15112 c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
15113 b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
15114 a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
15115 d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
15116 c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
15117 b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
15119 a = safe_add(a, olda);
15120 b = safe_add(b, oldb);
15121 c = safe_add(c, oldc);
15122 d = safe_add(d, oldd);
15124 return Array(a, b, c, d);
15128 * These functions implement the four basic operations the algorithm uses.
15130 function md5_cmn(q, a, b, x, s, t) {
15131 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
15133 function md5_ff(a, b, c, d, x, s, t) {
15134 return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
15136 function md5_gg(a, b, c, d, x, s, t) {
15137 return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
15139 function md5_hh(a, b, c, d, x, s, t) {
15140 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
15142 function md5_ii(a, b, c, d, x, s, t) {
15143 return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
15148 * @class Hashes.SHA1
15149 * @param {Object} [config]
15152 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
15153 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
15154 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15155 * See http://pajhome.org.uk/crypt/md5 for details.
15157 SHA1 : function (options) {
15159 * Private config properties. You may need to tweak these to be compatible with
15160 * the server-side, but the defaults work in most cases.
15161 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
15163 var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
15164 b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
15165 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
15168 this.hex = function (s) {
15169 return rstr2hex(rstr(s, utf8), hexcase);
15171 this.b64 = function (s) {
15172 return rstr2b64(rstr(s, utf8), b64pad);
15174 this.any = function (s, e) {
15175 return rstr2any(rstr(s, utf8), e);
15177 this.hex_hmac = function (k, d) {
15178 return rstr2hex(rstr_hmac(k, d));
15180 this.b64_hmac = function (k, d) {
15181 return rstr2b64(rstr_hmac(k, d), b64pad);
15183 this.any_hmac = function (k, d, e) {
15184 return rstr2any(rstr_hmac(k, d), e);
15187 * Perform a simple self-test to see if the VM is working
15188 * @return {String} Hexadecimal hash sample
15191 this.vm_test = function () {
15192 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15195 * @description Enable/disable uppercase hexadecimal returned string
15197 * @return {Object} this
15200 this.setUpperCase = function (a) {
15201 if (typeof a === 'boolean') {
15207 * @description Defines a base64 pad string
15208 * @param {string} Pad
15209 * @return {Object} this
15212 this.setPad = function (a) {
15213 b64pad = a || b64pad;
15217 * @description Defines a base64 pad string
15219 * @return {Object} this
15222 this.setUTF8 = function (a) {
15223 if (typeof a === 'boolean') {
15232 * Calculate the SHA-512 of a raw string
15235 s = (utf8) ? utf8Encode(s) : s;
15236 return binb2rstr(binb(rstr2binb(s), s.length * 8));
15240 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
15242 function rstr_hmac(key, data) {
15243 var bkey, ipad, opad, i, hash;
15244 key = (utf8) ? utf8Encode(key) : key;
15245 data = (utf8) ? utf8Encode(data) : data;
15246 bkey = rstr2binb(key);
15248 if (bkey.length > 16) {
15249 bkey = binb(bkey, key.length * 8);
15251 ipad = Array(16), opad = Array(16);
15252 for (i = 0; i < 16; i+=1) {
15253 ipad[i] = bkey[i] ^ 0x36363636;
15254 opad[i] = bkey[i] ^ 0x5C5C5C5C;
15256 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
15257 return binb2rstr(binb(opad.concat(hash), 512 + 160));
15261 * Calculate the SHA-1 of an array of big-endian words, and a bit length
15263 function binb(x, len) {
15264 var i, j, t, olda, oldb, oldc, oldd, olde,
15272 /* append padding */
15273 x[len >> 5] |= 0x80 << (24 - len % 32);
15274 x[((len + 64 >> 9) << 4) + 15] = len;
15276 for (i = 0; i < x.length; i += 16) {
15283 for (j = 0; j < 80; j+=1) {
15287 w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
15289 t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
15290 safe_add(safe_add(e, w[j]), sha1_kt(j)));
15293 c = bit_rol(b, 30);
15298 a = safe_add(a, olda);
15299 b = safe_add(b, oldb);
15300 c = safe_add(c, oldc);
15301 d = safe_add(d, oldd);
15302 e = safe_add(e, olde);
15304 return Array(a, b, c, d, e);
15308 * Perform the appropriate triplet combination function for the current
15311 function sha1_ft(t, b, c, d) {
15312 if (t < 20) { return (b & c) | ((~b) & d); }
15313 if (t < 40) { return b ^ c ^ d; }
15314 if (t < 60) { return (b & c) | (b & d) | (c & d); }
15319 * Determine the appropriate additive constant for the current iteration
15321 function sha1_kt(t) {
15322 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
15323 (t < 60) ? -1894007588 : -899497514;
15327 * @class Hashes.SHA256
15330 * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
15331 * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
15332 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15333 * See http://pajhome.org.uk/crypt/md5 for details.
15334 * Also http://anmar.eu.org/projects/jssha2/
15336 SHA256 : function (options) {
15338 * Private properties configuration variables. You may need to tweak these to be compatible with
15339 * the server-side, but the defaults work in most cases.
15340 * @see this.setUpperCase() method
15341 * @see this.setPad() method
15343 var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase */
15344 b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', /* base-64 pad character. Default '=' for strict RFC compliance */
15345 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15348 /* privileged (public) methods */
15349 this.hex = function (s) {
15350 return rstr2hex(rstr(s, utf8));
15352 this.b64 = function (s) {
15353 return rstr2b64(rstr(s, utf8), b64pad);
15355 this.any = function (s, e) {
15356 return rstr2any(rstr(s, utf8), e);
15358 this.hex_hmac = function (k, d) {
15359 return rstr2hex(rstr_hmac(k, d));
15361 this.b64_hmac = function (k, d) {
15362 return rstr2b64(rstr_hmac(k, d), b64pad);
15364 this.any_hmac = function (k, d, e) {
15365 return rstr2any(rstr_hmac(k, d), e);
15368 * Perform a simple self-test to see if the VM is working
15369 * @return {String} Hexadecimal hash sample
15372 this.vm_test = function () {
15373 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15376 * Enable/disable uppercase hexadecimal returned string
15378 * @return {Object} this
15381 this.setUpperCase = function (a) {
15382 if (typeof a === 'boolean') {
15388 * @description Defines a base64 pad string
15389 * @param {string} Pad
15390 * @return {Object} this
15393 this.setPad = function (a) {
15394 b64pad = a || b64pad;
15398 * Defines a base64 pad string
15400 * @return {Object} this
15403 this.setUTF8 = function (a) {
15404 if (typeof a === 'boolean') {
15413 * Calculate the SHA-512 of a raw string
15415 function rstr(s, utf8) {
15416 s = (utf8) ? utf8Encode(s) : s;
15417 return binb2rstr(binb(rstr2binb(s), s.length * 8));
15421 * Calculate the HMAC-sha256 of a key and some data (raw strings)
15423 function rstr_hmac(key, data) {
15424 key = (utf8) ? utf8Encode(key) : key;
15425 data = (utf8) ? utf8Encode(data) : data;
15427 bkey = rstr2binb(key),
15431 if (bkey.length > 16) { bkey = binb(bkey, key.length * 8); }
15433 for (; i < 16; i+=1) {
15434 ipad[i] = bkey[i] ^ 0x36363636;
15435 opad[i] = bkey[i] ^ 0x5C5C5C5C;
15438 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
15439 return binb2rstr(binb(opad.concat(hash), 512 + 256));
15443 * Main sha256 function, with its support functions
15445 function sha256_S (X, n) {return ( X >>> n ) | (X << (32 - n));}
15446 function sha256_R (X, n) {return ( X >>> n );}
15447 function sha256_Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
15448 function sha256_Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
15449 function sha256_Sigma0256(x) {return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));}
15450 function sha256_Sigma1256(x) {return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));}
15451 function sha256_Gamma0256(x) {return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));}
15452 function sha256_Gamma1256(x) {return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));}
15453 function sha256_Sigma0512(x) {return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39));}
15454 function sha256_Sigma1512(x) {return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41));}
15455 function sha256_Gamma0512(x) {return (sha256_S(x, 1) ^ sha256_S(x, 8) ^ sha256_R(x, 7));}
15456 function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));}
15459 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,
15460 -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
15461 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
15462 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
15463 -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
15464 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
15465 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885,
15466 -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
15467 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
15468 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,
15469 -1866530822, -1538233109, -1090935817, -965641998
15472 function binb(m, l) {
15473 var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
15474 1359893119, -1694144372, 528734635, 1541459225];
15475 var W = new Array(64);
15476 var a, b, c, d, e, f, g, h;
15479 /* append padding */
15480 m[l >> 5] |= 0x80 << (24 - l % 32);
15481 m[((l + 64 >> 9) << 4) + 15] = l;
15483 for (i = 0; i < m.length; i += 16)
15494 for (j = 0; j < 64; j+=1)
15499 W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
15500 sha256_Gamma0256(W[j - 15])), W[j - 16]);
15503 T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
15504 sha256_K[j]), W[j]);
15505 T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
15509 e = safe_add(d, T1);
15513 a = safe_add(T1, T2);
15516 HASH[0] = safe_add(a, HASH[0]);
15517 HASH[1] = safe_add(b, HASH[1]);
15518 HASH[2] = safe_add(c, HASH[2]);
15519 HASH[3] = safe_add(d, HASH[3]);
15520 HASH[4] = safe_add(e, HASH[4]);
15521 HASH[5] = safe_add(f, HASH[5]);
15522 HASH[6] = safe_add(g, HASH[6]);
15523 HASH[7] = safe_add(h, HASH[7]);
15531 * @class Hashes.SHA512
15534 * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
15535 * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
15536 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15537 * See http://pajhome.org.uk/crypt/md5 for details.
15539 SHA512 : function (options) {
15541 * Private properties configuration variables. You may need to tweak these to be compatible with
15542 * the server-side, but the defaults work in most cases.
15543 * @see this.setUpperCase() method
15544 * @see this.setPad() method
15546 var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false , /* hexadecimal output case format. false - lowercase; true - uppercase */
15547 b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', /* base-64 pad character. Default '=' for strict RFC compliance */
15548 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15551 /* privileged (public) methods */
15552 this.hex = function (s) {
15553 return rstr2hex(rstr(s));
15555 this.b64 = function (s) {
15556 return rstr2b64(rstr(s), b64pad);
15558 this.any = function (s, e) {
15559 return rstr2any(rstr(s), e);
15561 this.hex_hmac = function (k, d) {
15562 return rstr2hex(rstr_hmac(k, d));
15564 this.b64_hmac = function (k, d) {
15565 return rstr2b64(rstr_hmac(k, d), b64pad);
15567 this.any_hmac = function (k, d, e) {
15568 return rstr2any(rstr_hmac(k, d), e);
15571 * Perform a simple self-test to see if the VM is working
15572 * @return {String} Hexadecimal hash sample
15575 this.vm_test = function () {
15576 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15579 * @description Enable/disable uppercase hexadecimal returned string
15581 * @return {Object} this
15584 this.setUpperCase = function (a) {
15585 if (typeof a === 'boolean') {
15591 * @description Defines a base64 pad string
15592 * @param {string} Pad
15593 * @return {Object} this
15596 this.setPad = function (a) {
15597 b64pad = a || b64pad;
15601 * @description Defines a base64 pad string
15603 * @return {Object} this
15606 this.setUTF8 = function (a) {
15607 if (typeof a === 'boolean') {
15613 /* private methods */
15616 * Calculate the SHA-512 of a raw string
15619 s = (utf8) ? utf8Encode(s) : s;
15620 return binb2rstr(binb(rstr2binb(s), s.length * 8));
15623 * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
15625 function rstr_hmac(key, data) {
15626 key = (utf8) ? utf8Encode(key) : key;
15627 data = (utf8) ? utf8Encode(data) : data;
15630 bkey = rstr2binb(key),
15631 ipad = Array(32), opad = Array(32);
15633 if (bkey.length > 32) { bkey = binb(bkey, key.length * 8); }
15635 for (; i < 32; i+=1) {
15636 ipad[i] = bkey[i] ^ 0x36363636;
15637 opad[i] = bkey[i] ^ 0x5C5C5C5C;
15640 hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
15641 return binb2rstr(binb(opad.concat(hash), 1024 + 512));
15645 * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
15647 function binb(x, len) {
15650 hash = new Array(16),
15651 //Initial hash values
15653 new int64(0x6a09e667, -205731576),
15654 new int64(-1150833019, -2067093701),
15655 new int64(0x3c6ef372, -23791573),
15656 new int64(-1521486534, 0x5f1d36f1),
15657 new int64(0x510e527f, -1377402159),
15658 new int64(-1694144372, 0x2b3e6c1f),
15659 new int64(0x1f83d9ab, -79577749),
15660 new int64(0x5be0cd19, 0x137e2179)
15662 T1 = new int64(0, 0),
15663 T2 = new int64(0, 0),
15664 a = new int64(0,0),
15665 b = new int64(0,0),
15666 c = new int64(0,0),
15667 d = new int64(0,0),
15668 e = new int64(0,0),
15669 f = new int64(0,0),
15670 g = new int64(0,0),
15671 h = new int64(0,0),
15672 //Temporary variables not specified by the document
15673 s0 = new int64(0, 0),
15674 s1 = new int64(0, 0),
15675 Ch = new int64(0, 0),
15676 Maj = new int64(0, 0),
15677 r1 = new int64(0, 0),
15678 r2 = new int64(0, 0),
15679 r3 = new int64(0, 0);
15681 if (sha512_k === undefined) {
15684 new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
15685 new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
15686 new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
15687 new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
15688 new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
15689 new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
15690 new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
15691 new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
15692 new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
15693 new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
15694 new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
15695 new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
15696 new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
15697 new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
15698 new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
15699 new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
15700 new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
15701 new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
15702 new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
15703 new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
15704 new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
15705 new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
15706 new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
15707 new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
15708 new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
15709 new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
15710 new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
15711 new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
15712 new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
15713 new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
15714 new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
15715 new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
15716 new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
15717 new int64(-354779690, -840897762), new int64(-176337025, -294727304),
15718 new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
15719 new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
15720 new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
15721 new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
15722 new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
15723 new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
15727 for (i=0; i<80; i+=1) {
15728 W[i] = new int64(0, 0);
15731 // append padding to the source string. The format is described in the FIPS.
15732 x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
15733 x[((len + 128 >> 10)<< 5) + 31] = len;
15735 for (i = 0; i<l; i+=32) { //32 dwords is the block size
15736 int64copy(a, H[0]);
15737 int64copy(b, H[1]);
15738 int64copy(c, H[2]);
15739 int64copy(d, H[3]);
15740 int64copy(e, H[4]);
15741 int64copy(f, H[5]);
15742 int64copy(g, H[6]);
15743 int64copy(h, H[7]);
15745 for (j=0; j<16; j+=1) {
15746 W[j].h = x[i + 2*j];
15747 W[j].l = x[i + 2*j + 1];
15750 for (j=16; j<80; j+=1) {
15752 int64rrot(r1, W[j-2], 19);
15753 int64revrrot(r2, W[j-2], 29);
15754 int64shr(r3, W[j-2], 6);
15755 s1.l = r1.l ^ r2.l ^ r3.l;
15756 s1.h = r1.h ^ r2.h ^ r3.h;
15758 int64rrot(r1, W[j-15], 1);
15759 int64rrot(r2, W[j-15], 8);
15760 int64shr(r3, W[j-15], 7);
15761 s0.l = r1.l ^ r2.l ^ r3.l;
15762 s0.h = r1.h ^ r2.h ^ r3.h;
15764 int64add4(W[j], s1, W[j-7], s0, W[j-16]);
15767 for (j = 0; j < 80; j+=1) {
15769 Ch.l = (e.l & f.l) ^ (~e.l & g.l);
15770 Ch.h = (e.h & f.h) ^ (~e.h & g.h);
15773 int64rrot(r1, e, 14);
15774 int64rrot(r2, e, 18);
15775 int64revrrot(r3, e, 9);
15776 s1.l = r1.l ^ r2.l ^ r3.l;
15777 s1.h = r1.h ^ r2.h ^ r3.h;
15780 int64rrot(r1, a, 28);
15781 int64revrrot(r2, a, 2);
15782 int64revrrot(r3, a, 7);
15783 s0.l = r1.l ^ r2.l ^ r3.l;
15784 s0.h = r1.h ^ r2.h ^ r3.h;
15787 Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
15788 Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
15790 int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
15791 int64add(T2, s0, Maj);
15796 int64add(e, d, T1);
15800 int64add(a, T1, T2);
15802 int64add(H[0], H[0], a);
15803 int64add(H[1], H[1], b);
15804 int64add(H[2], H[2], c);
15805 int64add(H[3], H[3], d);
15806 int64add(H[4], H[4], e);
15807 int64add(H[5], H[5], f);
15808 int64add(H[6], H[6], g);
15809 int64add(H[7], H[7], h);
15812 //represent the hash as an array of 32-bit dwords
15813 for (i=0; i<8; i+=1) {
15814 hash[2*i] = H[i].h;
15815 hash[2*i + 1] = H[i].l;
15820 //A constructor for 64-bit numbers
15821 function int64(h, l) {
15824 //this.toString = int64toString;
15827 //Copies src into dst, assuming both are 64-bit numbers
15828 function int64copy(dst, src) {
15833 //Right-rotates a 64-bit number by shift
15834 //Won't handle cases of shift>=32
15835 //The function revrrot() is for that
15836 function int64rrot(dst, x, shift) {
15837 dst.l = (x.l >>> shift) | (x.h << (32-shift));
15838 dst.h = (x.h >>> shift) | (x.l << (32-shift));
15841 //Reverses the dwords of the source and then rotates right by shift.
15842 //This is equivalent to rotation by 32+shift
15843 function int64revrrot(dst, x, shift) {
15844 dst.l = (x.h >>> shift) | (x.l << (32-shift));
15845 dst.h = (x.l >>> shift) | (x.h << (32-shift));
15848 //Bitwise-shifts right a 64-bit number by shift
15849 //Won't handle shift>=32, but it's never needed in SHA512
15850 function int64shr(dst, x, shift) {
15851 dst.l = (x.l >>> shift) | (x.h << (32-shift));
15852 dst.h = (x.h >>> shift);
15855 //Adds two 64-bit numbers
15856 //Like the original implementation, does not rely on 32-bit operations
15857 function int64add(dst, x, y) {
15858 var w0 = (x.l & 0xffff) + (y.l & 0xffff);
15859 var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
15860 var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
15861 var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
15862 dst.l = (w0 & 0xffff) | (w1 << 16);
15863 dst.h = (w2 & 0xffff) | (w3 << 16);
15866 //Same, except with 4 addends. Works faster than adding them one by one.
15867 function int64add4(dst, a, b, c, d) {
15868 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
15869 var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
15870 var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
15871 var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
15872 dst.l = (w0 & 0xffff) | (w1 << 16);
15873 dst.h = (w2 & 0xffff) | (w3 << 16);
15876 //Same, except with 5 addends
15877 function int64add5(dst, a, b, c, d, e) {
15878 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
15879 w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
15880 w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
15881 w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
15882 dst.l = (w0 & 0xffff) | (w1 << 16);
15883 dst.h = (w2 & 0xffff) | (w3 << 16);
15887 * @class Hashes.RMD160
15889 * @param {Object} [config]
15891 * A JavaScript implementation of the RIPEMD-160 Algorithm
15892 * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
15893 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15894 * See http://pajhome.org.uk/crypt/md5 for details.
15895 * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
15897 RMD160 : function (options) {
15899 * Private properties configuration variables. You may need to tweak these to be compatible with
15900 * the server-side, but the defaults work in most cases.
15901 * @see this.setUpperCase() method
15902 * @see this.setPad() method
15904 var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, /* hexadecimal output case format. false - lowercase; true - uppercase */
15905 b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', /* base-64 pad character. Default '=' for strict RFC compliance */
15906 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15908 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
15909 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
15910 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
15911 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
15912 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13
15915 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
15916 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
15917 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
15918 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
15919 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11
15922 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
15923 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
15924 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
15925 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
15926 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6
15929 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
15930 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
15931 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
15932 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
15933 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11
15936 /* privileged (public) methods */
15937 this.hex = function (s) {
15938 return rstr2hex(rstr(s, utf8));
15940 this.b64 = function (s) {
15941 return rstr2b64(rstr(s, utf8), b64pad);
15943 this.any = function (s, e) {
15944 return rstr2any(rstr(s, utf8), e);
15946 this.hex_hmac = function (k, d) {
15947 return rstr2hex(rstr_hmac(k, d));
15949 this.b64_hmac = function (k, d) {
15950 return rstr2b64(rstr_hmac(k, d), b64pad);
15952 this.any_hmac = function (k, d, e) {
15953 return rstr2any(rstr_hmac(k, d), e);
15956 * Perform a simple self-test to see if the VM is working
15957 * @return {String} Hexadecimal hash sample
15960 this.vm_test = function () {
15961 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15964 * @description Enable/disable uppercase hexadecimal returned string
15966 * @return {Object} this
15969 this.setUpperCase = function (a) {
15970 if (typeof a === 'boolean' ) { hexcase = a; }
15974 * @description Defines a base64 pad string
15975 * @param {string} Pad
15976 * @return {Object} this
15979 this.setPad = function (a) {
15980 if (typeof a !== 'undefined' ) { b64pad = a; }
15984 * @description Defines a base64 pad string
15986 * @return {Object} this
15989 this.setUTF8 = function (a) {
15990 if (typeof a === 'boolean') { utf8 = a; }
15994 /* private methods */
15997 * Calculate the rmd160 of a raw string
16000 s = (utf8) ? utf8Encode(s) : s;
16001 return binl2rstr(binl(rstr2binl(s), s.length * 8));
16005 * Calculate the HMAC-rmd160 of a key and some data (raw strings)
16007 function rstr_hmac(key, data) {
16008 key = (utf8) ? utf8Encode(key) : key;
16009 data = (utf8) ? utf8Encode(data) : data;
16011 bkey = rstr2binl(key),
16012 ipad = Array(16), opad = Array(16);
16014 if (bkey.length > 16) {
16015 bkey = binl(bkey, key.length * 8);
16018 for (i = 0; i < 16; i+=1) {
16019 ipad[i] = bkey[i] ^ 0x36363636;
16020 opad[i] = bkey[i] ^ 0x5C5C5C5C;
16022 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
16023 return binl2rstr(binl(opad.concat(hash), 512 + 160));
16027 * Convert an array of little-endian words to a string
16029 function binl2rstr(input) {
16030 var i, output = '', l = input.length * 32;
16031 for (i = 0; i < l; i += 8) {
16032 output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
16038 * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
16040 function binl(x, len) {
16047 A1, B1, C1, D1, E1,
16048 A2, B2, C2, D2, E2;
16050 /* append padding */
16051 x[len >> 5] |= 0x80 << (len % 32);
16052 x[(((len + 64) >>> 9) << 4) + 14] = len;
16055 for (i = 0; i < l; i+=16) {
16056 A1 = A2 = h0; B1 = B2 = h1; C1 = C2 = h2; D1 = D2 = h3; E1 = E2 = h4;
16057 for (j = 0; j <= 79; j+=1) {
16058 T = safe_add(A1, rmd160_f(j, B1, C1, D1));
16059 T = safe_add(T, x[i + rmd160_r1[j]]);
16060 T = safe_add(T, rmd160_K1(j));
16061 T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
16062 A1 = E1; E1 = D1; D1 = bit_rol(C1, 10); C1 = B1; B1 = T;
16063 T = safe_add(A2, rmd160_f(79-j, B2, C2, D2));
16064 T = safe_add(T, x[i + rmd160_r2[j]]);
16065 T = safe_add(T, rmd160_K2(j));
16066 T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
16067 A2 = E2; E2 = D2; D2 = bit_rol(C2, 10); C2 = B2; B2 = T;
16070 T = safe_add(h1, safe_add(C1, D2));
16071 h1 = safe_add(h2, safe_add(D1, E2));
16072 h2 = safe_add(h3, safe_add(E1, A2));
16073 h3 = safe_add(h4, safe_add(A1, B2));
16074 h4 = safe_add(h0, safe_add(B1, C2));
16077 return [h0, h1, h2, h3, h4];
16080 // specific algorithm methods
16081 function rmd160_f(j, x, y, z) {
16082 return ( 0 <= j && j <= 15) ? (x ^ y ^ z) :
16083 (16 <= j && j <= 31) ? (x & y) | (~x & z) :
16084 (32 <= j && j <= 47) ? (x | ~y) ^ z :
16085 (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
16086 (64 <= j && j <= 79) ? x ^ (y | ~z) :
16087 'rmd160_f: j out of range';
16090 function rmd160_K1(j) {
16091 return ( 0 <= j && j <= 15) ? 0x00000000 :
16092 (16 <= j && j <= 31) ? 0x5a827999 :
16093 (32 <= j && j <= 47) ? 0x6ed9eba1 :
16094 (48 <= j && j <= 63) ? 0x8f1bbcdc :
16095 (64 <= j && j <= 79) ? 0xa953fd4e :
16096 'rmd160_K1: j out of range';
16099 function rmd160_K2(j){
16100 return ( 0 <= j && j <= 15) ? 0x50a28be6 :
16101 (16 <= j && j <= 31) ? 0x5c4dd124 :
16102 (32 <= j && j <= 47) ? 0x6d703ef3 :
16103 (48 <= j && j <= 63) ? 0x7a6d76e9 :
16104 (64 <= j && j <= 79) ? 0x00000000 :
16105 'rmd160_K2: j out of range';
16111 (function( window, undefined ) {
16112 var freeExports = false;
16113 if (typeof exports === 'object' ) {
16114 freeExports = exports;
16115 if (exports && typeof global === 'object' && global && global === global.global ) { window = global; }
16118 if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
16119 // define as an anonymous module, so, through path mapping, it can be aliased
16120 define(function () { return Hashes; });
16122 else if ( freeExports ) {
16123 // in Node.js or RingoJS v0.8.0+
16124 if ( typeof module === 'object' && module && module.exports === freeExports ) {
16125 module.exports = Hashes;
16127 // in Narwhal or RingoJS v0.7.0-
16129 freeExports.Hashes = Hashes;
16133 // in a browser or Rhino
16134 window.Hashes = Hashes;
16140 },{}],2:[function(require,module,exports){
16143 var hashes = require('jshashes'),
16144 xtend = require('xtend'),
16145 sha1 = new hashes.SHA1();
16149 ohauth.qsString = function(obj) {
16150 return Object.keys(obj).sort().map(function(key) {
16151 return ohauth.percentEncode(key) + '=' +
16152 ohauth.percentEncode(obj[key]);
16156 ohauth.stringQs = function(str) {
16157 return str.split('&').reduce(function(obj, pair){
16158 var parts = pair.split('=');
16159 obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
16160 '' : decodeURIComponent(parts[1]);
16165 ohauth.rawxhr = function(method, url, data, headers, callback) {
16166 var xhr = new XMLHttpRequest(),
16167 twoHundred = /^20\d$/;
16168 xhr.onreadystatechange = function() {
16169 if (4 == xhr.readyState && 0 !== xhr.status) {
16170 if (twoHundred.test(xhr.status)) callback(null, xhr);
16171 else return callback(xhr, null);
16174 xhr.onerror = function(e) { return callback(e, null); };
16175 xhr.open(method, url, true);
16176 for (var h in headers) xhr.setRequestHeader(h, headers[h]);
16180 ohauth.xhr = function(method, url, auth, data, options, callback) {
16181 var headers = (options && options.header) || {
16182 'Content-Type': 'application/x-www-form-urlencoded'
16184 headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
16185 ohauth.rawxhr(method, url, data, headers, callback);
16188 ohauth.nonce = function() {
16189 for (var o = ''; o.length < 6;) {
16190 o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
16195 ohauth.authHeader = function(obj) {
16196 return Object.keys(obj).sort().map(function(key) {
16197 return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
16201 ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
16203 ohauth.percentEncode = function(s) {
16204 return encodeURIComponent(s)
16205 .replace(/\!/g, '%21').replace(/\'/g, '%27')
16206 .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
16209 ohauth.baseString = function(method, url, params) {
16210 if (params.oauth_signature) delete params.oauth_signature;
16213 ohauth.percentEncode(url),
16214 ohauth.percentEncode(ohauth.qsString(params))].join('&');
16217 ohauth.signature = function(oauth_secret, token_secret, baseString) {
16218 return sha1.b64_hmac(
16219 ohauth.percentEncode(oauth_secret) + '&' +
16220 ohauth.percentEncode(token_secret),
16225 * Takes an options object for configuration (consumer_key,
16226 * consumer_secret, version, signature_method, token) and returns a
16227 * function that generates the Authorization header for given data.
16229 * The returned function takes these parameters:
16230 * - method: GET/POST/...
16231 * - uri: full URI with protocol, port, path and query string
16232 * - extra_params: any extra parameters (that are passed in the POST data),
16233 * can be an object or a from-urlencoded string.
16235 * Returned function returns full OAuth header with "OAuth" string in it.
16238 ohauth.headerGenerator = function(options) {
16239 options = options || {};
16240 var consumer_key = options.consumer_key || '',
16241 consumer_secret = options.consumer_secret || '',
16242 signature_method = options.signature_method || 'HMAC-SHA1',
16243 version = options.version || '1.0',
16244 token = options.token || '';
16246 return function(method, uri, extra_params) {
16247 method = method.toUpperCase();
16248 if (typeof extra_params === 'string' && extra_params.length > 0) {
16249 extra_params = ohauth.stringQs(extra_params);
16252 var uri_parts = uri.split('?', 2),
16253 base_uri = uri_parts[0];
16255 var query_params = uri_parts.length === 2 ?
16256 ohauth.stringQs(uri_parts[1]) : {};
16258 var oauth_params = {
16259 oauth_consumer_key: consumer_key,
16260 oauth_signature_method: signature_method,
16261 oauth_version: version,
16262 oauth_timestamp: ohauth.timestamp(),
16263 oauth_nonce: ohauth.nonce()
16266 if (token) oauth_params.oauth_token = token;
16268 var all_params = xtend({}, oauth_params, query_params, extra_params),
16269 base_str = ohauth.baseString(method, base_uri, all_params);
16271 oauth_params.oauth_signature = ohauth.signature(consumer_secret, token, base_str);
16273 return 'OAuth ' + ohauth.authHeader(oauth_params);
16277 module.exports = ohauth;
16279 },{"jshashes":7,"xtend":4}],6:[function(require,module,exports){
16280 module.exports = Object.keys || require('./shim');
16283 },{"./shim":8}],8:[function(require,module,exports){
16287 // modified from https://github.com/kriskowal/es5-shim
16288 var has = Object.prototype.hasOwnProperty,
16289 is = require('is'),
16290 forEach = require('foreach'),
16291 hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'),
16298 "propertyIsEnumerable",
16303 keysShim = function keys(object) {
16304 if (!is.object(object) && !is.array(object)) {
16305 throw new TypeError("Object.keys called on a non-object");
16308 var name, theKeys = [];
16309 for (name in object) {
16310 if (has.call(object, name)) {
16311 theKeys.push(name);
16315 if (hasDontEnumBug) {
16316 forEach(dontEnums, function (dontEnum) {
16317 if (has.call(object, dontEnum)) {
16318 theKeys.push(dontEnum);
16325 module.exports = keysShim;
16329 },{"is":9,"foreach":10}],9:[function(require,module,exports){
16333 * the definitive JavaScript type testing library
16335 * @copyright 2013 Enrico Marino
16339 var objProto = Object.prototype;
16340 var owns = objProto.hasOwnProperty;
16341 var toString = objProto.toString;
16342 var isActualNaN = function (value) {
16343 return value !== value;
16345 var NON_HOST_TYPES = {
16356 var is = module.exports = {};
16364 * Test if `value` is a type of `type`.
16366 * @param {Mixed} value value to test
16367 * @param {String} type type
16368 * @return {Boolean} true if `value` is a type of `type`, false otherwise
16373 is.type = function (value, type) {
16374 return typeof value === type;
16379 * Test if `value` is defined.
16381 * @param {Mixed} value value to test
16382 * @return {Boolean} true if 'value' is defined, false otherwise
16386 is.defined = function (value) {
16387 return value !== undefined;
16392 * Test if `value` is empty.
16394 * @param {Mixed} value value to test
16395 * @return {Boolean} true if `value` is empty, false otherwise
16399 is.empty = function (value) {
16400 var type = toString.call(value);
16403 if ('[object Array]' === type || '[object Arguments]' === type) {
16404 return value.length === 0;
16407 if ('[object Object]' === type) {
16408 for (key in value) if (owns.call(value, key)) return false;
16412 if ('[object String]' === type) {
16413 return '' === value;
16421 * Test if `value` is equal to `other`.
16423 * @param {Mixed} value value to test
16424 * @param {Mixed} other value to compare with
16425 * @return {Boolean} true if `value` is equal to `other`, false otherwise
16428 is.equal = function (value, other) {
16429 var type = toString.call(value)
16432 if (type !== toString.call(other)) {
16436 if ('[object Object]' === type) {
16437 for (key in value) {
16438 if (!is.equal(value[key], other[key])) {
16445 if ('[object Array]' === type) {
16446 key = value.length;
16447 if (key !== other.length) {
16451 if (!is.equal(value[key], other[key])) {
16458 if ('[object Function]' === type) {
16459 return value.prototype === other.prototype;
16462 if ('[object Date]' === type) {
16463 return value.getTime() === other.getTime();
16466 return value === other;
16471 * Test if `value` is hosted by `host`.
16473 * @param {Mixed} value to test
16474 * @param {Mixed} host host to test with
16475 * @return {Boolean} true if `value` is hosted by `host`, false otherwise
16479 is.hosted = function (value, host) {
16480 var type = typeof host[value];
16481 return type === 'object' ? !!host[value] : !NON_HOST_TYPES[type];
16486 * Test if `value` is an instance of `constructor`.
16488 * @param {Mixed} value value to test
16489 * @return {Boolean} true if `value` is an instance of `constructor`
16493 is.instance = is['instanceof'] = function (value, constructor) {
16494 return value instanceof constructor;
16499 * Test if `value` is null.
16501 * @param {Mixed} value value to test
16502 * @return {Boolean} true if `value` is null, false otherwise
16506 is['null'] = function (value) {
16507 return value === null;
16512 * Test if `value` is undefined.
16514 * @param {Mixed} value value to test
16515 * @return {Boolean} true if `value` is undefined, false otherwise
16519 is.undefined = function (value) {
16520 return value === undefined;
16529 * Test if `value` is an arguments object.
16531 * @param {Mixed} value value to test
16532 * @return {Boolean} true if `value` is an arguments object, false otherwise
16536 is.arguments = function (value) {
16537 var isStandardArguments = '[object Arguments]' === toString.call(value);
16538 var isOldArguments = !is.array(value) && is.arraylike(value) && is.object(value) && is.fn(value.callee);
16539 return isStandardArguments || isOldArguments;
16548 * Test if 'value' is an array.
16550 * @param {Mixed} value value to test
16551 * @return {Boolean} true if `value` is an array, false otherwise
16555 is.array = function (value) {
16556 return '[object Array]' === toString.call(value);
16560 * is.arguments.empty
16561 * Test if `value` is an empty arguments object.
16563 * @param {Mixed} value value to test
16564 * @return {Boolean} true if `value` is an empty arguments object, false otherwise
16567 is.arguments.empty = function (value) {
16568 return is.arguments(value) && value.length === 0;
16573 * Test if `value` is an empty array.
16575 * @param {Mixed} value value to test
16576 * @return {Boolean} true if `value` is an empty array, false otherwise
16579 is.array.empty = function (value) {
16580 return is.array(value) && value.length === 0;
16585 * Test if `value` is an arraylike object.
16587 * @param {Mixed} value value to test
16588 * @return {Boolean} true if `value` is an arguments object, false otherwise
16592 is.arraylike = function (value) {
16593 return !!value && !is.boolean(value)
16594 && owns.call(value, 'length')
16595 && isFinite(value.length)
16596 && is.number(value.length)
16597 && value.length >= 0;
16606 * Test if `value` is a boolean.
16608 * @param {Mixed} value value to test
16609 * @return {Boolean} true if `value` is a boolean, false otherwise
16613 is.boolean = function (value) {
16614 return '[object Boolean]' === toString.call(value);
16619 * Test if `value` is false.
16621 * @param {Mixed} value value to test
16622 * @return {Boolean} true if `value` is false, false otherwise
16626 is['false'] = function (value) {
16627 return is.boolean(value) && (value === false || value.valueOf() === false);
16632 * Test if `value` is true.
16634 * @param {Mixed} value value to test
16635 * @return {Boolean} true if `value` is true, false otherwise
16639 is['true'] = function (value) {
16640 return is.boolean(value) && (value === true || value.valueOf() === true);
16649 * Test if `value` is a date.
16651 * @param {Mixed} value value to test
16652 * @return {Boolean} true if `value` is a date, false otherwise
16656 is.date = function (value) {
16657 return '[object Date]' === toString.call(value);
16666 * Test if `value` is an html element.
16668 * @param {Mixed} value value to test
16669 * @return {Boolean} true if `value` is an HTML Element, false otherwise
16673 is.element = function (value) {
16674 return value !== undefined
16675 && typeof HTMLElement !== 'undefined'
16676 && value instanceof HTMLElement
16677 && value.nodeType === 1;
16686 * Test if `value` is an error object.
16688 * @param {Mixed} value value to test
16689 * @return {Boolean} true if `value` is an error object, false otherwise
16693 is.error = function (value) {
16694 return '[object Error]' === toString.call(value);
16702 * is.fn / is.function (deprecated)
16703 * Test if `value` is a function.
16705 * @param {Mixed} value value to test
16706 * @return {Boolean} true if `value` is a function, false otherwise
16710 is.fn = is['function'] = function (value) {
16711 var isAlert = typeof window !== 'undefined' && value === window.alert;
16712 return isAlert || '[object Function]' === toString.call(value);
16721 * Test if `value` is a number.
16723 * @param {Mixed} value value to test
16724 * @return {Boolean} true if `value` is a number, false otherwise
16728 is.number = function (value) {
16729 return '[object Number]' === toString.call(value);
16734 * Test if `value` is positive or negative infinity.
16736 * @param {Mixed} value value to test
16737 * @return {Boolean} true if `value` is positive or negative Infinity, false otherwise
16740 is.infinite = function (value) {
16741 return value === Infinity || value === -Infinity;
16746 * Test if `value` is a decimal number.
16748 * @param {Mixed} value value to test
16749 * @return {Boolean} true if `value` is a decimal number, false otherwise
16753 is.decimal = function (value) {
16754 return is.number(value) && !isActualNaN(value) && value % 1 !== 0;
16759 * Test if `value` is divisible by `n`.
16761 * @param {Number} value value to test
16762 * @param {Number} n dividend
16763 * @return {Boolean} true if `value` is divisible by `n`, false otherwise
16767 is.divisibleBy = function (value, n) {
16768 var isDividendInfinite = is.infinite(value);
16769 var isDivisorInfinite = is.infinite(n);
16770 var isNonZeroNumber = is.number(value) && !isActualNaN(value) && is.number(n) && !isActualNaN(n) && n !== 0;
16771 return isDividendInfinite || isDivisorInfinite || (isNonZeroNumber && value % n === 0);
16776 * Test if `value` is an integer.
16778 * @param value to test
16779 * @return {Boolean} true if `value` is an integer, false otherwise
16783 is.int = function (value) {
16784 return is.number(value) && !isActualNaN(value) && value % 1 === 0;
16789 * Test if `value` is greater than 'others' values.
16791 * @param {Number} value value to test
16792 * @param {Array} others values to compare with
16793 * @return {Boolean} true if `value` is greater than `others` values
16797 is.maximum = function (value, others) {
16798 if (isActualNaN(value)) {
16799 throw new TypeError('NaN is not a valid value');
16800 } else if (!is.arraylike(others)) {
16801 throw new TypeError('second argument must be array-like');
16803 var len = others.length;
16805 while (--len >= 0) {
16806 if (value < others[len]) {
16816 * Test if `value` is less than `others` values.
16818 * @param {Number} value value to test
16819 * @param {Array} others values to compare with
16820 * @return {Boolean} true if `value` is less than `others` values
16824 is.minimum = function (value, others) {
16825 if (isActualNaN(value)) {
16826 throw new TypeError('NaN is not a valid value');
16827 } else if (!is.arraylike(others)) {
16828 throw new TypeError('second argument must be array-like');
16830 var len = others.length;
16832 while (--len >= 0) {
16833 if (value > others[len]) {
16843 * Test if `value` is not a number.
16845 * @param {Mixed} value value to test
16846 * @return {Boolean} true if `value` is not a number, false otherwise
16850 is.nan = function (value) {
16851 return !is.number(value) || value !== value;
16856 * Test if `value` is an even number.
16858 * @param {Number} value value to test
16859 * @return {Boolean} true if `value` is an even number, false otherwise
16863 is.even = function (value) {
16864 return is.infinite(value) || (is.number(value) && value === value && value % 2 === 0);
16869 * Test if `value` is an odd number.
16871 * @param {Number} value value to test
16872 * @return {Boolean} true if `value` is an odd number, false otherwise
16876 is.odd = function (value) {
16877 return is.infinite(value) || (is.number(value) && value === value && value % 2 !== 0);
16882 * Test if `value` is greater than or equal to `other`.
16884 * @param {Number} value value to test
16885 * @param {Number} other value to compare with
16886 * @return {Boolean}
16890 is.ge = function (value, other) {
16891 if (isActualNaN(value) || isActualNaN(other)) {
16892 throw new TypeError('NaN is not a valid value');
16894 return !is.infinite(value) && !is.infinite(other) && value >= other;
16899 * Test if `value` is greater than `other`.
16901 * @param {Number} value value to test
16902 * @param {Number} other value to compare with
16903 * @return {Boolean}
16907 is.gt = function (value, other) {
16908 if (isActualNaN(value) || isActualNaN(other)) {
16909 throw new TypeError('NaN is not a valid value');
16911 return !is.infinite(value) && !is.infinite(other) && value > other;
16916 * Test if `value` is less than or equal to `other`.
16918 * @param {Number} value value to test
16919 * @param {Number} other value to compare with
16920 * @return {Boolean} if 'value' is less than or equal to 'other'
16924 is.le = function (value, other) {
16925 if (isActualNaN(value) || isActualNaN(other)) {
16926 throw new TypeError('NaN is not a valid value');
16928 return !is.infinite(value) && !is.infinite(other) && value <= other;
16933 * Test if `value` is less than `other`.
16935 * @param {Number} value value to test
16936 * @param {Number} other value to compare with
16937 * @return {Boolean} if `value` is less than `other`
16941 is.lt = function (value, other) {
16942 if (isActualNaN(value) || isActualNaN(other)) {
16943 throw new TypeError('NaN is not a valid value');
16945 return !is.infinite(value) && !is.infinite(other) && value < other;
16950 * Test if `value` is within `start` and `finish`.
16952 * @param {Number} value value to test
16953 * @param {Number} start lower bound
16954 * @param {Number} finish upper bound
16955 * @return {Boolean} true if 'value' is is within 'start' and 'finish'
16958 is.within = function (value, start, finish) {
16959 if (isActualNaN(value) || isActualNaN(start) || isActualNaN(finish)) {
16960 throw new TypeError('NaN is not a valid value');
16961 } else if (!is.number(value) || !is.number(start) || !is.number(finish)) {
16962 throw new TypeError('all arguments must be numbers');
16964 var isAnyInfinite = is.infinite(value) || is.infinite(start) || is.infinite(finish);
16965 return isAnyInfinite || (value >= start && value <= finish);
16974 * Test if `value` is an object.
16976 * @param {Mixed} value value to test
16977 * @return {Boolean} true if `value` is an object, false otherwise
16981 is.object = function (value) {
16982 return value && '[object Object]' === toString.call(value);
16987 * Test if `value` is a hash - a plain object literal.
16989 * @param {Mixed} value value to test
16990 * @return {Boolean} true if `value` is a hash, false otherwise
16994 is.hash = function (value) {
16995 return is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval;
17004 * Test if `value` is a regular expression.
17006 * @param {Mixed} value value to test
17007 * @return {Boolean} true if `value` is a regexp, false otherwise
17011 is.regexp = function (value) {
17012 return '[object RegExp]' === toString.call(value);
17021 * Test if `value` is a string.
17023 * @param {Mixed} value value to test
17024 * @return {Boolean} true if 'value' is a string, false otherwise
17028 is.string = function (value) {
17029 return '[object String]' === toString.call(value);
17033 },{}],10:[function(require,module,exports){
17035 var hasOwn = Object.prototype.hasOwnProperty;
17036 var toString = Object.prototype.toString;
17038 module.exports = function forEach (obj, fn, ctx) {
17039 if (toString.call(fn) !== '[object Function]') {
17040 throw new TypeError('iterator must be a function');
17042 var l = obj.length;
17044 for (var i = 0; i < l; i++) {
17045 fn.call(ctx, obj[i], i, obj);
17048 for (var k in obj) {
17049 if (hasOwn.call(obj, k)) {
17050 fn.call(ctx, obj[k], k, obj);
17061 (c) 2013, Vladimir Agafonkin
17062 RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
17063 https://github.com/mourner/rbush
17066 (function () { 'use strict';
17068 function rbush(maxEntries, format) {
17070 // jshint newcap: false, validthis: true
17071 if (!(this instanceof rbush)) return new rbush(maxEntries, format);
17073 // max entries in a node is 9 by default; min node fill is 40% for best performance
17074 this._maxEntries = Math.max(4, maxEntries || 9);
17075 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
17078 this._initFormat(format);
17084 rbush.prototype = {
17087 return this._all(this.data, []);
17090 search: function (bbox) {
17092 var node = this.data,
17094 toBBox = this.toBBox;
17096 if (!intersects(bbox, node.bbox)) return result;
17098 var nodesToSearch = [],
17099 i, len, child, childBBox;
17102 for (i = 0, len = node.children.length; i < len; i++) {
17104 child = node.children[i];
17105 childBBox = node.leaf ? toBBox(child) : child.bbox;
17107 if (intersects(bbox, childBBox)) {
17108 if (node.leaf) result.push(child);
17109 else if (contains(bbox, childBBox)) this._all(child, result);
17110 else nodesToSearch.push(child);
17113 node = nodesToSearch.pop();
17119 load: function (data) {
17120 if (!(data && data.length)) return this;
17122 if (data.length < this._minEntries) {
17123 for (var i = 0, len = data.length; i < len; i++) {
17124 this.insert(data[i]);
17129 // recursively build the tree with the given data from stratch using OMT algorithm
17130 var node = this._build(data.slice(), 0, data.length - 1, 0);
17132 if (!this.data.children.length) {
17133 // save as is if tree is empty
17136 } else if (this.data.height === node.height) {
17137 // split root if trees have the same height
17138 this._splitRoot(this.data, node);
17141 if (this.data.height < node.height) {
17142 // swap trees if inserted one is bigger
17143 var tmpNode = this.data;
17148 // insert the small tree into the large tree at appropriate level
17149 this._insert(node, this.data.height - node.height - 1, true);
17155 insert: function (item) {
17156 if (item) this._insert(item, this.data.height - 1);
17160 clear: function () {
17170 remove: function (item) {
17171 if (!item) return this;
17173 var node = this.data,
17174 bbox = this.toBBox(item),
17177 i, parent, index, goingUp;
17179 // depth-first iterative tree traversal
17180 while (node || path.length) {
17182 if (!node) { // go up
17184 parent = path[path.length - 1];
17189 if (node.leaf) { // check current node
17190 index = node.children.indexOf(item);
17192 if (index !== -1) {
17193 // item found, remove the item and condense tree upwards
17194 node.children.splice(index, 1);
17196 this._condense(path);
17201 if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
17206 node = node.children[0];
17208 } else if (parent) { // go right
17210 node = parent.children[i];
17213 } else node = null; // nothing found
17219 toBBox: function (item) { return item; },
17221 compareMinX: function (a, b) { return a[0] - b[0]; },
17222 compareMinY: function (a, b) { return a[1] - b[1]; },
17224 toJSON: function () { return this.data; },
17226 fromJSON: function (data) {
17231 _all: function (node, result) {
17232 var nodesToSearch = [];
17234 if (node.leaf) result.push.apply(result, node.children);
17235 else nodesToSearch.push.apply(nodesToSearch, node.children);
17237 node = nodesToSearch.pop();
17242 _build: function (items, left, right, level, height) {
17244 var N = right - left + 1,
17245 M = this._maxEntries,
17250 children: items.slice(left, right + 1),
17255 calcBBox(node, this.toBBox);
17260 // target height of the bulk-loaded tree
17261 height = Math.ceil(Math.log(N) / Math.log(M));
17263 // target number of root entries to maximize storage utilization
17264 M = Math.ceil(N / Math.pow(M, height - 1));
17267 // TODO eliminate recursion?
17275 var N2 = Math.ceil(N / M),
17276 N1 = N2 * Math.ceil(Math.sqrt(M)),
17277 i, j, right2, childNode;
17279 // split the items into M mostly square tiles
17280 for (i = left; i <= right; i += N1) {
17282 if (i + N1 <= right) partitionSort(items, i, right, i + N1, this.compareMinX);
17283 right2 = Math.min(i + N1 - 1, right);
17285 for (j = i; j <= right2; j += N2) {
17287 if (j + N2 <= right2) partitionSort(items, j, right2, j + N2, this.compareMinY);
17289 // pack each entry recursively
17290 childNode = this._build(items, j, Math.min(j + N2 - 1, right2), level + 1, height - 1);
17291 node.children.push(childNode);
17295 calcBBox(node, this.toBBox);
17300 _chooseSubtree: function (bbox, node, level, path) {
17302 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
17307 if (node.leaf || path.length - 1 === level) break;
17309 minArea = minEnlargement = Infinity;
17311 for (i = 0, len = node.children.length; i < len; i++) {
17312 child = node.children[i];
17313 area = bboxArea(child.bbox);
17314 enlargement = enlargedArea(bbox, child.bbox) - area;
17316 // choose entry with the least area enlargement
17317 if (enlargement < minEnlargement) {
17318 minEnlargement = enlargement;
17319 minArea = area < minArea ? area : minArea;
17320 targetNode = child;
17322 } else if (enlargement === minEnlargement) {
17323 // otherwise choose one with the smallest area
17324 if (area < minArea) {
17326 targetNode = child;
17337 _insert: function (item, level, isNode) {
17339 var toBBox = this.toBBox,
17340 bbox = isNode ? item.bbox : toBBox(item),
17343 // find the best node for accommodating the item, saving all nodes along the path too
17344 var node = this._chooseSubtree(bbox, this.data, level, insertPath);
17346 // put the item into the node
17347 node.children.push(item);
17348 extend(node.bbox, bbox);
17350 // split on node overflow; propagate upwards if necessary
17351 while (level >= 0) {
17352 if (insertPath[level].children.length > this._maxEntries) {
17353 this._split(insertPath, level);
17358 // adjust bboxes along the insertion path
17359 this._adjustParentBBoxes(bbox, insertPath, level);
17362 // split overflowed node into two
17363 _split: function (insertPath, level) {
17365 var node = insertPath[level],
17366 M = node.children.length,
17367 m = this._minEntries;
17369 this._chooseSplitAxis(node, m, M);
17372 children: node.children.splice(this._chooseSplitIndex(node, m, M)),
17373 height: node.height
17376 if (node.leaf) newNode.leaf = true;
17378 calcBBox(node, this.toBBox);
17379 calcBBox(newNode, this.toBBox);
17381 if (level) insertPath[level - 1].children.push(newNode);
17382 else this._splitRoot(node, newNode);
17385 _splitRoot: function (node, newNode) {
17388 children: [node, newNode],
17389 height: node.height + 1
17391 calcBBox(this.data, this.toBBox);
17394 _chooseSplitIndex: function (node, m, M) {
17396 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
17398 minOverlap = minArea = Infinity;
17400 for (i = m; i <= M - m; i++) {
17401 bbox1 = distBBox(node, 0, i, this.toBBox);
17402 bbox2 = distBBox(node, i, M, this.toBBox);
17404 overlap = intersectionArea(bbox1, bbox2);
17405 area = bboxArea(bbox1) + bboxArea(bbox2);
17407 // choose distribution with minimum overlap
17408 if (overlap < minOverlap) {
17409 minOverlap = overlap;
17412 minArea = area < minArea ? area : minArea;
17414 } else if (overlap === minOverlap) {
17415 // otherwise choose distribution with minimum area
17416 if (area < minArea) {
17426 // sorts node children by the best axis for split
17427 _chooseSplitAxis: function (node, m, M) {
17429 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
17430 compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
17431 xMargin = this._allDistMargin(node, m, M, compareMinX),
17432 yMargin = this._allDistMargin(node, m, M, compareMinY);
17434 // if total distributions margin value is minimal for x, sort by minX,
17435 // otherwise it's already sorted by minY
17436 if (xMargin < yMargin) node.children.sort(compareMinX);
17439 // total margin of all possible split distributions where each node is at least m full
17440 _allDistMargin: function (node, m, M, compare) {
17442 node.children.sort(compare);
17444 var toBBox = this.toBBox,
17445 leftBBox = distBBox(node, 0, m, toBBox),
17446 rightBBox = distBBox(node, M - m, M, toBBox),
17447 margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
17450 for (i = m; i < M - m; i++) {
17451 child = node.children[i];
17452 extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
17453 margin += bboxMargin(leftBBox);
17456 for (i = M - m - 1; i >= m; i--) {
17457 child = node.children[i];
17458 extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
17459 margin += bboxMargin(rightBBox);
17465 _adjustParentBBoxes: function (bbox, path, level) {
17466 // adjust bboxes along the given tree path
17467 for (var i = level; i >= 0; i--) {
17468 extend(path[i].bbox, bbox);
17472 _condense: function (path) {
17473 // go through the path, removing empty nodes and updating bboxes
17474 for (var i = path.length - 1, siblings; i >= 0; i--) {
17475 if (path[i].children.length === 0) {
17477 siblings = path[i - 1].children;
17478 siblings.splice(siblings.indexOf(path[i]), 1);
17480 } else this.clear();
17482 } else calcBBox(path[i], this.toBBox);
17486 _initFormat: function (format) {
17487 // data format (minX, minY, maxX, maxY accessors)
17489 // uses eval-type function compilation instead of just accepting a toBBox function
17490 // because the algorithms are very sensitive to sorting functions performance,
17491 // so they should be dead simple and without inner calls
17493 // jshint evil: true
17495 var compareArr = ['return a', ' - b', ';'];
17497 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
17498 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
17500 this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
17504 // calculate node's bbox from bboxes of its children
17505 function calcBBox(node, toBBox) {
17506 node.bbox = distBBox(node, 0, node.children.length, toBBox);
17509 // min bounding rectangle of node children from k to p-1
17510 function distBBox(node, k, p, toBBox) {
17511 var bbox = empty();
17513 for (var i = k, child; i < p; i++) {
17514 child = node.children[i];
17515 extend(bbox, node.leaf ? toBBox(child) : child.bbox);
17522 function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
17524 function extend(a, b) {
17525 a[0] = Math.min(a[0], b[0]);
17526 a[1] = Math.min(a[1], b[1]);
17527 a[2] = Math.max(a[2], b[2]);
17528 a[3] = Math.max(a[3], b[3]);
17532 function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
17533 function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
17535 function bboxArea(a) { return (a[2] - a[0]) * (a[3] - a[1]); }
17536 function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
17538 function enlargedArea(a, b) {
17539 return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
17540 (Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
17543 function intersectionArea (a, b) {
17544 var minX = Math.max(a[0], b[0]),
17545 minY = Math.max(a[1], b[1]),
17546 maxX = Math.min(a[2], b[2]),
17547 maxY = Math.min(a[3], b[3]);
17549 return Math.max(0, maxX - minX) *
17550 Math.max(0, maxY - minY);
17553 function contains(a, b) {
17554 return a[0] <= b[0] &&
17560 function intersects (a, b) {
17561 return b[0] <= a[2] &&
17568 function partitionSort(arr, left, right, k, compare) {
17572 pivot = Math.floor((left + right) / 2);
17573 pivot = partition(arr, left, right, pivot, compare);
17575 if (k === pivot) break;
17576 else if (k < pivot) right = pivot - 1;
17577 else left = pivot + 1;
17580 partition(arr, left, right, k, compare);
17583 function partition(arr, left, right, pivot, compare) {
17585 value = arr[pivot];
17587 swap(arr, pivot, right);
17589 for (var i = left; i < right; i++) {
17590 if (compare(arr[i], value) < 0) {
17595 swap(arr, right, k);
17600 function swap(arr, i, j) {
17607 // export as AMD/CommonJS module or global variable
17608 if (typeof define === 'function' && define.amd) define(function() { return rbush; });
17609 else if (typeof module !== 'undefined') module.exports = rbush;
17610 else if (typeof self !== 'undefined') self.rbush = rbush;
17611 else window.rbush = rbush;
17613 })();(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;
17614 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){
17615 module.exports = element;
17616 module.exports.pair = pair;
17617 module.exports.format = format;
17618 module.exports.formatPair = formatPair;
17620 function element(x, dims) {
17621 return search(x, dims).val;
17624 function formatPair(x) {
17625 return format(x.lat, 'lat') + ' ' + format(x.lon, 'lon');
17628 // Is 0 North or South?
17629 function format(x, dim) {
17634 dir = dirs[x >= 0 ? 0 : 1],
17636 whole = Math.floor(abs),
17637 fraction = abs - whole,
17638 fractionMinutes = fraction * 60,
17639 minutes = Math.floor(fractionMinutes),
17640 seconds = Math.floor((fractionMinutes - minutes) * 60);
17642 return whole + '° ' +
17643 (minutes ? minutes + "' " : '') +
17644 (seconds ? seconds + '" ' : '') + dir;
17647 function search(x, dims, r) {
17648 if (!dims) dims = 'NSEW';
17649 if (typeof x !== 'string') return { val: null, regex: r };
17650 r = r || /[\s\,]*([\-|\—|\―]?[0-9.]+)°? *(?:([0-9.]+)['’′‘] *)?(?:([0-9.]+)(?:''|"|”|″) *)?([NSEW])?/gi;
17652 if (!m) return { val: null, regex: r };
17653 else if (m[4] && dims.indexOf(m[4]) === -1) return { val: null, regex: r };
17655 val: (((m[1]) ? parseFloat(m[1]) : 0) +
17656 ((m[2] ? parseFloat(m[2]) / 60 : 0)) +
17657 ((m[3] ? parseFloat(m[3]) / 3600 : 0))) *
17658 ((m[4] && m[4] === 'S' || m[4] === 'W') ? -1 : 1),
17665 function pair(x, dims) {
17667 var one = search(x, dims);
17668 if (one.val === null) return null;
17669 var two = search(x, dims, one.regex);
17670 if (two.val === null) return null;
17671 // null if one/two are not contiguous.
17672 if (one.raw + two.raw !== x) return null;
17673 if (one.dim) return swapdim(one.val, two.val, one.dim);
17674 else return [one.val, two.val];
17677 function swapdim(a, b, dim) {
17678 if (dim == 'N' || dim == 'S') return [a, b];
17679 if (dim == 'W' || dim == 'E') return [b, a];
17685 ;toGeoJSON = (function() {
17688 var removeSpace = (/\s*/g),
17689 trimSpace = (/^\s*|\s*$/g),
17690 splitSpace = (/\s+/);
17691 // generate a short, numeric hash of a string
17692 function okhash(x) {
17693 if (!x || !x.length) return 0;
17694 for (var i = 0, h = 0; i < x.length; i++) {
17695 h = ((h << 5) - h) + x.charCodeAt(i) | 0;
17698 // all Y children of X
17699 function get(x, y) { return x.getElementsByTagName(y); }
17700 function attr(x, y) { return x.getAttribute(y); }
17701 function attrf(x, y) { return parseFloat(attr(x, y)); }
17702 // one Y child of X, if any, otherwise null
17703 function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
17704 // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
17705 function norm(el) { if (el.normalize) { el.normalize(); } return el; }
17706 // cast array x into numbers
17707 function numarray(x) {
17708 for (var j = 0, o = []; j < x.length; j++) o[j] = parseFloat(x[j]);
17711 function clean(x) {
17713 for (var i in x) if (x[i]) o[i] = x[i];
17716 // get the content of a text node, if any
17717 function nodeVal(x) { if (x) {norm(x);} return x && x.firstChild && x.firstChild.nodeValue; }
17718 // get one coordinate from a coordinate array, if any
17719 function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
17720 // get all coordinates from a coordinate array as [[],[]]
17721 function coord(v) {
17722 var coords = v.replace(trimSpace, '').split(splitSpace),
17724 for (var i = 0; i < coords.length; i++) {
17725 o.push(coord1(coords[i]));
17729 function coordPair(x) { return [attrf(x, 'lon'), attrf(x, 'lat')]; }
17731 // create a new feature collection parent object
17734 type: 'FeatureCollection',
17739 var styleSupport = false;
17740 if (typeof XMLSerializer !== 'undefined') {
17741 var serializer = new XMLSerializer();
17742 styleSupport = true;
17744 function xml2str(str) { return serializer.serializeToString(str); }
17747 kml: function(doc, o) {
17751 // styleindex keeps track of hashed styles in order to match features
17753 // atomic geospatial types supported by KML - MultiGeometry is
17754 // handled separately
17755 geotypes = ['Polygon', 'LineString', 'Point', 'Track'],
17756 // all root placemarks in the file
17757 placemarks = get(doc, 'Placemark'),
17758 styles = get(doc, 'Style');
17760 if (styleSupport) for (var k = 0; k < styles.length; k++) {
17761 styleIndex['#' + attr(styles[k], 'id')] = okhash(xml2str(styles[k])).toString(16);
17763 for (var j = 0; j < placemarks.length; j++) {
17764 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
17766 function gxCoord(v) { return numarray(v.split(' ')); }
17767 function gxCoords(root) {
17768 var elems = get(root, 'coord', 'gx'), coords = [];
17769 for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
17772 function getGeometry(root) {
17773 var geomNode, geomNodes, i, j, k, geoms = [];
17774 if (get1(root, 'MultiGeometry')) return getGeometry(get1(root, 'MultiGeometry'));
17775 if (get1(root, 'MultiTrack')) return getGeometry(get1(root, 'MultiTrack'));
17776 for (i = 0; i < geotypes.length; i++) {
17777 geomNodes = get(root, geotypes[i]);
17779 for (j = 0; j < geomNodes.length; j++) {
17780 geomNode = geomNodes[j];
17781 if (geotypes[i] == 'Point') {
17784 coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
17786 } else if (geotypes[i] == 'LineString') {
17788 type: 'LineString',
17789 coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
17791 } else if (geotypes[i] == 'Polygon') {
17792 var rings = get(geomNode, 'LinearRing'),
17794 for (k = 0; k < rings.length; k++) {
17795 coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
17799 coordinates: coords
17801 } else if (geotypes[i] == 'Track') {
17803 type: 'LineString',
17804 coordinates: gxCoords(geomNode)
17812 function getPlacemark(root) {
17813 var geoms = getGeometry(root), i, properties = {},
17814 name = nodeVal(get1(root, 'name')),
17815 styleUrl = nodeVal(get1(root, 'styleUrl')),
17816 description = nodeVal(get1(root, 'description')),
17817 extendedData = get1(root, 'ExtendedData');
17819 if (!geoms.length) return [];
17820 if (name) properties.name = name;
17821 if (styleUrl && styleIndex[styleUrl]) {
17822 properties.styleUrl = styleUrl;
17823 properties.styleHash = styleIndex[styleUrl];
17825 if (description) properties.description = description;
17826 if (extendedData) {
17827 var datas = get(extendedData, 'Data'),
17828 simpleDatas = get(extendedData, 'SimpleData');
17830 for (i = 0; i < datas.length; i++) {
17831 properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
17833 for (i = 0; i < simpleDatas.length; i++) {
17834 properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
17839 geometry: (geoms.length === 1) ? geoms[0] : {
17840 type: 'GeometryCollection',
17843 properties: properties
17848 gpx: function(doc, o) {
17850 tracks = get(doc, 'trk'),
17851 routes = get(doc, 'rte'),
17852 waypoints = get(doc, 'wpt'),
17853 // a feature collection
17855 for (i = 0; i < tracks.length; i++) {
17856 gj.features.push(getLinestring(tracks[i], 'trkpt'));
17858 for (i = 0; i < routes.length; i++) {
17859 gj.features.push(getLinestring(routes[i], 'rtept'));
17861 for (i = 0; i < waypoints.length; i++) {
17862 gj.features.push(getPoint(waypoints[i]));
17864 function getLinestring(node, pointname) {
17865 var j, pts = get(node, pointname), line = [];
17866 for (j = 0; j < pts.length; j++) {
17867 line.push(coordPair(pts[j]));
17871 properties: getProperties(node),
17873 type: 'LineString',
17878 function getPoint(node) {
17879 var prop = getProperties(node);
17880 prop.ele = nodeVal(get1(node, 'ele'));
17881 prop.sym = nodeVal(get1(node, 'sym'));
17887 coordinates: coordPair(node)
17891 function getProperties(node) {
17892 var meta = ['name', 'desc', 'author', 'copyright', 'link',
17893 'time', 'keywords'],
17896 for (k = 0; k < meta.length; k++) {
17897 prop[meta[k]] = nodeVal(get1(node, meta[k]));
17899 return clean(prop);
17907 if (typeof module !== 'undefined') module.exports = toGeoJSON;
17909 * marked - a markdown parser
17910 * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
17911 * https://github.com/chjj/marked
17917 * Block-Level Grammar
17922 code: /^( {4}[^\n]+\n*)+/,
17924 hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17925 heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17927 lheading: /^([^\n]+)\n *(=|-){3,} *\n*/,
17928 blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
17929 list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17930 html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
17931 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17933 paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17937 block.bullet = /(?:[*+-]|\d+\.)/;
17938 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17939 block.item = replace(block.item, 'gm')
17940 (/bull/g, block.bullet)
17943 block.list = replace(block.list)
17944 (/bull/g, block.bullet)
17945 ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)
17948 block._tag = '(?!(?:'
17949 + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17950 + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17951 + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b';
17953 block.html = replace(block.html)
17954 ('comment', /<!--[\s\S]*?-->/)
17955 ('closed', /<(tag)[\s\S]+?<\/\1>/)
17956 ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17957 (/tag/g, block._tag)
17960 block.paragraph = replace(block.paragraph)
17962 ('heading', block.heading)
17963 ('lheading', block.lheading)
17964 ('blockquote', block.blockquote)
17965 ('tag', '<' + block._tag)
17970 * Normal Block Grammar
17973 block.normal = merge({}, block);
17976 * GFM Block Grammar
17979 block.gfm = merge({}, block.normal, {
17980 fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
17984 block.gfm.paragraph = replace(block.paragraph)
17985 ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|')
17989 * GFM + Tables Block Grammar
17992 block.tables = merge({}, block.gfm, {
17993 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17994 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18001 function Lexer(options) {
18003 this.tokens.links = {};
18004 this.options = options || marked.defaults;
18005 this.rules = block.normal;
18007 if (this.options.gfm) {
18008 if (this.options.tables) {
18009 this.rules = block.tables;
18011 this.rules = block.gfm;
18017 * Expose Block Rules
18020 Lexer.rules = block;
18023 * Static Lex Method
18026 Lexer.lex = function(src, options) {
18027 var lexer = new Lexer(options);
18028 return lexer.lex(src);
18035 Lexer.prototype.lex = function(src) {
18037 .replace(/\r\n|\r/g, '\n')
18038 .replace(/\t/g, ' ')
18039 .replace(/\u00a0/g, ' ')
18040 .replace(/\u2424/g, '\n');
18042 return this.token(src, true);
18049 Lexer.prototype.token = function(src, top) {
18050 var src = src.replace(/^ +$/gm, '')
18063 if (cap = this.rules.newline.exec(src)) {
18064 src = src.substring(cap[0].length);
18065 if (cap[0].length > 1) {
18073 if (cap = this.rules.code.exec(src)) {
18074 src = src.substring(cap[0].length);
18075 cap = cap[0].replace(/^ {4}/gm, '');
18078 text: !this.options.pedantic
18079 ? cap.replace(/\n+$/, '')
18086 if (cap = this.rules.fences.exec(src)) {
18087 src = src.substring(cap[0].length);
18097 if (cap = this.rules.heading.exec(src)) {
18098 src = src.substring(cap[0].length);
18101 depth: cap[1].length,
18107 // table no leading pipe (gfm)
18108 if (top && (cap = this.rules.nptable.exec(src))) {
18109 src = src.substring(cap[0].length);
18113 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18114 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18115 cells: cap[3].replace(/\n$/, '').split('\n')
18118 for (i = 0; i < item.align.length; i++) {
18119 if (/^ *-+: *$/.test(item.align[i])) {
18120 item.align[i] = 'right';
18121 } else if (/^ *:-+: *$/.test(item.align[i])) {
18122 item.align[i] = 'center';
18123 } else if (/^ *:-+ *$/.test(item.align[i])) {
18124 item.align[i] = 'left';
18126 item.align[i] = null;
18130 for (i = 0; i < item.cells.length; i++) {
18131 item.cells[i] = item.cells[i].split(/ *\| */);
18134 this.tokens.push(item);
18140 if (cap = this.rules.lheading.exec(src)) {
18141 src = src.substring(cap[0].length);
18144 depth: cap[2] === '=' ? 1 : 2,
18151 if (cap = this.rules.hr.exec(src)) {
18152 src = src.substring(cap[0].length);
18160 if (cap = this.rules.blockquote.exec(src)) {
18161 src = src.substring(cap[0].length);
18164 type: 'blockquote_start'
18167 cap = cap[0].replace(/^ *> ?/gm, '');
18169 // Pass `top` to keep the current
18170 // "toplevel" state. This is exactly
18171 // how markdown.pl works.
18172 this.token(cap, top);
18175 type: 'blockquote_end'
18182 if (cap = this.rules.list.exec(src)) {
18183 src = src.substring(cap[0].length);
18187 type: 'list_start',
18188 ordered: bull.length > 1
18191 // Get each top-level item.
18192 cap = cap[0].match(this.rules.item);
18198 for (; i < l; i++) {
18201 // Remove the list item's bullet
18202 // so it is seen as the next token.
18203 space = item.length;
18204 item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18206 // Outdent whatever the
18207 // list item contains. Hacky.
18208 if (~item.indexOf('\n ')) {
18209 space -= item.length;
18210 item = !this.options.pedantic
18211 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18212 : item.replace(/^ {1,4}/gm, '');
18215 // Determine whether the next list item belongs here.
18216 // Backpedal if it does not belong in this list.
18217 if (this.options.smartLists && i !== l - 1) {
18218 b = block.bullet.exec(cap[i+1])[0];
18219 if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18220 src = cap.slice(i + 1).join('\n') + src;
18225 // Determine whether item is loose or not.
18226 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18227 // for discount behavior.
18228 loose = next || /\n\n(?!\s*$)/.test(item);
18230 next = item[item.length-1] === '\n';
18231 if (!loose) loose = next;
18236 ? 'loose_item_start'
18237 : 'list_item_start'
18241 this.token(item, false);
18244 type: 'list_item_end'
18256 if (cap = this.rules.html.exec(src)) {
18257 src = src.substring(cap[0].length);
18259 type: this.options.sanitize
18262 pre: cap[1] === 'pre' || cap[1] === 'script',
18269 if (top && (cap = this.rules.def.exec(src))) {
18270 src = src.substring(cap[0].length);
18271 this.tokens.links[cap[1].toLowerCase()] = {
18279 if (top && (cap = this.rules.table.exec(src))) {
18280 src = src.substring(cap[0].length);
18284 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18285 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18286 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18289 for (i = 0; i < item.align.length; i++) {
18290 if (/^ *-+: *$/.test(item.align[i])) {
18291 item.align[i] = 'right';
18292 } else if (/^ *:-+: *$/.test(item.align[i])) {
18293 item.align[i] = 'center';
18294 } else if (/^ *:-+ *$/.test(item.align[i])) {
18295 item.align[i] = 'left';
18297 item.align[i] = null;
18301 for (i = 0; i < item.cells.length; i++) {
18302 item.cells[i] = item.cells[i]
18303 .replace(/^ *\| *| *\| *$/g, '')
18307 this.tokens.push(item);
18312 // top-level paragraph
18313 if (top && (cap = this.rules.paragraph.exec(src))) {
18314 src = src.substring(cap[0].length);
18317 text: cap[1][cap[1].length-1] === '\n'
18318 ? cap[1].slice(0, -1)
18325 if (cap = this.rules.text.exec(src)) {
18326 // Top-level should never reach here.
18327 src = src.substring(cap[0].length);
18337 Error('Infinite loop on byte: ' + src.charCodeAt(0));
18341 return this.tokens;
18345 * Inline-Level Grammar
18349 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18350 autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18352 tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18353 link: /^!?\[(inside)\]\(href\)/,
18354 reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18355 nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18356 strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18357 em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18358 code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18359 br: /^ {2,}\n(?!\s*$)/,
18361 text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18364 inline._inside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
18365 inline._href = /\s*<?([^\s]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18367 inline.link = replace(inline.link)
18368 ('inside', inline._inside)
18369 ('href', inline._href)
18372 inline.reflink = replace(inline.reflink)
18373 ('inside', inline._inside)
18377 * Normal Inline Grammar
18380 inline.normal = merge({}, inline);
18383 * Pedantic Inline Grammar
18386 inline.pedantic = merge({}, inline.normal, {
18387 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18388 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18392 * GFM Inline Grammar
18395 inline.gfm = merge({}, inline.normal, {
18396 escape: replace(inline.escape)('])', '~|])')(),
18397 url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18398 del: /^~~(?=\S)([\s\S]*?\S)~~/,
18399 text: replace(inline.text)
18401 ('|', '|https?://|')
18406 * GFM + Line Breaks Inline Grammar
18409 inline.breaks = merge({}, inline.gfm, {
18410 br: replace(inline.br)('{2,}', '*')(),
18411 text: replace(inline.gfm.text)('{2,}', '*')()
18415 * Inline Lexer & Compiler
18418 function InlineLexer(links, options) {
18419 this.options = options || marked.defaults;
18420 this.links = links;
18421 this.rules = inline.normal;
18425 Error('Tokens array requires a `links` property.');
18428 if (this.options.gfm) {
18429 if (this.options.breaks) {
18430 this.rules = inline.breaks;
18432 this.rules = inline.gfm;
18434 } else if (this.options.pedantic) {
18435 this.rules = inline.pedantic;
18440 * Expose Inline Rules
18443 InlineLexer.rules = inline;
18446 * Static Lexing/Compiling Method
18449 InlineLexer.output = function(src, links, options) {
18450 var inline = new InlineLexer(links, options);
18451 return inline.output(src);
18458 InlineLexer.prototype.output = function(src) {
18467 if (cap = this.rules.escape.exec(src)) {
18468 src = src.substring(cap[0].length);
18474 if (cap = this.rules.autolink.exec(src)) {
18475 src = src.substring(cap[0].length);
18476 if (cap[2] === '@') {
18477 text = cap[1][6] === ':'
18478 ? this.mangle(cap[1].substring(7))
18479 : this.mangle(cap[1]);
18480 href = this.mangle('mailto:') + text;
18482 text = escape(cap[1]);
18494 if (cap = this.rules.url.exec(src)) {
18495 src = src.substring(cap[0].length);
18496 text = escape(cap[1]);
18507 if (cap = this.rules.tag.exec(src)) {
18508 src = src.substring(cap[0].length);
18509 out += this.options.sanitize
18516 if (cap = this.rules.link.exec(src)) {
18517 src = src.substring(cap[0].length);
18518 out += this.outputLink(cap, {
18526 if ((cap = this.rules.reflink.exec(src))
18527 || (cap = this.rules.nolink.exec(src))) {
18528 src = src.substring(cap[0].length);
18529 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18530 link = this.links[link.toLowerCase()];
18531 if (!link || !link.href) {
18533 src = cap[0].substring(1) + src;
18536 out += this.outputLink(cap, link);
18541 if (cap = this.rules.strong.exec(src)) {
18542 src = src.substring(cap[0].length);
18544 + this.output(cap[2] || cap[1])
18550 if (cap = this.rules.em.exec(src)) {
18551 src = src.substring(cap[0].length);
18553 + this.output(cap[2] || cap[1])
18559 if (cap = this.rules.code.exec(src)) {
18560 src = src.substring(cap[0].length);
18562 + escape(cap[2], true)
18568 if (cap = this.rules.br.exec(src)) {
18569 src = src.substring(cap[0].length);
18575 if (cap = this.rules.del.exec(src)) {
18576 src = src.substring(cap[0].length);
18578 + this.output(cap[1])
18584 if (cap = this.rules.text.exec(src)) {
18585 src = src.substring(cap[0].length);
18586 out += escape(cap[0]);
18592 Error('Infinite loop on byte: ' + src.charCodeAt(0));
18603 InlineLexer.prototype.outputLink = function(cap, link) {
18604 if (cap[0][0] !== '!') {
18606 + escape(link.href)
18610 + escape(link.title)
18614 + this.output(cap[1])
18617 return '<img src="'
18618 + escape(link.href)
18624 + escape(link.title)
18632 * Smartypants Transformations
18635 InlineLexer.prototype.smartypants = function(text) {
18636 if (!this.options.smartypants) return text;
18638 .replace(/--/g, '—')
18639 .replace(/'([^']*)'/g, '‘$1’')
18640 .replace(/"([^"]*)"/g, '“$1”')
18641 .replace(/\.{3}/g, '…');
18648 InlineLexer.prototype.mangle = function(text) {
18654 for (; i < l; i++) {
18655 ch = text.charCodeAt(i);
18656 if (Math.random() > 0.5) {
18657 ch = 'x' + ch.toString(16);
18659 out += '&#' + ch + ';';
18666 * Parsing & Compiling
18669 function Parser(options) {
18672 this.options = options || marked.defaults;
18676 * Static Parse Method
18679 Parser.parse = function(src, options) {
18680 var parser = new Parser(options);
18681 return parser.parse(src);
18688 Parser.prototype.parse = function(src) {
18689 this.inline = new InlineLexer(src.links, this.options);
18690 this.tokens = src.reverse();
18693 while (this.next()) {
18704 Parser.prototype.next = function() {
18705 return this.token = this.tokens.pop();
18709 * Preview Next Token
18712 Parser.prototype.peek = function() {
18713 return this.tokens[this.tokens.length-1] || 0;
18717 * Parse Text Tokens
18720 Parser.prototype.parseText = function() {
18721 var body = this.token.text;
18723 while (this.peek().type === 'text') {
18724 body += '\n' + this.next().text;
18727 return this.inline.output(body);
18731 * Parse Current Token
18734 Parser.prototype.tok = function() {
18735 switch (this.token.type) {
18746 + this.inline.output(this.token.text)
18752 if (this.options.highlight) {
18753 var code = this.options.highlight(this.token.text, this.token.lang);
18754 if (code != null && code !== this.token.text) {
18755 this.token.escaped = true;
18756 this.token.text = code;
18760 if (!this.token.escaped) {
18761 this.token.text = escape(this.token.text, true);
18764 return '<pre><code'
18767 + this.options.langPrefix
18773 + '</code></pre>\n';
18784 body += '<thead>\n<tr>\n';
18785 for (i = 0; i < this.token.header.length; i++) {
18786 heading = this.inline.output(this.token.header[i]);
18787 body += this.token.align[i]
18788 ? '<th align="' + this.token.align[i] + '">' + heading + '</th>\n'
18789 : '<th>' + heading + '</th>\n';
18791 body += '</tr>\n</thead>\n';
18794 body += '<tbody>\n'
18795 for (i = 0; i < this.token.cells.length; i++) {
18796 row = this.token.cells[i];
18798 for (j = 0; j < row.length; j++) {
18799 cell = this.inline.output(row[j]);
18800 body += this.token.align[j]
18801 ? '<td align="' + this.token.align[j] + '">' + cell + '</td>\n'
18802 : '<td>' + cell + '</td>\n';
18806 body += '</tbody>\n';
18812 case 'blockquote_start': {
18815 while (this.next().type !== 'blockquote_end') {
18816 body += this.tok();
18819 return '<blockquote>\n'
18821 + '</blockquote>\n';
18823 case 'list_start': {
18824 var type = this.token.ordered ? 'ol' : 'ul'
18827 while (this.next().type !== 'list_end') {
18828 body += this.tok();
18839 case 'list_item_start': {
18842 while (this.next().type !== 'list_item_end') {
18843 body += this.token.type === 'text'
18852 case 'loose_item_start': {
18855 while (this.next().type !== 'list_item_end') {
18856 body += this.tok();
18864 return !this.token.pre && !this.options.pedantic
18865 ? this.inline.output(this.token.text)
18868 case 'paragraph': {
18870 + this.inline.output(this.token.text)
18885 function escape(html, encode) {
18887 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
18888 .replace(/</g, '<')
18889 .replace(/>/g, '>')
18890 .replace(/"/g, '"')
18891 .replace(/'/g, ''');
18894 function replace(regex, opt) {
18895 regex = regex.source;
18897 return function self(name, val) {
18898 if (!name) return new RegExp(regex, opt);
18899 val = val.source || val;
18900 val = val.replace(/(^|[^\[])\^/g, '$1');
18901 regex = regex.replace(name, val);
18909 function merge(obj) {
18914 for (; i < arguments.length; i++) {
18915 target = arguments[i];
18916 for (key in target) {
18917 if (Object.prototype.hasOwnProperty.call(target, key)) {
18918 obj[key] = target[key];
18930 function marked(src, opt, callback) {
18931 if (callback || typeof opt === 'function') {
18937 if (opt) opt = merge({}, marked.defaults, opt);
18939 var tokens = Lexer.lex(tokens, opt)
18940 , highlight = opt.highlight
18942 , l = tokens.length
18945 if (!highlight || highlight.length < 3) {
18946 return callback(null, Parser.parse(tokens, opt));
18949 var done = function() {
18950 delete opt.highlight;
18951 var out = Parser.parse(tokens, opt);
18952 opt.highlight = highlight;
18953 return callback(null, out);
18956 for (; i < l; i++) {
18958 if (token.type !== 'code') return;
18960 return highlight(token.text, token.lang, function(err, code) {
18961 if (code == null || code === token.text) {
18962 return --pending || done();
18965 token.escaped = true;
18966 --pending || done();
18974 if (opt) opt = merge({}, marked.defaults, opt);
18975 return Parser.parse(Lexer.lex(src, opt), opt);
18977 e.message += '\nPlease report this to https://github.com/chjj/marked.';
18978 if ((opt || marked.defaults).silent) {
18979 return '<p>An error occured:</p><pre>'
18980 + escape(e.message + '', true)
18992 marked.setOptions = function(opt) {
18993 merge(marked.defaults, opt);
18997 marked.defaults = {
19006 langPrefix: 'lang-'
19013 marked.Parser = Parser;
19014 marked.parser = Parser.parse;
19016 marked.Lexer = Lexer;
19017 marked.lexer = Lexer.lex;
19019 marked.InlineLexer = InlineLexer;
19020 marked.inlineLexer = InlineLexer.output;
19022 marked.parse = marked;
19024 if (typeof exports === 'object') {
19025 module.exports = marked;
19026 } else if (typeof define === 'function' && define.amd) {
19027 define(function() { return marked; });
19029 this.marked = marked;
19032 }).call(function() {
19033 return this || (typeof window !== 'undefined' ? window : global);
19037 window.iD = function () {
19038 window.locale.en = iD.data.en;
19039 window.locale.current('en');
19044 // https://github.com/openstreetmap/iD/issues/772
19045 // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
19046 try { storage = localStorage; } catch (e) {} // eslint-disable-line no-empty
19047 storage = storage || (function() {
19050 getItem: function(k) { return s[k]; },
19051 setItem: function(k, v) { s[k] = v; },
19052 removeItem: function(k) { delete s[k]; }
19056 context.storage = function(k, v) {
19058 if (arguments.length === 1) return storage.getItem(k);
19059 else if (v === null) storage.removeItem(k);
19060 else storage.setItem(k, v);
19062 // localstorage quota exceeded
19063 /* eslint-disable no-console */
19064 if (typeof console !== 'undefined') console.error('localStorage quota exceeded');
19065 /* eslint-enable no-console */
19069 /* Accessor for setting minimum zoom for editing features. */
19071 var minEditableZoom = 16;
19072 context.minEditableZoom = function(_) {
19073 if (!arguments.length) return minEditableZoom;
19074 minEditableZoom = _;
19075 connection.tileZoom(_);
19079 var history = iD.History(context),
19080 dispatch = d3.dispatch('enter', 'exit'),
19083 ui = iD.ui(context),
19084 connection = iD.Connection(),
19085 locale = iD.detect().locale,
19088 if (locale && iD.data.locales.indexOf(locale) === -1) {
19089 locale = locale.split('-')[0];
19092 context.preauth = function(options) {
19093 connection.switch(options);
19097 context.locale = function(loc, path) {
19101 // Also set iD.detect().locale (unless we detected 'en-us' and openstreetmap wants 'en')..
19102 if (!(loc.toLowerCase() === 'en' && iD.detect().locale.toLowerCase() === 'en-us')) {
19103 iD.detect().locale = loc;
19109 context.loadLocale = function(cb) {
19110 if (locale && locale !== 'en' && iD.data.locales.indexOf(locale) !== -1) {
19111 localePath = localePath || context.assetPath() + 'locales/' + locale + '.json';
19112 d3.json(localePath, function(err, result) {
19113 window.locale[locale] = result;
19114 window.locale.current(locale);
19122 /* Straight accessors. Avoid using these if you can. */
19123 context.ui = function() { return ui; };
19124 context.connection = function() { return connection; };
19125 context.history = function() { return history; };
19128 function entitiesLoaded(err, result) {
19129 if (!err) history.merge(result.data, result.extent);
19132 context.loadTiles = function(projection, dimensions, callback) {
19133 function done(err, result) {
19134 entitiesLoaded(err, result);
19135 if (callback) callback(err, result);
19137 connection.loadTiles(projection, dimensions, done);
19140 context.loadEntity = function(id, callback) {
19141 function done(err, result) {
19142 entitiesLoaded(err, result);
19143 if (callback) callback(err, result);
19145 connection.loadEntity(id, done);
19148 context.zoomToEntity = function(id, zoomTo) {
19149 if (zoomTo !== false) {
19150 this.loadEntity(id, function(err, result) {
19152 var entity = _.find(result.data, function(e) { return e.id === id; });
19153 if (entity) { map.zoomTo(entity); }
19157 map.on('drawn.zoomToEntity', function() {
19158 if (!context.hasEntity(id)) return;
19159 map.on('drawn.zoomToEntity', null);
19160 context.on('enter.zoomToEntity', null);
19161 context.enter(iD.modes.Select(context, [id]));
19164 context.on('enter.zoomToEntity', function() {
19165 if (mode.id !== 'browse') {
19166 map.on('drawn.zoomToEntity', null);
19167 context.on('enter.zoomToEntity', null);
19173 context.graph = history.graph;
19174 context.changes = history.changes;
19175 context.intersects = history.intersects;
19177 var inIntro = false;
19179 context.inIntro = function(_) {
19180 if (!arguments.length) return inIntro;
19185 context.save = function() {
19186 if (inIntro || (mode && mode.id === 'save')) return;
19188 if (history.hasChanges()) return t('save.unsaved_changes');
19191 context.flush = function() {
19192 context.debouncedSave.cancel();
19193 connection.flush();
19199 // Debounce save, since it's a synchronous localStorage write,
19200 // and history changes can happen frequently (e.g. when dragging).
19201 context.debouncedSave = _.debounce(context.save, 350);
19202 function withDebouncedSave(fn) {
19203 return function() {
19204 var result = fn.apply(history, arguments);
19205 context.debouncedSave();
19210 context.perform = withDebouncedSave(history.perform);
19211 context.replace = withDebouncedSave(history.replace);
19212 context.pop = withDebouncedSave(history.pop);
19213 context.overwrite = withDebouncedSave(history.overwrite);
19214 context.undo = withDebouncedSave(history.undo);
19215 context.redo = withDebouncedSave(history.redo);
19218 context.hasEntity = function(id) {
19219 return history.graph().hasEntity(id);
19222 context.entity = function(id) {
19223 return history.graph().entity(id);
19226 context.childNodes = function(way) {
19227 return history.graph().childNodes(way);
19230 context.geometry = function(id) {
19231 return context.entity(id).geometry(history.graph());
19235 context.enter = function(newMode) {
19238 dispatch.exit(mode);
19243 dispatch.enter(mode);
19246 context.mode = function() {
19250 context.selectedIDs = function() {
19251 if (mode && mode.selectedIDs) {
19252 return mode.selectedIDs();
19259 context.install = function(behavior) {
19260 context.surface().call(behavior);
19263 context.uninstall = function(behavior) {
19264 context.surface().call(behavior.off);
19268 var copyIDs = [], copyGraph;
19269 context.copyGraph = function() { return copyGraph; };
19270 context.copyIDs = function(_) {
19271 if (!arguments.length) return copyIDs;
19273 copyGraph = history.graph();
19278 context.projection = iD.geo.RawMercator();
19281 var background = iD.Background(context);
19282 context.background = function() { return background; };
19285 var features = iD.Features(context);
19286 context.features = function() { return features; };
19287 context.hasHiddenConnections = function(id) {
19288 var graph = history.graph(),
19289 entity = graph.entity(id);
19290 return features.hasHiddenConnections(entity, graph);
19294 var map = iD.Map(context);
19295 context.map = function() { return map; };
19296 context.layers = function() { return map.layers; };
19297 context.surface = function() { return map.surface; };
19298 context.editable = function() { return map.editable(); };
19299 context.mouse = map.mouse;
19300 context.extent = map.extent;
19301 context.pan = map.pan;
19302 context.zoomIn = map.zoomIn;
19303 context.zoomOut = map.zoomOut;
19304 context.zoomInFurther = map.zoomInFurther;
19305 context.zoomOutFurther = map.zoomOutFurther;
19306 context.redrawEnable = map.redrawEnable;
19308 context.surfaceRect = function() {
19309 // Work around a bug in Firefox.
19310 // http://stackoverflow.com/questions/18153989/
19311 // https://bugzilla.mozilla.org/show_bug.cgi?id=530985
19312 return context.surface().node().parentNode.getBoundingClientRect();
19316 var presets = iD.presets();
19318 context.presets = function(_) {
19319 if (!arguments.length) return presets;
19321 iD.areaKeys = presets.areaKeys();
19325 context.imagery = function(_) {
19326 background.load(_);
19330 context.container = function(_) {
19331 if (!arguments.length) return container;
19333 container.classed('id-container', true);
19339 context.taginfo = function(_) {
19340 if (!arguments.length) return taginfo;
19346 context.embed = function(_) {
19347 if (!arguments.length) return embed;
19352 var assetPath = '';
19353 context.assetPath = function(_) {
19354 if (!arguments.length) return assetPath;
19360 context.assetMap = function(_) {
19361 if (!arguments.length) return assetMap;
19366 context.imagePath = function(_) {
19367 var asset = 'img/' + _;
19368 return assetMap[asset] || assetPath + asset;
19371 return d3.rebind(context, dispatch, 'on');
19374 iD.version = '1.8.4';
19379 var ua = navigator.userAgent,
19382 m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge
19384 detected.browser = m[1];
19385 detected.version = m[2];
19387 if (!detected.browser) {
19388 m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i); // IE11
19390 detected.browser = 'msie';
19391 detected.version = m[1];
19394 if (!detected.browser) {
19395 m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i); // Opera 15+
19397 detected.browser = 'Opera';
19398 detected.version = m[2];
19401 if (!detected.browser) {
19402 m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
19404 detected.browser = m[1];
19405 detected.version = m[2];
19406 m = ua.match(/version\/([\.\d]+)/i);
19407 if (m !== null) detected.version = m[1];
19410 if (!detected.browser) {
19411 detected.browser = navigator.appName;
19412 detected.version = navigator.appVersion;
19415 // keep major.minor version only..
19416 detected.version = detected.version.split(/\W/).slice(0,2).join('.');
19418 if (detected.browser.toLowerCase() === 'msie') {
19419 detected.ie = true;
19420 detected.browser = 'Internet Explorer';
19421 detected.support = parseFloat(detected.version) >= 11;
19423 detected.ie = false;
19424 detected.support = true;
19427 // Added due to incomplete svg style support. See #715
19428 detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 );
19430 detected.locale = (navigator.languages && navigator.languages.length)
19431 ? navigator.languages[0] : (navigator.language || navigator.userLanguage || 'en-US');
19433 detected.filedrop = (window.FileReader && 'ondrop' in window);
19436 return navigator.userAgent.indexOf(x) !== -1;
19440 detected.os = 'win';
19441 detected.platform = 'Windows';
19443 else if (nav('Mac')) {
19444 detected.os = 'mac';
19445 detected.platform = 'Macintosh';
19447 else if (nav('X11') || nav('Linux')) {
19448 detected.os = 'linux';
19449 detected.platform = 'Linux';
19452 detected.os = 'win';
19453 detected.platform = 'Unknown';
19456 iD.detect = function() { return detected; };
19458 iD.countryCode = function() {
19459 var countryCode = {},
19460 endpoint = 'https://nominatim.openstreetmap.org/reverse?';
19462 if (!iD.countryCode.cache) {
19463 iD.countryCode.cache = rbush();
19466 var cache = iD.countryCode.cache;
19468 countryCode.search = function(location, callback) {
19469 var countryCodes = cache.search([location[0], location[1], location[0], location[1]]);
19471 if (countryCodes.length > 0)
19472 return callback(null, countryCodes[0][4]);
19480 }), function(err, result) {
19482 return callback(err);
19483 else if (result && result.error)
19484 return callback(result.error);
19486 var extent = iD.geo.Extent(location).padByMeters(1000);
19488 cache.insert([extent[0][0], extent[0][1], extent[1][0], extent[1][1], result.address.country_code]);
19490 callback(null, result.address.country_code);
19494 return countryCode;
19496 iD.taginfo = function() {
19498 endpoint = 'https://taginfo.openstreetmap.org/api/4/',
19500 point: 'count_nodes',
19501 vertex: 'count_nodes',
19502 area: 'count_ways',
19512 if (!iD.taginfo.cache) {
19513 iD.taginfo.cache = {};
19516 var cache = iD.taginfo.cache;
19518 function sets(parameters, n, o) {
19519 if (parameters.geometry && o[parameters.geometry]) {
19520 parameters[n] = o[parameters.geometry];
19525 function setFilter(parameters) {
19526 return sets(parameters, 'filter', tag_filters);
19529 function setSort(parameters) {
19530 return sets(parameters, 'sortname', tag_sorts);
19533 function clean(parameters) {
19534 return _.omit(parameters, 'geometry', 'debounce');
19537 function popularKeys(parameters) {
19538 var pop_field = 'count_all';
19539 if (parameters.filter) pop_field = 'count_' + parameters.filter;
19540 return function(d) { return parseFloat(d[pop_field]) > 5000 || d.in_wiki; };
19543 function popularValues() {
19544 return function(d) { return parseFloat(d.fraction) > 0.01 || d.in_wiki; };
19547 function valKey(d) { return { value: d.key }; }
19549 function valKeyDescription(d) {
19552 title: d.description
19556 // sort keys with ':' lower than keys without ':'
19557 function sortKeys(a, b) {
19558 return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
19559 : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
19563 var debounced = _.debounce(d3.json, 100, true);
19565 function request(url, debounce, callback) {
19567 callback(null, cache[url]);
19568 } else if (debounce) {
19569 debounced(url, done);
19571 d3.json(url, done);
19574 function done(err, data) {
19575 if (!err) cache[url] = data;
19576 callback(err, data);
19580 taginfo.keys = function(parameters, callback) {
19581 var debounce = parameters.debounce;
19582 parameters = clean(setSort(parameters));
19583 request(endpoint + 'keys/all?' +
19584 iD.util.qsString(_.extend({
19586 sortname: 'count_all',
19589 }, parameters)), debounce, function(err, d) {
19590 if (err) return callback(err);
19591 callback(null, d.data.filter(popularKeys(parameters)).sort(sortKeys).map(valKey));
19595 taginfo.values = function(parameters, callback) {
19596 var debounce = parameters.debounce;
19597 parameters = clean(setSort(setFilter(parameters)));
19598 request(endpoint + 'key/values?' +
19599 iD.util.qsString(_.extend({
19601 sortname: 'count_all',
19604 }, parameters)), debounce, function(err, d) {
19605 if (err) return callback(err);
19606 callback(null, d.data.filter(popularValues()).map(valKeyDescription), parameters);
19610 taginfo.docs = function(parameters, callback) {
19611 var debounce = parameters.debounce;
19612 parameters = clean(setSort(parameters));
19614 var path = 'key/wiki_pages?';
19615 if (parameters.value) path = 'tag/wiki_pages?';
19616 else if (parameters.rtype) path = 'relation/wiki_pages?';
19618 request(endpoint + path + iD.util.qsString(parameters), debounce, function(err, d) {
19619 if (err) return callback(err);
19620 callback(null, d.data);
19624 taginfo.endpoint = function(_) {
19625 if (!arguments.length) return endpoint;
19632 iD.wikipedia = function() {
19634 endpoint = 'https://en.wikipedia.org/w/api.php?';
19636 wiki.search = function(lang, query, callback) {
19637 lang = lang || 'en';
19638 d3.jsonp(endpoint.replace('en', lang) +
19643 srinfo: 'suggestion',
19645 callback: '{callback}',
19647 }), function(data) {
19648 if (!data.query) return;
19649 callback(query, data.query.search.map(function(d) {
19655 wiki.suggestions = function(lang, query, callback) {
19656 lang = lang || 'en';
19657 d3.jsonp(endpoint.replace('en', lang) +
19659 action: 'opensearch',
19663 callback: '{callback}',
19666 callback(d[0], d[1]);
19670 wiki.translations = function(lang, title, callback) {
19671 d3.jsonp(endpoint.replace('en', lang) +
19676 callback: '{callback}',
19680 var list = d.query.pages[Object.keys(d.query.pages)[0]],
19682 if (list && list.langlinks) {
19683 list.langlinks.forEach(function(d) {
19684 translations[d.lang] = d['*'];
19686 callback(translations);
19695 iD.util.tagText = function(entity) {
19696 return d3.entries(entity.tags).map(function(e) {
19697 return e.key + '=' + e.value;
19701 iD.util.entitySelector = function(ids) {
19702 return ids.length ? '.' + ids.join(',.') : 'nothing';
19705 iD.util.entityOrMemberSelector = function(ids, graph) {
19706 var s = iD.util.entitySelector(ids);
19708 ids.forEach(function(id) {
19709 var entity = graph.hasEntity(id);
19710 if (entity && entity.type === 'relation') {
19711 entity.members.forEach(function(member) {
19712 s += ',.' + member.id;
19720 iD.util.displayName = function(entity) {
19721 var localeName = 'name:' + iD.detect().locale.toLowerCase().split('-')[0];
19722 return entity.tags[localeName] || entity.tags.name || entity.tags.ref;
19725 iD.util.displayType = function(id) {
19727 n: t('inspector.node'),
19728 w: t('inspector.way'),
19729 r: t('inspector.relation')
19733 iD.util.stringQs = function(str) {
19734 return str.split('&').reduce(function(obj, pair){
19735 var parts = pair.split('=');
19736 if (parts.length === 2) {
19737 obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
19743 iD.util.qsString = function(obj, noencode) {
19744 function softEncode(s) {
19745 // encode everything except special characters used in certain hash parameters:
19746 // "/" in map states, ":", ",", {" and "}" in background
19747 return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
19749 return Object.keys(obj).sort().map(function(key) {
19750 return encodeURIComponent(key) + '=' + (
19751 noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
19755 iD.util.prefixDOMProperty = function(property) {
19756 var prefixes = ['webkit', 'ms', 'moz', 'o'],
19758 n = prefixes.length,
19764 property = property.substr(0, 1).toUpperCase() + property.substr(1);
19767 if (prefixes[i] + property in s)
19768 return prefixes[i] + property;
19773 iD.util.prefixCSSProperty = function(property) {
19774 var prefixes = ['webkit', 'ms', 'Moz', 'O'],
19776 n = prefixes.length,
19777 s = document.body.style;
19779 if (property.toLowerCase() in s)
19780 return property.toLowerCase();
19783 if (prefixes[i] + property in s)
19784 return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
19790 iD.util.setTransform = function(el, x, y, scale) {
19791 var prop = iD.util.transformProperty = iD.util.transformProperty || iD.util.prefixCSSProperty('Transform'),
19792 translate = iD.detect().opera ?
19793 'translate(' + x + 'px,' + y + 'px)' :
19794 'translate3d(' + x + 'px,' + y + 'px,0)';
19795 return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
19798 iD.util.getStyle = function(selector) {
19799 for (var i = 0; i < document.styleSheets.length; i++) {
19800 var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules || [];
19801 for (var k = 0; k < rules.length; k++) {
19802 var selectorText = rules[k].selectorText && rules[k].selectorText.split(', ');
19803 if (_.contains(selectorText, selector)) {
19810 iD.util.editDistance = function(a, b) {
19811 if (a.length === 0) return b.length;
19812 if (b.length === 0) return a.length;
19814 for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
19815 for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
19816 for (i = 1; i <= b.length; i++) {
19817 for (j = 1; j <= a.length; j++) {
19818 if (b.charAt(i-1) === a.charAt(j-1)) {
19819 matrix[i][j] = matrix[i-1][j-1];
19821 matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
19822 Math.min(matrix[i][j-1] + 1, // insertion
19823 matrix[i-1][j] + 1)); // deletion
19827 return matrix[b.length][a.length];
19830 // a d3.mouse-alike which
19831 // 1. Only works on HTML elements, not SVG
19832 // 2. Does not cause style recalculation
19833 iD.util.fastMouse = function(container) {
19834 var rect = container.getBoundingClientRect(),
19835 rectLeft = rect.left,
19836 rectTop = rect.top,
19837 clientLeft = +container.clientLeft,
19838 clientTop = +container.clientTop;
19839 return function(e) {
19841 e.clientX - rectLeft - clientLeft,
19842 e.clientY - rectTop - clientTop];
19846 /* eslint-disable no-proto */
19847 iD.util.getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; };
19848 /* eslint-enable no-proto */
19850 iD.util.asyncMap = function(inputs, func, callback) {
19851 var remaining = inputs.length,
19855 inputs.forEach(function(d, i) {
19856 func(d, function done(err, data) {
19860 if (!remaining) callback(errors, results);
19865 // wraps an index to an interval [0..length-1]
19866 iD.util.wrap = function(index, length) {
19868 index += Math.ceil(-index/length)*length;
19869 return index % length;
19871 // A per-domain session mutex backed by a cookie and dead man's
19872 // switch. If the session crashes, the mutex will auto-release
19873 // after 5 seconds.
19875 iD.util.SessionMutex = function(name) {
19880 var expires = new Date();
19881 expires.setSeconds(expires.getSeconds() + 5);
19882 document.cookie = name + '=1; expires=' + expires.toUTCString();
19885 mutex.lock = function() {
19886 if (intervalID) return true;
19887 var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
19888 if (cookie) return false;
19890 intervalID = window.setInterval(renew, 4000);
19894 mutex.unlock = function() {
19895 if (!intervalID) return;
19896 document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
19897 clearInterval(intervalID);
19901 mutex.locked = function() {
19902 return !!intervalID;
19907 iD.util.SuggestNames = function(preset, suggestions) {
19908 preset = preset.id.split('/', 2);
19912 return function(value, callback) {
19914 if (value && value.length > 2) {
19915 if (suggestions[k] && suggestions[k][v]) {
19916 for (var sugg in suggestions[k][v]) {
19917 var dist = iD.util.editDistance(value, sugg.substring(0, value.length));
19927 result.sort(function(a, b) {
19928 return a.dist - b.dist;
19931 result = result.slice(0,3);
19937 iD.geo.roundCoords = function(c) {
19938 return [Math.floor(c[0]), Math.floor(c[1])];
19941 iD.geo.interp = function(p1, p2, t) {
19942 return [p1[0] + (p2[0] - p1[0]) * t,
19943 p1[1] + (p2[1] - p1[1]) * t];
19946 // 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product.
19947 // Returns a positive value, if OAB makes a counter-clockwise turn,
19948 // negative for clockwise turn, and zero if the points are collinear.
19949 iD.geo.cross = function(o, a, b) {
19950 return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
19953 // http://jsperf.com/id-dist-optimization
19954 iD.geo.euclideanDistance = function(a, b) {
19955 var x = a[0] - b[0], y = a[1] - b[1];
19956 return Math.sqrt((x * x) + (y * y));
19959 // using WGS84 polar radius (6356752.314245179 m)
19960 // const = 2 * PI * r / 360
19961 iD.geo.latToMeters = function(dLat) {
19962 return dLat * 110946.257617;
19965 // using WGS84 equatorial radius (6378137.0 m)
19966 // const = 2 * PI * r / 360
19967 iD.geo.lonToMeters = function(dLon, atLat) {
19968 return Math.abs(atLat) >= 90 ? 0 :
19969 dLon * 111319.490793 * Math.abs(Math.cos(atLat * (Math.PI/180)));
19972 // using WGS84 polar radius (6356752.314245179 m)
19973 // const = 2 * PI * r / 360
19974 iD.geo.metersToLat = function(m) {
19975 return m / 110946.257617;
19978 // using WGS84 equatorial radius (6378137.0 m)
19979 // const = 2 * PI * r / 360
19980 iD.geo.metersToLon = function(m, atLat) {
19981 return Math.abs(atLat) >= 90 ? 0 :
19982 m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180)));
19985 // Equirectangular approximation of spherical distances on Earth
19986 iD.geo.sphericalDistance = function(a, b) {
19987 var x = iD.geo.lonToMeters(a[0] - b[0], (a[1] + b[1]) / 2),
19988 y = iD.geo.latToMeters(a[1] - b[1]);
19989 return Math.sqrt((x * x) + (y * y));
19992 iD.geo.edgeEqual = function(a, b) {
19993 return (a[0] === b[0] && a[1] === b[1]) ||
19994 (a[0] === b[1] && a[1] === b[0]);
19997 // Return the counterclockwise angle in the range (-pi, pi)
19998 // between the positive X axis and the line intersecting a and b.
19999 iD.geo.angle = function(a, b, projection) {
20000 a = projection(a.loc);
20001 b = projection(b.loc);
20002 return Math.atan2(b[1] - a[1], b[0] - a[0]);
20005 // Choose the edge with the minimal distance from `point` to its orthogonal
20006 // projection onto that edge, if such a projection exists, or the distance to
20007 // the closest vertex on that edge. Returns an object with the `index` of the
20008 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
20009 iD.geo.chooseEdge = function(nodes, point, projection) {
20010 var dist = iD.geo.euclideanDistance,
20011 points = nodes.map(function(n) { return projection(n.loc); }),
20015 function dot(p, q) {
20016 return p[0] * q[0] + p[1] * q[1];
20019 for (var i = 0; i < points.length - 1; i++) {
20021 s = [points[i + 1][0] - o[0],
20022 points[i + 1][1] - o[1]],
20023 v = [point[0] - o[0],
20025 proj = dot(v, s) / dot(s, s),
20030 } else if (proj > 1) {
20033 p = [o[0] + proj * s[0], o[1] + proj * s[1]];
20036 var d = dist(p, point);
20040 loc = projection.invert(p);
20051 // Return the intersection point of 2 line segments.
20052 // From https://github.com/pgkelley4/line-segments-intersect
20053 // This uses the vector cross product approach described below:
20054 // http://stackoverflow.com/a/565282/786339
20055 iD.geo.lineIntersection = function(a, b) {
20056 function subtractPoints(point1, point2) {
20057 return [point1[0] - point2[0], point1[1] - point2[1]];
20059 function crossProduct(point1, point2) {
20060 return point1[0] * point2[1] - point1[1] * point2[0];
20063 var p = [a[0][0], a[0][1]],
20064 p2 = [a[1][0], a[1][1]],
20065 q = [b[0][0], b[0][1]],
20066 q2 = [b[1][0], b[1][1]],
20067 r = subtractPoints(p2, p),
20068 s = subtractPoints(q2, q),
20069 uNumerator = crossProduct(subtractPoints(q, p), r),
20070 denominator = crossProduct(r, s);
20072 if (uNumerator && denominator) {
20073 var u = uNumerator / denominator,
20074 t = crossProduct(subtractPoints(q, p), s) / denominator;
20076 if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
20077 return iD.geo.interp(p, p2, t);
20084 iD.geo.pathIntersections = function(path1, path2) {
20085 var intersections = [];
20086 for (var i = 0; i < path1.length - 1; i++) {
20087 for (var j = 0; j < path2.length - 1; j++) {
20088 var a = [ path1[i], path1[i+1] ],
20089 b = [ path2[j], path2[j+1] ],
20090 hit = iD.geo.lineIntersection(a, b);
20091 if (hit) intersections.push(hit);
20094 return intersections;
20097 // Return whether point is contained in polygon.
20099 // `point` should be a 2-item array of coordinates.
20100 // `polygon` should be an array of 2-item arrays of coordinates.
20102 // From https://github.com/substack/point-in-polygon.
20103 // ray-casting algorithm based on
20104 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
20106 iD.geo.pointInPolygon = function(point, polygon) {
20111 for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
20112 var xi = polygon[i][0], yi = polygon[i][1];
20113 var xj = polygon[j][0], yj = polygon[j][1];
20115 var intersect = ((yi > y) !== (yj > y)) &&
20116 (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
20117 if (intersect) inside = !inside;
20123 iD.geo.polygonContainsPolygon = function(outer, inner) {
20124 return _.every(inner, function(point) {
20125 return iD.geo.pointInPolygon(point, outer);
20129 iD.geo.polygonIntersectsPolygon = function(outer, inner, checkSegments) {
20130 function testSegments(outer, inner) {
20131 for (var i = 0; i < outer.length - 1; i++) {
20132 for (var j = 0; j < inner.length - 1; j++) {
20133 var a = [ outer[i], outer[i+1] ],
20134 b = [ inner[j], inner[j+1] ];
20135 if (iD.geo.lineIntersection(a, b)) return true;
20141 function testPoints(outer, inner) {
20142 return _.some(inner, function(point) {
20143 return iD.geo.pointInPolygon(point, outer);
20147 return testPoints(outer, inner) || (!!checkSegments && testSegments(outer, inner));
20150 iD.geo.pathLength = function(path) {
20153 for (var i = 0; i < path.length - 1; i++) {
20154 dx = path[i][0] - path[i + 1][0];
20155 dy = path[i][1] - path[i + 1][1];
20156 length += Math.sqrt(dx * dx + dy * dy);
20160 iD.geo.Extent = function geoExtent(min, max) {
20161 if (!(this instanceof iD.geo.Extent)) return new iD.geo.Extent(min, max);
20162 if (min instanceof iD.geo.Extent) {
20164 } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
20168 this[0] = min || [ Infinity, Infinity];
20169 this[1] = max || min || [-Infinity, -Infinity];
20173 iD.geo.Extent.prototype = new Array(2);
20175 _.extend(iD.geo.Extent.prototype, {
20176 equals: function (obj) {
20177 return this[0][0] === obj[0][0] &&
20178 this[0][1] === obj[0][1] &&
20179 this[1][0] === obj[1][0] &&
20180 this[1][1] === obj[1][1];
20183 extend: function(obj) {
20184 if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20185 return iD.geo.Extent([Math.min(obj[0][0], this[0][0]),
20186 Math.min(obj[0][1], this[0][1])],
20187 [Math.max(obj[1][0], this[1][0]),
20188 Math.max(obj[1][1], this[1][1])]);
20191 _extend: function(extent) {
20192 this[0][0] = Math.min(extent[0][0], this[0][0]);
20193 this[0][1] = Math.min(extent[0][1], this[0][1]);
20194 this[1][0] = Math.max(extent[1][0], this[1][0]);
20195 this[1][1] = Math.max(extent[1][1], this[1][1]);
20199 return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
20202 center: function() {
20203 return [(this[0][0] + this[1][0]) / 2,
20204 (this[0][1] + this[1][1]) / 2];
20207 polygon: function() {
20209 [this[0][0], this[0][1]],
20210 [this[0][0], this[1][1]],
20211 [this[1][0], this[1][1]],
20212 [this[1][0], this[0][1]],
20213 [this[0][0], this[0][1]]
20217 contains: function(obj) {
20218 if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20219 return obj[0][0] >= this[0][0] &&
20220 obj[0][1] >= this[0][1] &&
20221 obj[1][0] <= this[1][0] &&
20222 obj[1][1] <= this[1][1];
20225 intersects: function(obj) {
20226 if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20227 return obj[0][0] <= this[1][0] &&
20228 obj[0][1] <= this[1][1] &&
20229 obj[1][0] >= this[0][0] &&
20230 obj[1][1] >= this[0][1];
20233 intersection: function(obj) {
20234 if (!this.intersects(obj)) return new iD.geo.Extent();
20235 return new iD.geo.Extent([Math.max(obj[0][0], this[0][0]),
20236 Math.max(obj[0][1], this[0][1])],
20237 [Math.min(obj[1][0], this[1][0]),
20238 Math.min(obj[1][1], this[1][1])]);
20241 percentContainedIn: function(obj) {
20242 if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20243 var a1 = this.intersection(obj).area(),
20246 if (a1 === Infinity || a2 === Infinity || a1 === 0 || a2 === 0) {
20253 padByMeters: function(meters) {
20254 var dLat = iD.geo.metersToLat(meters),
20255 dLon = iD.geo.metersToLon(meters, this.center()[1]);
20256 return iD.geo.Extent(
20257 [this[0][0] - dLon, this[0][1] - dLat],
20258 [this[1][0] + dLon, this[1][1] + dLat]);
20261 toParam: function() {
20262 return [this[0][0], this[0][1], this[1][0], this[1][1]].join(',');
20266 iD.geo.Turn = function(turn) {
20267 if (!(this instanceof iD.geo.Turn))
20268 return new iD.geo.Turn(turn);
20269 _.extend(this, turn);
20272 iD.geo.Intersection = function(graph, vertexId) {
20273 var vertex = graph.entity(vertexId),
20274 parentWays = graph.parentWays(vertex),
20278 function addHighway(way, adjacentNodeId) {
20279 if (highways[adjacentNodeId]) {
20280 coincident.push(adjacentNodeId);
20282 highways[adjacentNodeId] = way;
20286 // Pre-split ways that would need to be split in
20287 // order to add a restriction. The real split will
20288 // happen when the restriction is added.
20289 parentWays.forEach(function(way) {
20290 if (!way.tags.highway || way.isArea() || way.isDegenerate())
20293 var isFirst = (vertexId === way.first()),
20294 isLast = (vertexId === way.last()),
20295 isAffix = (isFirst || isLast),
20296 isClosingNode = (isFirst && isLast);
20298 if (isAffix && !isClosingNode) {
20299 var index = (isFirst ? 1 : way.nodes.length - 2);
20300 addHighway(way, way.nodes[index]);
20303 var splitIndex, wayA, wayB, indexA, indexB;
20304 if (isClosingNode) {
20305 splitIndex = Math.ceil(way.nodes.length / 2); // split at midpoint
20306 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
20307 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
20309 indexB = way.nodes.length - 2;
20311 splitIndex = _.indexOf(way.nodes, vertex.id, 1); // split at vertexid
20312 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
20313 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
20314 indexA = splitIndex - 1;
20315 indexB = splitIndex + 1;
20317 graph = graph.replace(wayA).replace(wayB);
20318 addHighway(wayA, way.nodes[indexA]);
20319 addHighway(wayB, way.nodes[indexB]);
20323 // remove any ways from this intersection that are coincident
20324 // (i.e. any adjacent node used by more than one intersecting way)
20325 coincident.forEach(function (n) {
20326 delete highways[n];
20330 var intersection = {
20331 highways: highways,
20332 ways: _.values(highways),
20336 intersection.adjacentNodeId = function(fromWayId) {
20337 return _.find(_.keys(highways), function(k) {
20338 return highways[k].id === fromWayId;
20342 intersection.turns = function(fromNodeId) {
20343 var start = highways[fromNodeId];
20347 if (start.first() === vertex.id && start.tags.oneway === 'yes')
20349 if (start.last() === vertex.id && start.tags.oneway === '-1')
20352 function withRestriction(turn) {
20353 graph.parentRelations(graph.entity(turn.from.way)).forEach(function(relation) {
20354 if (relation.tags.type !== 'restriction')
20357 var f = relation.memberByRole('from'),
20358 t = relation.memberByRole('to'),
20359 v = relation.memberByRole('via');
20361 if (f && f.id === turn.from.way &&
20362 v && v.id === turn.via.node &&
20363 t && t.id === turn.to.way) {
20364 turn.restriction = relation.id;
20365 } else if (/^only_/.test(relation.tags.restriction) &&
20366 f && f.id === turn.from.way &&
20367 v && v.id === turn.via.node &&
20368 t && t.id !== turn.to.way) {
20369 turn.restriction = relation.id;
20370 turn.indirect_restriction = true;
20374 return iD.geo.Turn(turn);
20379 way: start.id.split(/-(a|b)/)[0]
20381 via = { node: vertex.id },
20384 _.each(highways, function(end, adjacentNodeId) {
20389 if (end.first() !== vertex.id && end.tags.oneway !== 'yes') {
20390 turns.push(withRestriction({
20394 node: adjacentNodeId,
20395 way: end.id.split(/-(a|b)/)[0]
20401 if (end.last() !== vertex.id && end.tags.oneway !== '-1') {
20402 turns.push(withRestriction({
20406 node: adjacentNodeId,
20407 way: end.id.split(/-(a|b)/)[0]
20415 if (start.tags.oneway !== 'yes' && start.tags.oneway !== '-1') {
20416 turns.push(withRestriction({
20427 return intersection;
20431 iD.geo.inferRestriction = function(graph, from, via, to, projection) {
20432 var fromWay = graph.entity(from.way),
20433 fromNode = graph.entity(from.node),
20434 toWay = graph.entity(to.way),
20435 toNode = graph.entity(to.node),
20436 viaNode = graph.entity(via.node),
20437 fromOneWay = (fromWay.tags.oneway === 'yes' && fromWay.last() === via.node) ||
20438 (fromWay.tags.oneway === '-1' && fromWay.first() === via.node),
20439 toOneWay = (toWay.tags.oneway === 'yes' && toWay.first() === via.node) ||
20440 (toWay.tags.oneway === '-1' && toWay.last() === via.node),
20441 angle = iD.geo.angle(viaNode, fromNode, projection) -
20442 iD.geo.angle(viaNode, toNode, projection);
20444 angle = angle * 180 / Math.PI;
20449 if (fromNode === toNode)
20450 return 'no_u_turn';
20451 if ((angle < 23 || angle > 336) && fromOneWay && toOneWay)
20452 return 'no_u_turn';
20454 return 'no_right_turn';
20456 return 'no_left_turn';
20458 return 'no_straight_on';
20460 // For fixing up rendering of multipolygons with tags on the outer member.
20461 // https://github.com/openstreetmap/iD/issues/613
20462 iD.geo.isSimpleMultipolygonOuterMember = function(entity, graph) {
20463 if (entity.type !== 'way')
20466 var parents = graph.parentRelations(entity);
20467 if (parents.length !== 1)
20470 var parent = parents[0];
20471 if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
20474 var members = parent.members, member;
20475 for (var i = 0; i < members.length; i++) {
20476 member = members[i];
20477 if (member.id === entity.id && member.role && member.role !== 'outer')
20478 return false; // Not outer member
20479 if (member.id !== entity.id && (!member.role || member.role === 'outer'))
20480 return false; // Not a simple multipolygon
20486 iD.geo.simpleMultipolygonOuterMember = function(entity, graph) {
20487 if (entity.type !== 'way')
20490 var parents = graph.parentRelations(entity);
20491 if (parents.length !== 1)
20494 var parent = parents[0];
20495 if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
20498 var members = parent.members, member, outerMember;
20499 for (var i = 0; i < members.length; i++) {
20500 member = members[i];
20501 if (!member.role || member.role === 'outer') {
20503 return false; // Not a simple multipolygon
20504 outerMember = member;
20508 return outerMember && graph.hasEntity(outerMember.id);
20511 // Join `array` into sequences of connecting ways.
20513 // Segments which share identical start/end nodes will, as much as possible,
20514 // be connected with each other.
20516 // The return value is a nested array. Each constituent array contains elements
20517 // of `array` which have been determined to connect. Each consitituent array
20518 // also has a `nodes` property whose value is an ordered array of member nodes,
20519 // with appropriate order reversal and start/end coordinate de-duplication.
20521 // Members of `array` must have, at minimum, `type` and `id` properties.
20522 // Thus either an array of `iD.Way`s or a relation member array may be
20525 // If an member has a `tags` property, its tags will be reversed via
20526 // `iD.actions.Reverse` in the output.
20528 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
20529 // false) and non-way members are ignored.
20531 iD.geo.joinWays = function(array, graph) {
20532 var joined = [], member, current, nodes, first, last, i, how, what;
20534 array = array.filter(function(member) {
20535 return member.type === 'way' && graph.hasEntity(member.id);
20538 function resolve(member) {
20539 return graph.childNodes(graph.entity(member.id));
20542 function reverse(member) {
20543 return member.tags ? iD.actions.Reverse(member.id, {reverseOneway: true})(graph).entity(member.id) : member;
20546 while (array.length) {
20547 member = array.shift();
20548 current = [member];
20549 current.nodes = nodes = resolve(member).slice();
20550 joined.push(current);
20552 while (array.length && _.first(nodes) !== _.last(nodes)) {
20553 first = _.first(nodes);
20554 last = _.last(nodes);
20556 for (i = 0; i < array.length; i++) {
20558 what = resolve(member);
20560 if (last === _.first(what)) {
20562 what = what.slice(1);
20564 } else if (last === _.last(what)) {
20566 what = what.slice(0, -1).reverse();
20567 member = reverse(member);
20569 } else if (first === _.last(what)) {
20570 how = nodes.unshift;
20571 what = what.slice(0, -1);
20573 } else if (first === _.first(what)) {
20574 how = nodes.unshift;
20575 what = what.slice(1).reverse();
20576 member = reverse(member);
20584 break; // No more joinable ways.
20586 how.apply(current, [member]);
20587 how.apply(nodes, what);
20589 array.splice(i, 1);
20596 Bypasses features of D3's default projection stream pipeline that are unnecessary:
20597 * Antimeridian clipping
20598 * Spherical rotation
20601 iD.geo.RawMercator = function () {
20602 var project = d3.geo.mercator.raw,
20603 k = 512 / Math.PI, // scale
20604 x = 0, y = 0, // translate
20605 clipExtent = [[0, 0], [0, 0]];
20607 function projection(point) {
20608 point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
20609 return [point[0] * k + x, y - point[1] * k];
20612 projection.invert = function(point) {
20613 point = project.invert((point[0] - x) / k, (y - point[1]) / k);
20614 return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
20617 projection.scale = function(_) {
20618 if (!arguments.length) return k;
20623 projection.translate = function(_) {
20624 if (!arguments.length) return [x, y];
20630 projection.clipExtent = function(_) {
20631 if (!arguments.length) return clipExtent;
20636 projection.stream = d3.geo.transform({
20637 point: function(x, y) {
20638 x = projection([x, y]);
20639 this.stream.point(x[0], x[1]);
20646 iD.actions.AddEntity = function(way) {
20647 return function(graph) {
20648 return graph.replace(way);
20651 iD.actions.AddMember = function(relationId, member, memberIndex) {
20652 return function(graph) {
20653 var relation = graph.entity(relationId);
20655 if (isNaN(memberIndex) && member.type === 'way') {
20656 var members = relation.indexedMembers();
20657 members.push(member);
20659 var joined = iD.geo.joinWays(members, graph);
20660 for (var i = 0; i < joined.length; i++) {
20661 var segment = joined[i];
20662 for (var j = 0; j < segment.length && segment.length >= 2; j++) {
20663 if (segment[j] !== member)
20667 memberIndex = segment[j + 1].index;
20668 } else if (j === segment.length - 1) {
20669 memberIndex = segment[j - 1].index + 1;
20671 memberIndex = Math.min(segment[j - 1].index + 1, segment[j + 1].index + 1);
20677 return graph.replace(relation.addMember(member, memberIndex));
20680 iD.actions.AddMidpoint = function(midpoint, node) {
20681 return function(graph) {
20682 graph = graph.replace(node.move(midpoint.loc));
20684 var parents = _.intersection(
20685 graph.parentWays(graph.entity(midpoint.edge[0])),
20686 graph.parentWays(graph.entity(midpoint.edge[1])));
20688 parents.forEach(function(way) {
20689 for (var i = 0; i < way.nodes.length - 1; i++) {
20690 if (iD.geo.edgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
20691 graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
20693 // Add only one midpoint on doubled-back segments,
20694 // turning them into self-intersections.
20703 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
20704 iD.actions.AddVertex = function(wayId, nodeId, index) {
20705 return function(graph) {
20706 return graph.replace(graph.entity(wayId).addNode(nodeId, index));
20709 iD.actions.ChangeMember = function(relationId, member, memberIndex) {
20710 return function(graph) {
20711 return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
20714 iD.actions.ChangePreset = function(entityId, oldPreset, newPreset) {
20715 return function(graph) {
20716 var entity = graph.entity(entityId),
20717 geometry = entity.geometry(graph),
20718 tags = entity.tags;
20720 if (oldPreset) tags = oldPreset.removeTags(tags, geometry);
20721 if (newPreset) tags = newPreset.applyTags(tags, geometry);
20723 return graph.replace(entity.update({tags: tags}));
20726 iD.actions.ChangeTags = function(entityId, tags) {
20727 return function(graph) {
20728 var entity = graph.entity(entityId);
20729 return graph.replace(entity.update({tags: tags}));
20732 iD.actions.Circularize = function(wayId, projection, maxAngle) {
20733 maxAngle = (maxAngle || 20) * Math.PI / 180;
20735 var action = function(graph) {
20736 var way = graph.entity(wayId);
20738 if (!way.isConvex(graph)) {
20739 graph = action.makeConvex(graph);
20742 var nodes = _.uniq(graph.childNodes(way)),
20743 keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }),
20744 points = nodes.map(function(n) { return projection(n.loc); }),
20745 keyPoints = keyNodes.map(function(n) { return projection(n.loc); }),
20746 centroid = (points.length === 2) ? iD.geo.interp(points[0], points[1], 0.5) : d3.geom.polygon(points).centroid(),
20747 radius = d3.median(points, function(p) { return iD.geo.euclideanDistance(centroid, p); }),
20748 sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
20751 // we need atleast two key nodes for the algorithm to work
20752 if (!keyNodes.length) {
20753 keyNodes = [nodes[0]];
20754 keyPoints = [points[0]];
20757 if (keyNodes.length === 1) {
20758 var index = nodes.indexOf(keyNodes[0]),
20759 oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
20761 keyNodes.push(nodes[oppositeIndex]);
20762 keyPoints.push(points[oppositeIndex]);
20765 // key points and nodes are those connected to the ways,
20766 // they are projected onto the circle, inbetween nodes are moved
20767 // to constant intervals between key nodes, extra inbetween nodes are
20768 // added if necessary.
20769 for (var i = 0; i < keyPoints.length; i++) {
20770 var nextKeyNodeIndex = (i + 1) % keyNodes.length,
20771 startNode = keyNodes[i],
20772 endNode = keyNodes[nextKeyNodeIndex],
20773 startNodeIndex = nodes.indexOf(startNode),
20774 endNodeIndex = nodes.indexOf(endNode),
20775 numberNewPoints = -1,
20776 indexRange = endNodeIndex - startNodeIndex,
20777 distance, totalAngle, eachAngle, startAngle, endAngle,
20778 angle, loc, node, j,
20779 inBetweenNodes = [];
20781 if (indexRange < 0) {
20782 indexRange += nodes.length;
20785 // position this key node
20786 distance = iD.geo.euclideanDistance(centroid, keyPoints[i]);
20787 if (distance === 0) { distance = 1e-4; }
20789 centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
20790 centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
20791 graph = graph.replace(keyNodes[i].move(projection.invert(keyPoints[i])));
20793 // figure out the between delta angle we want to match to
20794 startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
20795 endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
20796 totalAngle = endAngle - startAngle;
20798 // detects looping around -pi/pi
20799 if (totalAngle * sign > 0) {
20800 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
20805 eachAngle = totalAngle / (indexRange + numberNewPoints);
20806 } while (Math.abs(eachAngle) > maxAngle);
20808 // move existing points
20809 for (j = 1; j < indexRange; j++) {
20810 angle = startAngle + j * eachAngle;
20811 loc = projection.invert([
20812 centroid[0] + Math.cos(angle)*radius,
20813 centroid[1] + Math.sin(angle)*radius]);
20815 node = nodes[(j + startNodeIndex) % nodes.length].move(loc);
20816 graph = graph.replace(node);
20819 // add new inbetween nodes if necessary
20820 for (j = 0; j < numberNewPoints; j++) {
20821 angle = startAngle + (indexRange + j) * eachAngle;
20822 loc = projection.invert([
20823 centroid[0] + Math.cos(angle) * radius,
20824 centroid[1] + Math.sin(angle) * radius]);
20826 node = iD.Node({loc: loc});
20827 graph = graph.replace(node);
20829 nodes.splice(endNodeIndex + j, 0, node);
20830 inBetweenNodes.push(node.id);
20833 // Check for other ways that share these keyNodes..
20834 // If keyNodes are adjacent in both ways,
20835 // we can add inBetween nodes to that shared way too..
20836 if (indexRange === 1 && inBetweenNodes.length) {
20837 var startIndex1 = way.nodes.lastIndexOf(startNode.id),
20838 endIndex1 = way.nodes.lastIndexOf(endNode.id),
20839 wayDirection1 = (endIndex1 - startIndex1);
20840 if (wayDirection1 < -1) { wayDirection1 = 1; }
20842 /* eslint-disable no-loop-func */
20843 _.each(_.without(graph.parentWays(keyNodes[i]), way), function(sharedWay) {
20844 if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
20845 var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id),
20846 endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id),
20847 wayDirection2 = (endIndex2 - startIndex2),
20848 insertAt = endIndex2;
20849 if (wayDirection2 < -1) { wayDirection2 = 1; }
20851 if (wayDirection1 !== wayDirection2) {
20852 inBetweenNodes.reverse();
20853 insertAt = startIndex2;
20855 for (j = 0; j < inBetweenNodes.length; j++) {
20856 sharedWay = sharedWay.addNode(inBetweenNodes[j], insertAt + j);
20858 graph = graph.replace(sharedWay);
20861 /* eslint-enable no-loop-func */
20866 // update the way to have all the new nodes
20867 ids = nodes.map(function(n) { return n.id; });
20870 way = way.update({nodes: ids});
20871 graph = graph.replace(way);
20876 action.makeConvex = function(graph) {
20877 var way = graph.entity(wayId),
20878 nodes = _.uniq(graph.childNodes(way)),
20879 points = nodes.map(function(n) { return projection(n.loc); }),
20880 sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
20881 hull = d3.geom.hull(points);
20883 // D3 convex hulls go counterclockwise..
20889 for (var i = 0; i < hull.length - 1; i++) {
20890 var startIndex = points.indexOf(hull[i]),
20891 endIndex = points.indexOf(hull[i+1]),
20892 indexRange = (endIndex - startIndex);
20894 if (indexRange < 0) {
20895 indexRange += nodes.length;
20898 // move interior nodes to the surface of the convex hull..
20899 for (var j = 1; j < indexRange; j++) {
20900 var point = iD.geo.interp(hull[i], hull[i+1], j / indexRange),
20901 node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
20902 graph = graph.replace(node);
20908 action.disabled = function(graph) {
20909 if (!graph.entity(wayId).isClosed())
20910 return 'not_closed';
20915 // Connect the ways at the given nodes.
20917 // The last node will survive. All other nodes will be replaced with
20918 // the surviving node in parent ways, and then removed.
20920 // Tags and relation memberships of of non-surviving nodes are merged
20921 // to the survivor.
20923 // This is the inverse of `iD.actions.Disconnect`.
20926 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
20927 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
20929 iD.actions.Connect = function(nodeIds) {
20930 return function(graph) {
20931 var survivor = graph.entity(_.last(nodeIds));
20933 for (var i = 0; i < nodeIds.length - 1; i++) {
20934 var node = graph.entity(nodeIds[i]);
20936 /* eslint-disable no-loop-func */
20937 graph.parentWays(node).forEach(function(parent) {
20938 if (!parent.areAdjacent(node.id, survivor.id)) {
20939 graph = graph.replace(parent.replaceNode(node.id, survivor.id));
20943 graph.parentRelations(node).forEach(function(parent) {
20944 graph = graph.replace(parent.replaceMember(node, survivor));
20946 /* eslint-enable no-loop-func */
20948 survivor = survivor.mergeTags(node.tags);
20949 graph = iD.actions.DeleteNode(node.id)(graph);
20952 graph = graph.replace(survivor);
20957 iD.actions.CopyEntity = function(id, fromGraph, deep) {
20958 var newEntities = [];
20960 var action = function(graph) {
20961 var entity = fromGraph.entity(id);
20963 newEntities = entity.copy(deep, fromGraph);
20965 for (var i = 0; i < newEntities.length; i++) {
20966 graph = graph.replace(newEntities[i]);
20972 action.newEntities = function() {
20973 return newEntities;
20978 iD.actions.DeleteMember = function(relationId, memberIndex) {
20979 return function(graph) {
20980 var relation = graph.entity(relationId)
20981 .removeMember(memberIndex);
20983 graph = graph.replace(relation);
20985 if (relation.isDegenerate())
20986 graph = iD.actions.DeleteRelation(relation.id)(graph);
20991 iD.actions.DeleteMultiple = function(ids) {
20993 way: iD.actions.DeleteWay,
20994 node: iD.actions.DeleteNode,
20995 relation: iD.actions.DeleteRelation
20998 var action = function(graph) {
20999 ids.forEach(function(id) {
21000 if (graph.hasEntity(id)) { // It may have been deleted aready.
21001 graph = actions[graph.entity(id).type](id)(graph);
21008 action.disabled = function(graph) {
21009 for (var i = 0; i < ids.length; i++) {
21011 disabled = actions[graph.entity(id).type](id).disabled(graph);
21012 if (disabled) return disabled;
21018 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
21019 iD.actions.DeleteNode = function(nodeId) {
21020 var action = function(graph) {
21021 var node = graph.entity(nodeId);
21023 graph.parentWays(node)
21024 .forEach(function(parent) {
21025 parent = parent.removeNode(nodeId);
21026 graph = graph.replace(parent);
21028 if (parent.isDegenerate()) {
21029 graph = iD.actions.DeleteWay(parent.id)(graph);
21033 graph.parentRelations(node)
21034 .forEach(function(parent) {
21035 parent = parent.removeMembersWithID(nodeId);
21036 graph = graph.replace(parent);
21038 if (parent.isDegenerate()) {
21039 graph = iD.actions.DeleteRelation(parent.id)(graph);
21043 return graph.remove(node);
21046 action.disabled = function() {
21052 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
21053 iD.actions.DeleteRelation = function(relationId) {
21054 function deleteEntity(entity, graph) {
21055 return !graph.parentWays(entity).length &&
21056 !graph.parentRelations(entity).length &&
21057 !entity.hasInterestingTags();
21060 var action = function(graph) {
21061 var relation = graph.entity(relationId);
21063 graph.parentRelations(relation)
21064 .forEach(function(parent) {
21065 parent = parent.removeMembersWithID(relationId);
21066 graph = graph.replace(parent);
21068 if (parent.isDegenerate()) {
21069 graph = iD.actions.DeleteRelation(parent.id)(graph);
21073 _.uniq(_.pluck(relation.members, 'id')).forEach(function(memberId) {
21074 graph = graph.replace(relation.removeMembersWithID(memberId));
21076 var entity = graph.entity(memberId);
21077 if (deleteEntity(entity, graph)) {
21078 graph = iD.actions.DeleteMultiple([memberId])(graph);
21082 return graph.remove(relation);
21085 action.disabled = function(graph) {
21086 if (!graph.entity(relationId).isComplete(graph))
21087 return 'incomplete_relation';
21092 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
21093 iD.actions.DeleteWay = function(wayId) {
21094 function deleteNode(node, graph) {
21095 return !graph.parentWays(node).length &&
21096 !graph.parentRelations(node).length &&
21097 !node.hasInterestingTags();
21100 var action = function(graph) {
21101 var way = graph.entity(wayId);
21103 graph.parentRelations(way)
21104 .forEach(function(parent) {
21105 parent = parent.removeMembersWithID(wayId);
21106 graph = graph.replace(parent);
21108 if (parent.isDegenerate()) {
21109 graph = iD.actions.DeleteRelation(parent.id)(graph);
21113 _.uniq(way.nodes).forEach(function(nodeId) {
21114 graph = graph.replace(way.removeNode(nodeId));
21116 var node = graph.entity(nodeId);
21117 if (deleteNode(node, graph)) {
21118 graph = graph.remove(node);
21122 return graph.remove(way);
21125 action.disabled = function(graph) {
21126 var disabled = false;
21128 graph.parentRelations(graph.entity(wayId)).forEach(function(parent) {
21129 var type = parent.tags.type,
21130 role = parent.memberById(wayId).role || 'outer';
21131 if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
21132 disabled = 'part_of_relation';
21141 iD.actions.DeprecateTags = function(entityId) {
21142 return function(graph) {
21143 var entity = graph.entity(entityId),
21144 newtags = _.clone(entity.tags),
21148 // This handles deprecated tags with a single condition
21149 for (var i = 0; i < iD.data.deprecated.length; i++) {
21151 rule = iD.data.deprecated[i];
21152 var match = _.pairs(rule.old)[0],
21153 replacements = rule.replace ? _.pairs(rule.replace) : null;
21155 if (entity.tags[match[0]] && match[1] === '*') {
21157 var value = entity.tags[match[0]];
21158 if (replacements && !newtags[replacements[0][0]]) {
21159 newtags[replacements[0][0]] = value;
21161 delete newtags[match[0]];
21164 } else if (entity.tags[match[0]] === match[1]) {
21165 newtags = _.assign({}, rule.replace || {}, _.omit(newtags, match[0]));
21171 return graph.replace(entity.update({tags: newtags}));
21177 iD.actions.DiscardTags = function(difference) {
21178 return function(graph) {
21179 function discardTags(entity) {
21180 if (!_.isEmpty(entity.tags)) {
21182 _.each(entity.tags, function(v, k) {
21183 if (v) tags[k] = v;
21186 graph = graph.replace(entity.update({
21187 tags: _.omit(tags, iD.data.discarded)
21192 difference.modified().forEach(discardTags);
21193 difference.created().forEach(discardTags);
21198 // Disconect the ways at the given node.
21200 // Optionally, disconnect only the given ways.
21202 // For testing convenience, accepts an ID to assign to the (first) new node.
21203 // Normally, this will be undefined and the way will automatically
21204 // be assigned a new ID.
21206 // This is the inverse of `iD.actions.Connect`.
21209 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
21210 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
21212 iD.actions.Disconnect = function(nodeId, newNodeId) {
21215 var action = function(graph) {
21216 var node = graph.entity(nodeId),
21217 connections = action.connections(graph);
21219 connections.forEach(function(connection) {
21220 var way = graph.entity(connection.wayID),
21221 newNode = iD.Node({id: newNodeId, loc: node.loc, tags: node.tags});
21223 graph = graph.replace(newNode);
21224 if (connection.index === 0 && way.isArea()) {
21225 // replace shared node with shared node..
21226 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
21228 // replace shared node with multiple new nodes..
21229 graph = graph.replace(way.updateNode(newNode.id, connection.index));
21236 action.connections = function(graph) {
21237 var candidates = [],
21239 parentWays = graph.parentWays(graph.entity(nodeId));
21241 parentWays.forEach(function(way) {
21242 if (wayIds && wayIds.indexOf(way.id) === -1) {
21246 if (way.isArea() && (way.nodes[0] === nodeId)) {
21247 candidates.push({wayID: way.id, index: 0});
21249 way.nodes.forEach(function(waynode, index) {
21250 if (waynode === nodeId) {
21251 candidates.push({wayID: way.id, index: index});
21257 return keeping ? candidates : candidates.slice(1);
21260 action.disabled = function(graph) {
21261 var connections = action.connections(graph);
21262 if (connections.length === 0 || (wayIds && wayIds.length !== connections.length))
21263 return 'not_connected';
21266 action.limitWays = function(_) {
21267 if (!arguments.length) return wayIds;
21274 // Join ways at the end node they share.
21276 // This is the inverse of `iD.actions.Split`.
21279 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
21280 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
21282 iD.actions.Join = function(ids) {
21284 function groupEntitiesByGeometry(graph) {
21285 var entities = ids.map(function(id) { return graph.entity(id); });
21286 return _.extend({line: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
21289 var action = function(graph) {
21290 var ways = ids.map(graph.entity, graph),
21291 survivor = ways[0];
21293 // Prefer to keep an existing way.
21294 for (var i = 0; i < ways.length; i++) {
21295 if (!ways[i].isNew()) {
21296 survivor = ways[i];
21301 var joined = iD.geo.joinWays(ways, graph)[0];
21303 survivor = survivor.update({nodes: _.pluck(joined.nodes, 'id')});
21304 graph = graph.replace(survivor);
21306 joined.forEach(function(way) {
21307 if (way.id === survivor.id)
21310 graph.parentRelations(way).forEach(function(parent) {
21311 graph = graph.replace(parent.replaceMember(way, survivor));
21314 survivor = survivor.mergeTags(way.tags);
21316 graph = graph.replace(survivor);
21317 graph = iD.actions.DeleteWay(way.id)(graph);
21323 action.disabled = function(graph) {
21324 var geometries = groupEntitiesByGeometry(graph);
21325 if (ids.length < 2 || ids.length !== geometries.line.length)
21326 return 'not_eligible';
21328 var joined = iD.geo.joinWays(ids.map(graph.entity, graph), graph);
21329 if (joined.length > 1)
21330 return 'not_adjacent';
21332 var nodeIds = _.pluck(joined[0].nodes, 'id').slice(1, -1),
21335 conflicting = false;
21337 joined[0].forEach(function(way) {
21338 var parents = graph.parentRelations(way);
21339 parents.forEach(function(parent) {
21340 if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; }))
21344 for (var k in way.tags) {
21345 if (!(k in tags)) {
21346 tags[k] = way.tags[k];
21347 } else if (tags[k] && iD.interestingTag(k) && tags[k] !== way.tags[k]) {
21348 conflicting = true;
21354 return 'restriction';
21357 return 'conflicting_tags';
21362 iD.actions.Merge = function(ids) {
21363 function groupEntitiesByGeometry(graph) {
21364 var entities = ids.map(function(id) { return graph.entity(id); });
21365 return _.extend({point: [], area: [], line: [], relation: []},
21366 _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
21369 var action = function(graph) {
21370 var geometries = groupEntitiesByGeometry(graph),
21371 target = geometries.area[0] || geometries.line[0],
21372 points = geometries.point;
21374 points.forEach(function(point) {
21375 target = target.mergeTags(point.tags);
21377 graph.parentRelations(point).forEach(function(parent) {
21378 graph = graph.replace(parent.replaceMember(point, target));
21381 graph = graph.remove(point);
21384 graph = graph.replace(target);
21389 action.disabled = function(graph) {
21390 var geometries = groupEntitiesByGeometry(graph);
21391 if (geometries.point.length === 0 ||
21392 (geometries.area.length + geometries.line.length) !== 1 ||
21393 geometries.relation.length !== 0)
21394 return 'not_eligible';
21399 iD.actions.MergePolygon = function(ids, newRelationId) {
21401 function groupEntities(graph) {
21402 var entities = ids.map(function (id) { return graph.entity(id); });
21407 }, _.groupBy(entities, function(entity) {
21408 if (entity.type === 'way' && entity.isClosed()) {
21409 return 'closedWay';
21410 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
21411 return 'multipolygon';
21418 var action = function(graph) {
21419 var entities = groupEntities(graph);
21421 // An array representing all the polygons that are part of the multipolygon.
21423 // Each element is itself an array of objects with an id property, and has a
21424 // locs property which is an array of the locations forming the polygon.
21425 var polygons = entities.multipolygon.reduce(function(polygons, m) {
21426 return polygons.concat(iD.geo.joinWays(m.members, graph));
21427 }, []).concat(entities.closedWay.map(function(d) {
21428 var member = [{id: d.id}];
21429 member.nodes = graph.childNodes(d);
21433 // contained is an array of arrays of boolean values,
21434 // where contained[j][k] is true iff the jth way is
21435 // contained by the kth way.
21436 var contained = polygons.map(function(w, i) {
21437 return polygons.map(function(d, n) {
21438 if (i === n) return null;
21439 return iD.geo.polygonContainsPolygon(
21440 _.pluck(d.nodes, 'loc'),
21441 _.pluck(w.nodes, 'loc'));
21445 // Sort all polygons as either outer or inner ways
21449 while (polygons.length) {
21450 extractUncontained(polygons);
21451 polygons = polygons.filter(isContained);
21452 contained = contained.filter(isContained).map(filterContained);
21455 function isContained(d, i) {
21456 return _.any(contained[i]);
21459 function filterContained(d) {
21460 return d.filter(isContained);
21463 function extractUncontained(polygons) {
21464 polygons.forEach(function(d, i) {
21465 if (!isContained(d, i)) {
21466 d.forEach(function(member) {
21470 role: outer ? 'outer' : 'inner'
21478 // Move all tags to one relation
21479 var relation = entities.multipolygon[0] ||
21480 iD.Relation({ id: newRelationId, tags: { type: 'multipolygon' }});
21482 entities.multipolygon.slice(1).forEach(function(m) {
21483 relation = relation.mergeTags(m.tags);
21484 graph = graph.remove(m);
21487 entities.closedWay.forEach(function(way) {
21488 function isThisOuter(m) {
21489 return m.id === way.id && m.role !== 'inner';
21491 if (members.some(isThisOuter)) {
21492 relation = relation.mergeTags(way.tags);
21493 graph = graph.replace(way.update({ tags: {} }));
21497 return graph.replace(relation.update({
21499 tags: _.omit(relation.tags, 'area')
21503 action.disabled = function(graph) {
21504 var entities = groupEntities(graph);
21505 if (entities.other.length > 0 ||
21506 entities.closedWay.length + entities.multipolygon.length < 2)
21507 return 'not_eligible';
21508 if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); }))
21509 return 'incomplete_relation';
21514 iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser) {
21515 var option = 'safe', // 'safe', 'force_local', 'force_remote'
21519 return _.isFunction(formatUser) ? formatUser(d) : d;
21523 function mergeLocation(remote, target) {
21524 function pointEqual(a, b) {
21525 var epsilon = 1e-6;
21526 return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon);
21529 if (option === 'force_local' || pointEqual(target.loc, remote.loc)) {
21532 if (option === 'force_remote') {
21533 return target.update({loc: remote.loc});
21536 conflicts.push(t('merge_remote_changes.conflict.location', { user: user(remote.user) }));
21541 function mergeNodes(base, remote, target) {
21542 if (option === 'force_local' || _.isEqual(target.nodes, remote.nodes)) {
21545 if (option === 'force_remote') {
21546 return target.update({nodes: remote.nodes});
21549 var ccount = conflicts.length,
21550 o = base.nodes || [],
21551 a = target.nodes || [],
21552 b = remote.nodes || [],
21554 hunks = Diff3.diff3_merge(a, o, b, true);
21556 for (var i = 0; i < hunks.length; i++) {
21557 var hunk = hunks[i];
21559 nodes.push.apply(nodes, hunk.ok);
21561 // for all conflicts, we can assume c.a !== c.b
21562 // because `diff3_merge` called with `true` option to exclude false conflicts..
21563 var c = hunk.conflict;
21564 if (_.isEqual(c.o, c.a)) { // only changed remotely
21565 nodes.push.apply(nodes, c.b);
21566 } else if (_.isEqual(c.o, c.b)) { // only changed locally
21567 nodes.push.apply(nodes, c.a);
21568 } else { // changed both locally and remotely
21569 conflicts.push(t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) }));
21575 return (conflicts.length === ccount) ? target.update({nodes: nodes}) : target;
21579 function mergeChildren(targetWay, children, updates, graph) {
21580 function isUsed(node, targetWay) {
21581 var parentWays = _.pluck(graph.parentWays(node), 'id');
21582 return node.hasInterestingTags() ||
21583 _.without(parentWays, targetWay.id).length > 0 ||
21584 graph.parentRelations(node).length > 0;
21587 var ccount = conflicts.length;
21589 for (var i = 0; i < children.length; i++) {
21590 var id = children[i],
21591 node = graph.hasEntity(id);
21593 // remove unused childNodes..
21594 if (targetWay.nodes.indexOf(id) === -1) {
21595 if (node && !isUsed(node, targetWay)) {
21596 updates.removeIds.push(id);
21601 // restore used childNodes..
21602 var local = localGraph.hasEntity(id),
21603 remote = remoteGraph.hasEntity(id),
21606 if (option === 'force_remote' && remote && remote.visible) {
21607 updates.replacements.push(remote);
21609 } else if (option === 'force_local' && local) {
21610 target = iD.Entity(local);
21612 target = target.update({ version: remote.version });
21614 updates.replacements.push(target);
21616 } else if (option === 'safe' && local && remote && local.version !== remote.version) {
21617 target = iD.Entity(local, { version: remote.version });
21618 if (remote.visible) {
21619 target = mergeLocation(remote, target);
21621 conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
21624 if (conflicts.length !== ccount) break;
21625 updates.replacements.push(target);
21633 function updateChildren(updates, graph) {
21634 for (var i = 0; i < updates.replacements.length; i++) {
21635 graph = graph.replace(updates.replacements[i]);
21637 if (updates.removeIds.length) {
21638 graph = iD.actions.DeleteMultiple(updates.removeIds)(graph);
21644 function mergeMembers(remote, target) {
21645 if (option === 'force_local' || _.isEqual(target.members, remote.members)) {
21648 if (option === 'force_remote') {
21649 return target.update({members: remote.members});
21652 conflicts.push(t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) }));
21657 function mergeTags(base, remote, target) {
21658 function ignoreKey(k) {
21659 return _.contains(iD.data.discarded, k);
21662 if (option === 'force_local' || _.isEqual(target.tags, remote.tags)) {
21665 if (option === 'force_remote') {
21666 return target.update({tags: remote.tags});
21669 var ccount = conflicts.length,
21670 o = base.tags || {},
21671 a = target.tags || {},
21672 b = remote.tags || {},
21673 keys = _.reject(_.union(_.keys(o), _.keys(a), _.keys(b)), ignoreKey),
21677 for (var i = 0; i < keys.length; i++) {
21680 if (o[k] !== b[k] && a[k] !== b[k]) { // changed remotely..
21681 if (o[k] !== a[k]) { // changed locally..
21682 conflicts.push(t('merge_remote_changes.conflict.tags',
21683 { tag: k, local: a[k], remote: b[k], user: user(remote.user) }));
21685 } else { // unchanged locally, accept remote change..
21686 if (b.hasOwnProperty(k)) {
21696 return (changed && conflicts.length === ccount) ? target.update({tags: tags}) : target;
21700 // `graph.base()` is the common ancestor of the two graphs.
21701 // `localGraph` contains user's edits up to saving
21702 // `remoteGraph` contains remote edits to modified nodes
21703 // `graph` must be a descendent of `localGraph` and may include
21704 // some conflict resolution actions performed on it.
21706 // --- ... --- `localGraph` -- ... -- `graph`
21708 // `graph.base()` --- ... --- `remoteGraph`
21710 var action = function(graph) {
21711 var updates = { replacements: [], removeIds: [] },
21712 base = graph.base().entities[id],
21713 local = localGraph.entity(id),
21714 remote = remoteGraph.entity(id),
21715 target = iD.Entity(local, { version: remote.version });
21718 if (!remote.visible) {
21719 if (option === 'force_remote') {
21720 return iD.actions.DeleteMultiple([id])(graph);
21722 } else if (option === 'force_local') {
21723 if (target.type === 'way') {
21724 target = mergeChildren(target, _.uniq(local.nodes), updates, graph);
21725 graph = updateChildren(updates, graph);
21727 return graph.replace(target);
21730 conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
21731 return graph; // do nothing
21736 if (target.type === 'node') {
21737 target = mergeLocation(remote, target);
21739 } else if (target.type === 'way') {
21740 // pull in any child nodes that may not be present locally..
21741 graph.rebase(remoteGraph.childNodes(remote), [graph], false);
21742 target = mergeNodes(base, remote, target);
21743 target = mergeChildren(target, _.union(local.nodes, remote.nodes), updates, graph);
21745 } else if (target.type === 'relation') {
21746 target = mergeMembers(remote, target);
21749 target = mergeTags(base, remote, target);
21751 if (!conflicts.length) {
21752 graph = updateChildren(updates, graph).replace(target);
21758 action.withOption = function(opt) {
21763 action.conflicts = function() {
21769 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
21770 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
21771 iD.actions.Move = function(moveIds, tryDelta, projection, cache) {
21772 var delta = tryDelta;
21774 function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; }
21775 function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
21777 function setupCache(graph) {
21778 function canMove(nodeId) {
21779 var parents = _.pluck(graph.parentWays(graph.entity(nodeId)), 'id');
21780 if (parents.length < 3) return true;
21782 // Don't move a vertex where >2 ways meet, unless all parentWays are moving too..
21783 var parentsMoving = _.all(parents, function(id) { return cache.moving[id]; });
21784 if (!parentsMoving) delete cache.moving[nodeId];
21786 return parentsMoving;
21789 function cacheEntities(ids) {
21790 _.each(ids, function(id) {
21791 if (cache.moving[id]) return;
21792 cache.moving[id] = true;
21794 var entity = graph.hasEntity(id);
21795 if (!entity) return;
21797 if (entity.type === 'node') {
21798 cache.nodes.push(id);
21799 cache.startLoc[id] = entity.loc;
21800 } else if (entity.type === 'way') {
21801 cache.ways.push(id);
21802 cacheEntities(entity.nodes);
21804 cacheEntities(_.pluck(entity.members, 'id'));
21809 function cacheIntersections(ids) {
21810 function isEndpoint(way, id) { return !way.isClosed() && !!way.affix(id); }
21812 _.each(ids, function(id) {
21813 // consider only intersections with 1 moved and 1 unmoved way.
21814 _.each(graph.childNodes(graph.entity(id)), function(node) {
21815 var parents = graph.parentWays(node);
21816 if (parents.length !== 2) return;
21818 var moved = graph.entity(id),
21819 unmoved = _.find(parents, function(way) { return !cache.moving[way.id]; });
21820 if (!unmoved) return;
21822 // exclude ways that are overly connected..
21823 if (_.intersection(moved.nodes, unmoved.nodes).length > 2) return;
21825 if (moved.isArea() || unmoved.isArea()) return;
21827 cache.intersection[node.id] = {
21830 unmovedId: unmoved.id,
21831 movedIsEP: isEndpoint(moved, node.id),
21832 unmovedIsEP: isEndpoint(unmoved, node.id)
21844 cache.intersection = {};
21845 cache.replacedVertex = {};
21846 cache.startLoc = {};
21850 cacheEntities(moveIds);
21851 cacheIntersections(cache.ways);
21852 cache.nodes = _.filter(cache.nodes, canMove);
21859 // Place a vertex where the moved vertex used to be, to preserve way shape..
21860 function replaceMovedVertex(nodeId, wayId, graph, delta) {
21861 var way = graph.entity(wayId),
21862 moved = graph.entity(nodeId),
21863 movedIndex = way.nodes.indexOf(nodeId),
21864 len, prevIndex, nextIndex;
21866 if (way.isClosed()) {
21867 len = way.nodes.length - 1;
21868 prevIndex = (movedIndex + len - 1) % len;
21869 nextIndex = (movedIndex + len + 1) % len;
21871 len = way.nodes.length;
21872 prevIndex = movedIndex - 1;
21873 nextIndex = movedIndex + 1;
21876 var prev = graph.hasEntity(way.nodes[prevIndex]),
21877 next = graph.hasEntity(way.nodes[nextIndex]);
21879 // Don't add orig vertex at endpoint..
21880 if (!prev || !next) return graph;
21882 var key = wayId + '_' + nodeId,
21883 orig = cache.replacedVertex[key];
21886 cache.replacedVertex[key] = orig;
21887 cache.startLoc[orig.id] = cache.startLoc[nodeId];
21892 start = projection(cache.startLoc[nodeId]);
21893 end = projection.invert(vecAdd(start, delta));
21895 end = cache.startLoc[nodeId];
21897 orig = orig.move(end);
21899 var angle = Math.abs(iD.geo.angle(orig, prev, projection) -
21900 iD.geo.angle(orig, next, projection)) * 180 / Math.PI;
21902 // Don't add orig vertex if it would just make a straight line..
21903 if (angle > 175 && angle < 185) return graph;
21905 // Don't add orig vertex if another point is already nearby (within 10m)
21906 if (iD.geo.sphericalDistance(prev.loc, orig.loc) < 10 ||
21907 iD.geo.sphericalDistance(orig.loc, next.loc) < 10) return graph;
21909 // moving forward or backward along way?
21910 var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection),
21911 p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection),
21912 d1 = iD.geo.pathLength(p1),
21913 d2 = iD.geo.pathLength(p2),
21914 insertAt = (d1 < d2) ? movedIndex : nextIndex;
21916 // moving around closed loop?
21917 if (way.isClosed() && insertAt === 0) insertAt = len;
21919 way = way.addNode(orig.id, insertAt);
21920 return graph.replace(orig).replace(way);
21923 // Reorder nodes around intersections that have moved..
21924 function unZorroIntersection(intersection, graph) {
21925 var vertex = graph.entity(intersection.nodeId),
21926 way1 = graph.entity(intersection.movedId),
21927 way2 = graph.entity(intersection.unmovedId),
21928 isEP1 = intersection.movedIsEP,
21929 isEP2 = intersection.unmovedIsEP;
21931 // don't move the vertex if it is the endpoint of both ways.
21932 if (isEP1 && isEP2) return graph;
21934 var nodes1 = _.without(graph.childNodes(way1), vertex),
21935 nodes2 = _.without(graph.childNodes(way2), vertex);
21937 if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
21938 if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
21940 var edge1 = !isEP1 && iD.geo.chooseEdge(nodes1, projection(vertex.loc), projection),
21941 edge2 = !isEP2 && iD.geo.chooseEdge(nodes2, projection(vertex.loc), projection),
21944 // snap vertex to nearest edge (or some point between them)..
21945 if (!isEP1 && !isEP2) {
21946 var epsilon = 1e-4, maxIter = 10;
21947 for (var i = 0; i < maxIter; i++) {
21948 loc = iD.geo.interp(edge1.loc, edge2.loc, 0.5);
21949 edge1 = iD.geo.chooseEdge(nodes1, projection(loc), projection);
21950 edge2 = iD.geo.chooseEdge(nodes2, projection(loc), projection);
21951 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
21953 } else if (!isEP1) {
21959 graph = graph.replace(vertex.move(loc));
21961 // if zorro happened, reorder nodes..
21962 if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
21963 way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
21964 graph = graph.replace(way1);
21966 if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
21967 way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
21968 graph = graph.replace(way2);
21975 function cleanupIntersections(graph) {
21976 _.each(cache.intersection, function(obj) {
21977 graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, delta);
21978 graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
21979 graph = unZorroIntersection(obj, graph);
21985 // check if moving way endpoint can cross an unmoved way, if so limit delta..
21986 function limitDelta(graph) {
21987 _.each(cache.intersection, function(obj) {
21988 if (!obj.movedIsEP) return;
21990 var node = graph.entity(obj.nodeId),
21991 start = projection(node.loc),
21992 end = vecAdd(start, delta),
21993 movedNodes = graph.childNodes(graph.entity(obj.movedId)),
21994 movedPath = _.map(_.pluck(movedNodes, 'loc'),
21995 function(loc) { return vecAdd(projection(loc), delta); }),
21996 unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId)),
21997 unmovedPath = _.map(_.pluck(unmovedNodes, 'loc'), projection),
21998 hits = iD.geo.pathIntersections(movedPath, unmovedPath);
22000 for (var i = 0; i < hits.length; i++) {
22001 if (_.isEqual(hits[i], end)) continue;
22002 var edge = iD.geo.chooseEdge(unmovedNodes, end, projection);
22003 delta = vecSub(projection(edge.loc), start);
22009 var action = function(graph) {
22010 if (delta[0] === 0 && delta[1] === 0) return graph;
22014 if (!_.isEmpty(cache.intersection)) {
22018 _.each(cache.nodes, function(id) {
22019 var node = graph.entity(id),
22020 start = projection(node.loc),
22021 end = vecAdd(start, delta);
22022 graph = graph.replace(node.move(projection.invert(end)));
22025 if (!_.isEmpty(cache.intersection)) {
22026 graph = cleanupIntersections(graph);
22032 action.disabled = function(graph) {
22033 function incompleteRelation(id) {
22034 var entity = graph.entity(id);
22035 return entity.type === 'relation' && !entity.isComplete(graph);
22038 if (_.any(moveIds, incompleteRelation))
22039 return 'incomplete_relation';
22042 action.delta = function() {
22048 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
22049 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
22050 iD.actions.MoveNode = function(nodeId, loc) {
22051 return function(graph) {
22052 return graph.replace(graph.entity(nodeId).move(loc));
22055 iD.actions.Noop = function() {
22056 return function(graph) {
22061 * Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
22064 iD.actions.Orthogonalize = function(wayId, projection) {
22065 var threshold = 12, // degrees within right or straight to alter
22066 lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180),
22067 upperThreshold = Math.cos(threshold * Math.PI / 180);
22069 var action = function(graph) {
22070 var way = graph.entity(wayId),
22071 nodes = graph.childNodes(way),
22072 points = _.uniq(nodes).map(function(n) { return projection(n.loc); }),
22073 corner = {i: 0, dotp: 1},
22075 i, j, score, motions;
22077 if (nodes.length === 4) {
22078 for (i = 0; i < 1000; i++) {
22079 motions = points.map(calcMotion);
22080 points[corner.i] = addPoints(points[corner.i],motions[corner.i]);
22081 score = corner.dotp;
22082 if (score < epsilon) {
22087 graph = graph.replace(graph.entity(nodes[corner.i].id)
22088 .move(projection.invert(points[corner.i])));
22091 originalPoints = _.clone(points);
22094 for (i = 0; i < 1000; i++) {
22095 motions = points.map(calcMotion);
22096 for (j = 0; j < motions.length; j++) {
22097 points[j] = addPoints(points[j],motions[j]);
22099 var newScore = squareness(points);
22100 if (newScore < score) {
22101 best = _.clone(points);
22104 if (score < epsilon) {
22111 for (i = 0; i < points.length; i++) {
22112 // only move the points that actually moved
22113 if (originalPoints[i][0] !== points[i][0] || originalPoints[i][1] !== points[i][1]) {
22114 graph = graph.replace(graph.entity(nodes[i].id)
22115 .move(projection.invert(points[i])));
22119 // remove empty nodes on straight sections
22120 for (i = 0; i < points.length; i++) {
22121 var node = nodes[i];
22123 if (graph.parentWays(node).length > 1 ||
22124 graph.parentRelations(node).length ||
22125 node.hasInterestingTags()) {
22130 var dotp = normalizedDotProduct(i, points);
22131 if (dotp < -1 + epsilon) {
22132 graph = iD.actions.DeleteNode(nodes[i].id)(graph);
22139 function calcMotion(b, i, array) {
22140 var a = array[(i - 1 + array.length) % array.length],
22141 c = array[(i + 1) % array.length],
22142 p = subtractPoints(a, b),
22143 q = subtractPoints(c, b),
22146 scale = 2 * Math.min(iD.geo.euclideanDistance(p, [0, 0]), iD.geo.euclideanDistance(q, [0, 0]));
22147 p = normalizePoint(p, 1.0);
22148 q = normalizePoint(q, 1.0);
22150 dotp = filterDotProduct(p[0] * q[0] + p[1] * q[1]);
22152 // nasty hack to deal with almost-straight segments (angle is closer to 180 than to 90/270).
22153 if (array.length > 3) {
22154 if (dotp < -0.707106781186547) {
22157 } else if (dotp && Math.abs(dotp) < corner.dotp) {
22159 corner.dotp = Math.abs(dotp);
22162 return normalizePoint(addPoints(p, q), 0.1 * dotp * scale);
22166 function squareness(points) {
22167 return points.reduce(function(sum, val, i, array) {
22168 var dotp = normalizedDotProduct(i, array);
22170 dotp = filterDotProduct(dotp);
22171 return sum + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
22175 function normalizedDotProduct(i, points) {
22176 var a = points[(i - 1 + points.length) % points.length],
22178 c = points[(i + 1) % points.length],
22179 p = subtractPoints(a, b),
22180 q = subtractPoints(c, b);
22182 p = normalizePoint(p, 1.0);
22183 q = normalizePoint(q, 1.0);
22185 return p[0] * q[0] + p[1] * q[1];
22188 function subtractPoints(a, b) {
22189 return [a[0] - b[0], a[1] - b[1]];
22192 function addPoints(a, b) {
22193 return [a[0] + b[0], a[1] + b[1]];
22196 function normalizePoint(point, scale) {
22197 var vector = [0, 0];
22198 var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]);
22199 if (length !== 0) {
22200 vector[0] = point[0] / length;
22201 vector[1] = point[1] / length;
22204 vector[0] *= scale;
22205 vector[1] *= scale;
22210 function filterDotProduct(dotp) {
22211 if (lowerThreshold > Math.abs(dotp) || Math.abs(dotp) > upperThreshold) {
22218 action.disabled = function(graph) {
22219 var way = graph.entity(wayId),
22220 nodes = graph.childNodes(way),
22221 points = _.uniq(nodes).map(function(n) { return projection(n.loc); });
22223 if (squareness(points)) {
22227 return 'not_squarish';
22232 // Create a restriction relation for `turn`, which must have the following structure:
22235 // from: { node: <node ID>, way: <way ID> },
22236 // via: { node: <node ID> },
22237 // to: { node: <node ID>, way: <way ID> },
22238 // restriction: <'no_right_turn', 'no_left_turn', etc.>
22241 // This specifies a restriction of type `restriction` when traveling from
22242 // `from.node` in `from.way` toward `to.node` in `to.way` via `via.node`.
22243 // (The action does not check that these entities form a valid intersection.)
22245 // If `restriction` is not provided, it is automatically determined by
22246 // iD.geo.inferRestriction.
22248 // If necessary, the `from` and `to` ways are split. In these cases, `from.node`
22249 // and `to.node` are used to determine which portion of the split ways become
22250 // members of the restriction.
22252 // For testing convenience, accepts an ID to assign to the new relation.
22253 // Normally, this will be undefined and the relation will automatically
22254 // be assigned a new ID.
22256 iD.actions.RestrictTurn = function(turn, projection, restrictionId) {
22257 return function(graph) {
22258 var from = graph.entity(turn.from.way),
22259 via = graph.entity(turn.via.node),
22260 to = graph.entity(turn.to.way);
22262 function isClosingNode(way, nodeId) {
22263 return nodeId === way.first() && nodeId === way.last();
22266 function split(toOrFrom) {
22267 var newID = toOrFrom.newID || iD.Way().id;
22268 graph = iD.actions.Split(via.id, [newID])
22269 .limitWays([toOrFrom.way])(graph);
22271 var a = graph.entity(newID),
22272 b = graph.entity(toOrFrom.way);
22274 if (a.nodes.indexOf(toOrFrom.node) !== -1) {
22281 if (!from.affix(via.id) || isClosingNode(from, via.id)) {
22282 if (turn.from.node === turn.to.node) {
22284 from = to = split(turn.from)[0];
22285 } else if (turn.from.way === turn.to.way) {
22286 // Straight-on or circular
22287 var s = split(turn.from);
22292 from = split(turn.from)[0];
22296 if (!to.affix(via.id) || isClosingNode(to, via.id)) {
22297 to = split(turn.to)[0];
22300 return graph.replace(iD.Relation({
22303 type: 'restriction',
22304 restriction: turn.restriction ||
22305 iD.geo.inferRestriction(
22313 {id: from.id, type: 'way', role: 'from'},
22314 {id: via.id, type: 'node', role: 'via'},
22315 {id: to.id, type: 'way', role: 'to'}
22321 Order the nodes of a way in reverse order and reverse any direction dependent tags
22322 other than `oneway`. (We assume that correcting a backwards oneway is the primary
22323 reason for reversing a way.)
22325 The following transforms are performed:
22328 *:right=* ⟺ *:left=*
22329 *:forward=* ⟺ *:backward=*
22330 direction=up ⟺ direction=down
22331 incline=up ⟺ incline=down
22335 role=forward ⟺ role=backward
22336 role=north ⟺ role=south
22337 role=east ⟺ role=west
22339 In addition, numeric-valued `incline` tags are negated.
22341 The JOSM implementation was used as a guide, but transformations that were of unclear benefit
22342 or adjusted tags that don't seem to be used in practice were omitted.
22345 http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
22346 http://wiki.openstreetmap.org/wiki/Key:direction#Steps
22347 http://wiki.openstreetmap.org/wiki/Key:incline
22348 http://wiki.openstreetmap.org/wiki/Route#Members
22349 http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
22351 iD.actions.Reverse = function(wayId, options) {
22352 var replacements = [
22353 [/:right$/, ':left'], [/:left$/, ':right'],
22354 [/:forward$/, ':backward'], [/:backward$/, ':forward']
22356 numeric = /^([+\-]?)(?=[\d.])/,
22358 forward: 'backward',
22359 backward: 'forward',
22366 function reverseKey(key) {
22367 for (var i = 0; i < replacements.length; ++i) {
22368 var replacement = replacements[i];
22369 if (replacement[0].test(key)) {
22370 return key.replace(replacement[0], replacement[1]);
22376 function reverseValue(key, value) {
22377 if (key === 'incline' && numeric.test(value)) {
22378 return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
22379 } else if (key === 'incline' || key === 'direction') {
22380 return {up: 'down', down: 'up'}[value] || value;
22381 } else if (options && options.reverseOneway && key === 'oneway') {
22382 return {yes: '-1', '1': '-1', '-1': 'yes'}[value] || value;
22384 return {left: 'right', right: 'left'}[value] || value;
22388 return function(graph) {
22389 var way = graph.entity(wayId),
22390 nodes = way.nodes.slice().reverse(),
22391 tags = {}, key, role;
22393 for (key in way.tags) {
22394 tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
22397 graph.parentRelations(way).forEach(function(relation) {
22398 relation.members.forEach(function(member, index) {
22399 if (member.id === way.id && (role = roleReversals[member.role])) {
22400 relation = relation.updateMember({role: role}, index);
22401 graph = graph.replace(relation);
22406 return graph.replace(way.update({nodes: nodes, tags: tags}));
22409 iD.actions.Revert = function(id) {
22411 var action = function(graph) {
22412 var entity = graph.hasEntity(id),
22413 base = graph.base().entities[id];
22415 if (entity && !base) { // entity will be removed..
22416 if (entity.type === 'node') {
22417 graph.parentWays(entity)
22418 .forEach(function(parent) {
22419 parent = parent.removeNode(id);
22420 graph = graph.replace(parent);
22422 if (parent.isDegenerate()) {
22423 graph = iD.actions.DeleteWay(parent.id)(graph);
22428 graph.parentRelations(entity)
22429 .forEach(function(parent) {
22430 parent = parent.removeMembersWithID(id);
22431 graph = graph.replace(parent);
22433 if (parent.isDegenerate()) {
22434 graph = iD.actions.DeleteRelation(parent.id)(graph);
22439 return graph.revert(id);
22444 iD.actions.RotateWay = function(wayId, pivot, angle, projection) {
22445 return function(graph) {
22446 return graph.update(function(graph) {
22447 var way = graph.entity(wayId);
22449 _.unique(way.nodes).forEach(function(id) {
22451 var node = graph.entity(id),
22452 point = projection(node.loc),
22455 radial[0] = point[0] - pivot[0];
22456 radial[1] = point[1] - pivot[1];
22459 radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + pivot[0],
22460 radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + pivot[1]
22463 graph = graph.replace(node.move(projection.invert(point)));
22470 // Split a way at the given node.
22472 // Optionally, split only the given ways, if multiple ways share
22475 // This is the inverse of `iD.actions.Join`.
22477 // For testing convenience, accepts an ID to assign to the new way.
22478 // Normally, this will be undefined and the way will automatically
22479 // be assigned a new ID.
22482 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
22484 iD.actions.Split = function(nodeId, newWayIds) {
22487 // if the way is closed, we need to search for a partner node
22488 // to split the way at.
22490 // The following looks for a node that is both far away from
22491 // the initial node in terms of way segment length and nearby
22492 // in terms of beeline-distance. This assures that areas get
22493 // split on the most "natural" points (independent of the number
22495 // For example: bone-shaped areas get split across their waist
22496 // line, circles across the diameter.
22497 function splitArea(nodes, idxA, graph) {
22498 var lengths = new Array(nodes.length),
22504 function wrap(index) {
22505 return iD.util.wrap(index, nodes.length);
22508 function dist(nA, nB) {
22509 return iD.geo.sphericalDistance(graph.entity(nA).loc, graph.entity(nB).loc);
22512 // calculate lengths
22514 for (i = wrap(idxA+1); i !== idxA; i = wrap(i+1)) {
22515 length += dist(nodes[i], nodes[wrap(i-1)]);
22516 lengths[i] = length;
22520 for (i = wrap(idxA-1); i !== idxA; i = wrap(i-1)) {
22521 length += dist(nodes[i], nodes[wrap(i+1)]);
22522 if (length < lengths[i])
22523 lengths[i] = length;
22526 // determine best opposite node to split
22527 for (i = 0; i < nodes.length; i++) {
22528 var cost = lengths[i] / dist(nodes[idxA], nodes[i]);
22538 function split(graph, wayA, newWayId) {
22539 var wayB = iD.Way({id: newWayId, tags: wayA.tags}),
22542 isArea = wayA.isArea(),
22543 isOuter = iD.geo.isSimpleMultipolygonOuterMember(wayA, graph);
22545 if (wayA.isClosed()) {
22546 var nodes = wayA.nodes.slice(0, -1),
22547 idxA = _.indexOf(nodes, nodeId),
22548 idxB = splitArea(nodes, idxA, graph);
22551 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
22552 nodesB = nodes.slice(idxB, idxA + 1);
22554 nodesA = nodes.slice(idxA, idxB + 1);
22555 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
22558 var idx = _.indexOf(wayA.nodes, nodeId, 1);
22559 nodesA = wayA.nodes.slice(0, idx + 1);
22560 nodesB = wayA.nodes.slice(idx);
22563 wayA = wayA.update({nodes: nodesA});
22564 wayB = wayB.update({nodes: nodesB});
22566 graph = graph.replace(wayA);
22567 graph = graph.replace(wayB);
22569 graph.parentRelations(wayA).forEach(function(relation) {
22570 if (relation.isRestriction()) {
22571 var via = relation.memberByRole('via');
22572 if (via && wayB.contains(via.id)) {
22573 relation = relation.updateMember({id: wayB.id}, relation.memberById(wayA.id).index);
22574 graph = graph.replace(relation);
22577 if (relation === isOuter) {
22578 graph = graph.replace(relation.mergeTags(wayA.tags));
22579 graph = graph.replace(wayA.update({tags: {}}));
22580 graph = graph.replace(wayB.update({tags: {}}));
22586 role: relation.memberById(wayA.id).role
22589 graph = iD.actions.AddMember(relation.id, member)(graph);
22593 if (!isOuter && isArea) {
22594 var multipolygon = iD.Relation({
22595 tags: _.extend({}, wayA.tags, {type: 'multipolygon'}),
22597 {id: wayA.id, role: 'outer', type: 'way'},
22598 {id: wayB.id, role: 'outer', type: 'way'}
22601 graph = graph.replace(multipolygon);
22602 graph = graph.replace(wayA.update({tags: {}}));
22603 graph = graph.replace(wayB.update({tags: {}}));
22609 var action = function(graph) {
22610 var candidates = action.ways(graph);
22611 for (var i = 0; i < candidates.length; i++) {
22612 graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
22617 action.ways = function(graph) {
22618 var node = graph.entity(nodeId),
22619 parents = graph.parentWays(node),
22620 hasLines = _.any(parents, function(parent) { return parent.geometry(graph) === 'line'; });
22622 return parents.filter(function(parent) {
22623 if (wayIds && wayIds.indexOf(parent.id) === -1)
22626 if (!wayIds && hasLines && parent.geometry(graph) !== 'line')
22629 if (parent.isClosed()) {
22633 for (var i = 1; i < parent.nodes.length - 1; i++) {
22634 if (parent.nodes[i] === nodeId) {
22643 action.disabled = function(graph) {
22644 var candidates = action.ways(graph);
22645 if (candidates.length === 0 || (wayIds && wayIds.length !== candidates.length))
22646 return 'not_eligible';
22649 action.limitWays = function(_) {
22650 if (!arguments.length) return wayIds;
22658 * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
22661 iD.actions.Straighten = function(wayId, projection) {
22662 function positionAlongWay(n, s, e) {
22663 return ((n[0] - s[0]) * (e[0] - s[0]) + (n[1] - s[1]) * (e[1] - s[1]))/
22664 (Math.pow(e[0] - s[0], 2) + Math.pow(e[1] - s[1], 2));
22667 var action = function(graph) {
22668 var way = graph.entity(wayId),
22669 nodes = graph.childNodes(way),
22670 points = nodes.map(function(n) { return projection(n.loc); }),
22671 startPoint = points[0],
22672 endPoint = points[points.length-1],
22676 for (i = 1; i < points.length-1; i++) {
22677 var node = nodes[i],
22680 if (graph.parentWays(node).length > 1 ||
22681 graph.parentRelations(node).length ||
22682 node.hasInterestingTags()) {
22684 var u = positionAlongWay(point, startPoint, endPoint),
22685 p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
22686 p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]);
22688 graph = graph.replace(graph.entity(node.id)
22689 .move(projection.invert([p0, p1])));
22692 if (toDelete.indexOf(node) === -1) {
22693 toDelete.push(node);
22698 for (i = 0; i < toDelete.length; i++) {
22699 graph = iD.actions.DeleteNode(toDelete[i].id)(graph);
22705 action.disabled = function(graph) {
22706 // check way isn't too bendy
22707 var way = graph.entity(wayId),
22708 nodes = graph.childNodes(way),
22709 points = nodes.map(function(n) { return projection(n.loc); }),
22710 startPoint = points[0],
22711 endPoint = points[points.length-1],
22712 threshold = 0.2 * Math.sqrt(Math.pow(startPoint[0] - endPoint[0], 2) + Math.pow(startPoint[1] - endPoint[1], 2)),
22715 for (i = 1; i < points.length-1; i++) {
22716 var point = points[i],
22717 u = positionAlongWay(point, startPoint, endPoint),
22718 p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
22719 p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]),
22720 dist = Math.sqrt(Math.pow(p0 - point[0], 2) + Math.pow(p1 - point[1], 2));
22722 // to bendy if point is off by 20% of total start/end distance in projected space
22723 if (dist > threshold) {
22724 return 'too_bendy';
22731 // Remove the effects of `turn.restriction` on `turn`, which must have the
22732 // following structure:
22735 // from: { node: <node ID>, way: <way ID> },
22736 // via: { node: <node ID> },
22737 // to: { node: <node ID>, way: <way ID> },
22738 // restriction: <relation ID>
22741 // In the simple case, `restriction` is a reference to a `no_*` restriction
22742 // on the turn itself. In this case, it is simply deleted.
22744 // The more complex case is where `restriction` references an `only_*`
22745 // restriction on a different turn in the same intersection. In that case,
22746 // that restriction is also deleted, but at the same time restrictions on
22747 // the turns other than the first two are created.
22749 iD.actions.UnrestrictTurn = function(turn) {
22750 return function(graph) {
22751 return iD.actions.DeleteRelation(turn.restriction)(graph);
22755 iD.behavior.AddWay = function(context) {
22756 var event = d3.dispatch('start', 'startFromWay', 'startFromNode'),
22757 draw = iD.behavior.Draw(context);
22759 var addWay = function(surface) {
22760 draw.on('click', event.start)
22761 .on('clickWay', event.startFromWay)
22762 .on('clickNode', event.startFromNode)
22763 .on('cancel', addWay.cancel)
22764 .on('finish', addWay.cancel);
22767 .dblclickEnable(false);
22769 surface.call(draw);
22772 addWay.off = function(surface) {
22773 surface.call(draw.off);
22776 addWay.cancel = function() {
22777 window.setTimeout(function() {
22778 context.map().dblclickEnable(true);
22781 context.enter(iD.modes.Browse(context));
22784 addWay.tail = function(text) {
22789 return d3.rebind(addWay, event, 'on');
22791 iD.behavior.Breathe = function() {
22792 var duration = 800,
22793 selector = '.selected.shadow, .selected .shadow',
22794 selected = d3.select(null),
22799 function reset(selection) {
22801 .style('stroke-opacity', null)
22802 .style('stroke-width', null)
22803 .style('fill-opacity', null)
22807 function setAnimationParams(transition, fromTo) {
22809 .style('stroke-opacity', function(d) { return params[d.id][fromTo].opacity; })
22810 .style('stroke-width', function(d) { return params[d.id][fromTo].width; })
22811 .style('fill-opacity', function(d) { return params[d.id][fromTo].opacity; })
22812 .style('r', function(d) { return params[d.id][fromTo].width; });
22815 function calcAnimationParams(selection) {
22818 .each(function(d) {
22819 var s = d3.select(this),
22820 tag = s.node().tagName,
22821 p = {'from': {}, 'to': {}},
22824 // determine base opacity and width
22825 if (tag === 'circle') {
22826 opacity = parseFloat(s.style('fill-opacity') || 0.5);
22827 width = parseFloat(s.style('r') || 15.5);
22829 opacity = parseFloat(s.style('stroke-opacity') || 0.7);
22830 width = parseFloat(s.style('stroke-width') || 10);
22833 // calculate from/to interpolation params..
22835 p.from.opacity = opacity * 0.6;
22836 p.to.opacity = opacity * 1.25;
22837 p.from.width = width * 0.9;
22838 p.to.width = width * (tag === 'circle' ? 1.5 : 1.25);
22843 function run(surface, fromTo) {
22844 var toFrom = (fromTo === 'from' ? 'to': 'from'),
22845 currSelected = surface.selectAll(selector),
22846 currClassed = Array.prototype.slice.call(surface.node().classList),
22849 if (done || currSelected.empty()) {
22850 selected.call(reset);
22854 if (!_.isEqual(currSelected, selected) || !_.isEqual(currClassed, classed)) {
22855 selected.call(reset);
22856 classed = _.clone(currClassed);
22857 selected = currSelected.call(calcAnimationParams);
22862 .call(setAnimationParams, fromTo)
22863 .duration(duration)
22864 .each(function() { ++n; })
22865 .each('end', function() {
22866 if (!--n) { // call once
22867 surface.call(run, toFrom);
22872 var breathe = function(surface) {
22874 d3.timer(function() {
22875 if (done) return true;
22877 var currSelected = surface.selectAll(selector);
22878 if (currSelected.empty()) return false;
22880 surface.call(run, 'from');
22885 breathe.off = function() {
22896 iD.behavior.Copy = function(context) {
22897 var keybinding = d3.keybinding('copy');
22899 function groupEntities(ids, graph) {
22900 var entities = ids.map(function (id) { return graph.entity(id); });
22901 return _.extend({relation: [], way: [], node: []},
22902 _.groupBy(entities, function(entity) { return entity.type; }));
22905 function getDescendants(id, graph, descendants) {
22906 var entity = graph.entity(id),
22909 descendants = descendants || {};
22911 if (entity.type === 'relation') {
22912 children = _.pluck(entity.members, 'id');
22913 } else if (entity.type === 'way') {
22914 children = entity.nodes;
22919 for (i = 0; i < children.length; i++) {
22920 if (!descendants[children[i]]) {
22921 descendants[children[i]] = true;
22922 descendants = getDescendants(children[i], graph, descendants);
22926 return descendants;
22929 function doCopy() {
22930 d3.event.preventDefault();
22931 if (context.inIntro()) return;
22933 var graph = context.graph(),
22934 selected = groupEntities(context.selectedIDs(), graph),
22939 for (i = 0; i < selected.relation.length; i++) {
22940 entity = selected.relation[i];
22941 if (!skip[entity.id] && entity.isComplete(graph)) {
22942 canCopy.push(entity.id);
22943 skip = getDescendants(entity.id, graph, skip);
22946 for (i = 0; i < selected.way.length; i++) {
22947 entity = selected.way[i];
22948 if (!skip[entity.id]) {
22949 canCopy.push(entity.id);
22950 skip = getDescendants(entity.id, graph, skip);
22953 for (i = 0; i < selected.node.length; i++) {
22954 entity = selected.node[i];
22955 if (!skip[entity.id]) {
22956 canCopy.push(entity.id);
22960 context.copyIDs(canCopy);
22964 keybinding.on(iD.ui.cmd('⌘C'), doCopy);
22965 d3.select(document).call(keybinding);
22969 copy.off = function() {
22970 d3.select(document).call(keybinding.off);
22976 `iD.behavior.drag` is like `d3.behavior.drag`, with the following differences:
22978 * The `origin` function is expected to return an [x, y] tuple rather than an
22980 * The events are `start`, `move`, and `end`.
22981 (https://github.com/mbostock/d3/issues/563)
22982 * The `start` event is not dispatched until the first cursor movement occurs.
22983 (https://github.com/mbostock/d3/pull/368)
22984 * The `move` event has a `point` and `delta` [x, y] tuple properties rather
22985 than `x`, `y`, `dx`, and `dy` properties.
22986 * The `end` event is not dispatched if no movement occurs.
22987 * An `off` function is available that unbinds the drag's internal event handlers.
22988 * Delegation is supported via the `delegate` function.
22991 iD.behavior.drag = function() {
22992 function d3_eventCancel() {
22993 d3.event.stopPropagation();
22994 d3.event.preventDefault();
22997 var event = d3.dispatch('start', 'move', 'end'),
23001 event_, target, surface;
23003 event.of = function(thiz, argumentz) {
23004 return function(e1) {
23005 var e0 = e1.sourceEvent = d3.event;
23009 event[e1.type].apply(thiz, argumentz);
23016 var d3_event_userSelectProperty = iD.util.prefixCSSProperty('UserSelect'),
23017 d3_event_userSelectSuppress = d3_event_userSelectProperty ?
23019 var selection = d3.selection(),
23020 select = selection.style(d3_event_userSelectProperty);
23021 selection.style(d3_event_userSelectProperty, 'none');
23022 return function () {
23023 selection.style(d3_event_userSelectProperty, select);
23027 var w = d3.select(window).on('selectstart.' + type, d3_eventCancel);
23028 return function () {
23029 w.on('selectstart.' + type, null);
23033 function mousedown() {
23035 event_ = event.of(target, arguments);
23036 var eventTarget = d3.event.target,
23037 touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
23041 selectEnable = d3_event_userSelectSuppress(touchId !== null ? 'drag-' + touchId : 'drag');
23043 var w = d3.select(window)
23044 .on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', dragmove)
23045 .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', dragend, true);
23048 offset = origin.apply(target, arguments);
23049 offset = [offset[0] - origin_[0], offset[1] - origin_[1]];
23054 if (touchId === null) d3.event.stopPropagation();
23057 var p = target.parentNode || surface;
23058 return touchId !== null ? d3.touches(p).filter(function(p) {
23059 return p.identifier === touchId;
23060 })[0] : d3.mouse(p);
23063 function dragmove() {
23066 dx = p[0] - origin_[0],
23067 dy = p[1] - origin_[1];
23069 if (dx === 0 && dy === 0)
23084 point: [p[0] + offset[0], p[1] + offset[1]],
23089 function dragend() {
23096 if (d3.event.target === eventTarget) w.on('click.drag', click, true);
23099 w.on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', null)
23100 .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', null);
23106 w.on('click.drag', null);
23110 function drag(selection) {
23111 var matchesSelector = iD.util.prefixDOMProperty('matchesSelector'),
23112 delegate = mousedown;
23115 delegate = function() {
23117 target = d3.event.target;
23118 for (; target && target !== root; target = target.parentNode) {
23119 if (target[matchesSelector](selector) &&
23120 (!filter || filter(target.__data__))) {
23121 return mousedown.call(target, target.__data__);
23127 selection.on('mousedown.drag' + selector, delegate)
23128 .on('touchstart.drag' + selector, delegate);
23131 drag.off = function(selection) {
23132 selection.on('mousedown.drag' + selector, null)
23133 .on('touchstart.drag' + selector, null);
23136 drag.delegate = function(_) {
23137 if (!arguments.length) return selector;
23142 drag.filter = function(_) {
23143 if (!arguments.length) return origin;
23148 drag.origin = function (_) {
23149 if (!arguments.length) return origin;
23154 drag.cancel = function() {
23156 .on('mousemove.drag', null)
23157 .on('mouseup.drag', null);
23161 drag.target = function() {
23162 if (!arguments.length) return target;
23163 target = arguments[0];
23164 event_ = event.of(target, Array.prototype.slice.call(arguments, 1));
23168 drag.surface = function() {
23169 if (!arguments.length) return surface;
23170 surface = arguments[0];
23174 return d3.rebind(drag, event, 'on');
23176 iD.behavior.Draw = function(context) {
23177 var event = d3.dispatch('move', 'click', 'clickWay',
23178 'clickNode', 'undo', 'cancel', 'finish'),
23179 keybinding = d3.keybinding('draw'),
23180 hover = iD.behavior.Hover(context)
23182 .on('hover', context.ui().sidebar.hover),
23183 tail = iD.behavior.Tail(),
23184 edit = iD.behavior.Edit(context),
23185 closeTolerance = 4,
23189 if (d3.event.altKey) return {};
23190 else return d3.event.target.__data__ || {};
23193 function mousedown() {
23196 var p = context.container().node();
23197 return touchId !== null ? d3.touches(p).filter(function(p) {
23198 return p.identifier === touchId;
23199 })[0] : d3.mouse(p);
23202 var element = d3.select(this),
23203 touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
23207 element.on('mousemove.draw', null);
23209 d3.select(window).on('mouseup.draw', function() {
23210 var t2 = +new Date(),
23212 dist = iD.geo.euclideanDistance(p1, p2);
23214 element.on('mousemove.draw', mousemove);
23215 d3.select(window).on('mouseup.draw', null);
23217 if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) {
23218 // Prevent a quick second click
23219 d3.select(window).on('click.draw-block', function() {
23220 d3.event.stopPropagation();
23223 context.map().dblclickEnable(false);
23225 window.setTimeout(function() {
23226 context.map().dblclickEnable(true);
23227 d3.select(window).on('click.draw-block', null);
23235 function mousemove() {
23236 event.move(datum());
23241 if (d.type === 'way') {
23242 var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection),
23243 edge = [d.nodes[choice.index - 1], d.nodes[choice.index]];
23244 event.clickWay(choice.loc, edge);
23246 } else if (d.type === 'node') {
23247 event.clickNode(d);
23250 event.click(context.map().mouseCoordinates());
23254 function backspace() {
23255 d3.event.preventDefault();
23260 d3.event.preventDefault();
23265 d3.event.preventDefault();
23269 function draw(selection) {
23270 context.install(hover);
23271 context.install(edit);
23273 if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
23274 context.install(tail);
23278 .on('⌫', backspace)
23284 .on('mousedown.draw', mousedown)
23285 .on('mousemove.draw', mousemove);
23287 d3.select(document)
23293 draw.off = function(selection) {
23294 context.ui().sidebar.hover.cancel();
23295 context.uninstall(hover);
23296 context.uninstall(edit);
23298 if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
23299 context.uninstall(tail);
23300 iD.behavior.Draw.usedTails[tail.text()] = true;
23304 .on('mousedown.draw', null)
23305 .on('mousemove.draw', null);
23308 .on('mouseup.draw', null);
23310 d3.select(document)
23311 .call(keybinding.off);
23314 draw.tail = function(_) {
23319 return d3.rebind(draw, event, 'on');
23322 iD.behavior.Draw.usedTails = {};
23323 iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
23324 var way = context.entity(wayId),
23325 isArea = context.geometry(wayId) === 'area',
23327 annotation = t((way.isDegenerate() ?
23328 'operations.start.annotation.' :
23329 'operations.continue.annotation.') + context.geometry(wayId)),
23330 draw = iD.behavior.Draw(context);
23332 var startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0,
23333 start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}),
23334 end = iD.Node({loc: context.map().mouseCoordinates()}),
23336 nodes: typeof index === 'undefined' ? [start.id, end.id] : [end.id, start.id],
23337 tags: _.clone(way.tags)
23340 var f = context[way.isDegenerate() ? 'replace' : 'perform'];
23342 f(iD.actions.AddEntity(end),
23343 iD.actions.AddVertex(wayId, end.id, index));
23345 f(iD.actions.AddEntity(start),
23346 iD.actions.AddEntity(end),
23347 iD.actions.AddEntity(segment));
23350 function move(datum) {
23353 if (datum.type === 'node' && datum.id !== end.id) {
23355 } else if (datum.type === 'way' && datum.id !== segment.id) {
23356 loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc;
23358 loc = context.map().mouseCoordinates();
23361 context.replace(iD.actions.MoveNode(end.id, loc));
23364 function undone() {
23366 context.enter(iD.modes.Browse(context));
23369 function setActiveElements() {
23370 var active = isArea ? [wayId, end.id] : [segment.id, start.id, end.id];
23371 context.surface().selectAll(iD.util.entitySelector(active))
23372 .classed('active', true);
23375 var drawWay = function(surface) {
23376 draw.on('move', move)
23377 .on('click', drawWay.add)
23378 .on('clickWay', drawWay.addWay)
23379 .on('clickNode', drawWay.addNode)
23380 .on('undo', context.undo)
23381 .on('cancel', drawWay.cancel)
23382 .on('finish', drawWay.finish);
23385 .dblclickEnable(false)
23386 .on('drawn.draw', setActiveElements);
23388 setActiveElements();
23390 surface.call(draw);
23393 .on('undone.draw', undone);
23396 drawWay.off = function(surface) {
23401 .on('drawn.draw', null);
23403 surface.call(draw.off)
23404 .selectAll('.active')
23405 .classed('active', false);
23408 .on('undone.draw', null);
23411 function ReplaceTemporaryNode(newNode) {
23412 return function(graph) {
23415 .replace(way.addNode(newNode.id, index))
23420 .replace(graph.entity(wayId).addNode(newNode.id, index))
23428 // Accept the current position of the temporary node and continue drawing.
23429 drawWay.add = function(loc) {
23431 // prevent duplicate nodes
23432 var last = context.hasEntity(way.nodes[way.nodes.length - (isArea ? 2 : 1)]);
23433 if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return;
23435 var newNode = iD.Node({loc: loc});
23438 iD.actions.AddEntity(newNode),
23439 ReplaceTemporaryNode(newNode),
23443 context.enter(mode);
23446 // Connect the way to an existing way.
23447 drawWay.addWay = function(loc, edge) {
23448 var previousEdge = startIndex ?
23449 [way.nodes[startIndex], way.nodes[startIndex - 1]] :
23450 [way.nodes[0], way.nodes[1]];
23452 // Avoid creating duplicate segments
23453 if (!isArea && iD.geo.edgeEqual(edge, previousEdge))
23456 var newNode = iD.Node({ loc: loc });
23459 iD.actions.AddMidpoint({ loc: loc, edge: edge}, newNode),
23460 ReplaceTemporaryNode(newNode),
23464 context.enter(mode);
23467 // Connect the way to an existing node and continue drawing.
23468 drawWay.addNode = function(node) {
23470 // Avoid creating duplicate segments
23471 if (way.areAdjacent(node.id, way.nodes[way.nodes.length - 1])) return;
23474 ReplaceTemporaryNode(node),
23478 context.enter(mode);
23481 // Finish the draw operation, removing the temporary node. If the way has enough
23482 // nodes to be valid, it's selected. Otherwise, return to browse mode.
23483 drawWay.finish = function() {
23487 window.setTimeout(function() {
23488 context.map().dblclickEnable(true);
23491 if (context.hasEntity(wayId)) {
23493 iD.modes.Select(context, [wayId])
23494 .suppressMenu(true)
23495 .newFeature(true));
23497 context.enter(iD.modes.Browse(context));
23501 // Cancel the draw operation and return to browse, deleting everything drawn.
23502 drawWay.cancel = function() {
23504 d3.functor(baseGraph),
23505 t('operations.cancel_draw.annotation'));
23507 window.setTimeout(function() {
23508 context.map().dblclickEnable(true);
23512 context.enter(iD.modes.Browse(context));
23515 drawWay.tail = function(text) {
23522 iD.behavior.Edit = function(context) {
23525 .minzoom(context.minEditableZoom());
23528 edit.off = function() {
23535 iD.behavior.Hash = function(context) {
23536 var s0 = null, // cached location.hash
23537 lat = 90 - 1e-8; // allowable latitude range
23539 var parser = function(map, s) {
23540 var q = iD.util.stringQs(s);
23541 var args = (q.map || '').split('/').map(Number);
23542 if (args.length < 3 || args.some(isNaN)) {
23543 return true; // replace bogus hash
23544 } else if (s !== formatter(map).slice(1)) {
23545 map.centerZoom([args[1],
23546 Math.min(lat, Math.max(-lat, args[2]))], args[0]);
23550 var formatter = function(map) {
23551 var mode = context.mode(),
23552 center = map.center(),
23554 precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
23555 q = _.omit(iD.util.stringQs(location.hash.substring(1)), 'comment'),
23558 if (mode && mode.id === 'browse') {
23561 var selected = context.selectedIDs().filter(function(id) {
23562 return !context.entity(id).isNew();
23564 if (selected.length) {
23565 newParams.id = selected.join(',');
23569 newParams.map = zoom.toFixed(2) +
23570 '/' + center[0].toFixed(precision) +
23571 '/' + center[1].toFixed(precision);
23573 return '#' + iD.util.qsString(_.assign(q, newParams), true);
23576 function update() {
23577 if (context.inIntro()) return;
23578 var s1 = formatter(context.map());
23579 if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map!
23582 var throttledUpdate = _.throttle(update, 500);
23584 function hashchange() {
23585 if (location.hash === s0) return; // ignore spurious hashchange events
23586 if (parser(context.map(), (s0 = location.hash).substring(1))) {
23587 update(); // replace bogus hash
23593 .on('move.hash', throttledUpdate);
23596 .on('enter.hash', throttledUpdate);
23599 .on('hashchange.hash', hashchange);
23601 if (location.hash) {
23602 var q = iD.util.stringQs(location.hash.substring(1));
23603 if (q.id) context.zoomToEntity(q.id.split(',')[0], !q.map);
23604 if (q.comment) context.storage('comment', q.comment);
23606 if (q.map) hash.hadHash = true;
23610 hash.off = function() {
23611 throttledUpdate.cancel();
23614 .on('move.hash', null);
23617 .on('enter.hash', null);
23620 .on('hashchange.hash', null);
23622 location.hash = '';
23628 The hover behavior adds the `.hover` class on mouseover to all elements to which
23629 the identical datum is bound, and removes it on mouseout.
23631 The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
23632 representation may consist of several elements scattered throughout the DOM hierarchy.
23633 Only one of these elements can have the :hover pseudo-class, but all of them will
23634 have the .hover class.
23636 iD.behavior.Hover = function() {
23637 var dispatch = d3.dispatch('hover'),
23642 function keydown() {
23643 if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
23644 dispatch.hover(null);
23645 selection.selectAll('.hover')
23646 .classed('hover-suppressed', true)
23647 .classed('hover', false);
23652 if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
23653 dispatch.hover(target ? target.id : null);
23654 selection.selectAll('.hover-suppressed')
23655 .classed('hover-suppressed', false)
23656 .classed('hover', true);
23660 var hover = function(__) {
23663 function enter(d) {
23664 if (d === target) return;
23668 selection.selectAll('.hover')
23669 .classed('hover', false);
23670 selection.selectAll('.hover-suppressed')
23671 .classed('hover-suppressed', false);
23673 if (target instanceof iD.Entity) {
23674 var selector = '.' + target.id;
23676 if (target.type === 'relation') {
23677 target.members.forEach(function(member) {
23678 selector += ', .' + member.id;
23682 var suppressed = altDisables && d3.event && d3.event.altKey;
23684 selection.selectAll(selector)
23685 .classed(suppressed ? 'hover-suppressed' : 'hover', true);
23687 dispatch.hover(target.id);
23689 dispatch.hover(null);
23695 function mouseover() {
23697 var target = d3.event.target;
23698 enter(target ? target.__data__ : null);
23701 function mouseout() {
23703 var target = d3.event.relatedTarget;
23704 enter(target ? target.__data__ : null);
23707 function mousedown() {
23710 .on('mouseup.hover', mouseup);
23713 function mouseup() {
23718 .on('mouseover.hover', mouseover)
23719 .on('mouseout.hover', mouseout)
23720 .on('mousedown.hover', mousedown)
23721 .on('mouseup.hover', mouseup);
23724 .on('keydown.hover', keydown)
23725 .on('keyup.hover', keyup);
23728 hover.off = function(selection) {
23729 selection.selectAll('.hover')
23730 .classed('hover', false);
23731 selection.selectAll('.hover-suppressed')
23732 .classed('hover-suppressed', false);
23735 .on('mouseover.hover', null)
23736 .on('mouseout.hover', null)
23737 .on('mousedown.hover', null)
23738 .on('mouseup.hover', null);
23741 .on('keydown.hover', null)
23742 .on('keyup.hover', null)
23743 .on('mouseup.hover', null);
23746 hover.altDisables = function(_) {
23747 if (!arguments.length) return altDisables;
23752 return d3.rebind(hover, dispatch, 'on');
23754 iD.behavior.Lasso = function(context) {
23756 var behavior = function(selection) {
23760 function mousedown() {
23761 if (d3.event.shiftKey === true) {
23763 mouse = context.mouse();
23767 .on('mousemove.lasso', mousemove)
23768 .on('mouseup.lasso', mouseup);
23770 d3.event.stopPropagation();
23774 function mousemove() {
23776 lasso = iD.ui.Lasso(context).a(mouse);
23777 context.surface().call(lasso);
23780 lasso.b(context.mouse());
23783 function normalize(a, b) {
23785 [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
23786 [Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
23789 function mouseup() {
23791 .on('mousemove.lasso', null)
23792 .on('mouseup.lasso', null);
23794 if (!lasso) return;
23796 var graph = context.graph(),
23797 extent = iD.geo.Extent(
23798 normalize(context.projection.invert(lasso.a()),
23799 context.projection.invert(lasso.b())));
23803 var selected = context.intersects(extent).filter(function(entity) {
23804 return entity.type === 'node' &&
23805 !context.features().isHidden(entity, graph, entity.geometry(graph));
23808 if (selected.length) {
23809 context.enter(iD.modes.Select(context, _.pluck(selected, 'id')));
23814 .on('mousedown.lasso', mousedown);
23817 behavior.off = function(selection) {
23818 selection.on('mousedown.lasso', null);
23823 iD.behavior.Paste = function(context) {
23824 var keybinding = d3.keybinding('paste');
23826 function omitTag(v, k) {
23834 k === 'description' ||
23835 k.indexOf('name') !== -1 ||
23836 k.indexOf('wiki') === 0 ||
23837 k.indexOf('addr:') === 0 ||
23838 k.indexOf('contact:') === 0
23842 function doPaste() {
23843 d3.event.preventDefault();
23844 if (context.inIntro()) return;
23846 var mouse = context.mouse(),
23847 projection = context.projection,
23848 viewport = iD.geo.Extent(projection.clipExtent()).polygon();
23850 if (!iD.geo.pointInPolygon(mouse, viewport)) return;
23852 var extent = iD.geo.Extent(),
23853 oldIDs = context.copyIDs(),
23854 oldGraph = context.copyGraph(),
23858 if (!oldIDs.length) return;
23860 for (i = 0; i < oldIDs.length; i++) {
23861 var oldEntity = oldGraph.entity(oldIDs[i]),
23862 action = iD.actions.CopyEntity(oldEntity.id, oldGraph, true),
23865 extent._extend(oldEntity.extent(oldGraph));
23866 context.perform(action);
23868 // First element in `newEntities` contains the copied Entity,
23869 // Subsequent array elements contain any descendants..
23870 newEntities = action.newEntities();
23871 newIDs.push(newEntities[0].id);
23873 for (j = 0; j < newEntities.length; j++) {
23874 var newEntity = newEntities[j],
23875 tags = _.omit(newEntity.tags, omitTag);
23877 context.perform(iD.actions.ChangeTags(newEntity.id, tags));
23881 // Put pasted objects where mouse pointer is..
23882 var center = projection(extent.center()),
23883 delta = [ mouse[0] - center[0], mouse[1] - center[1] ];
23885 context.perform(iD.actions.Move(newIDs, delta, projection));
23886 context.enter(iD.modes.Move(context, newIDs));
23890 keybinding.on(iD.ui.cmd('⌘V'), doPaste);
23891 d3.select(document).call(keybinding);
23895 paste.off = function() {
23896 d3.select(document).call(keybinding.off);
23901 iD.behavior.Select = function(context) {
23902 function keydown() {
23903 if (d3.event && d3.event.shiftKey) {
23905 .classed('behavior-multiselect', true);
23910 if (!d3.event || !d3.event.shiftKey) {
23912 .classed('behavior-multiselect', false);
23917 var datum = d3.event.target.__data__,
23918 lasso = d3.select('#surface .lasso').node(),
23919 mode = context.mode();
23921 if (!(datum instanceof iD.Entity)) {
23922 if (!d3.event.shiftKey && !lasso && mode.id !== 'browse')
23923 context.enter(iD.modes.Browse(context));
23925 } else if (!d3.event.shiftKey && !lasso) {
23926 // Avoid re-entering Select mode with same entity.
23927 if (context.selectedIDs().length !== 1 || context.selectedIDs()[0] !== datum.id) {
23928 context.enter(iD.modes.Select(context, [datum.id]));
23930 mode.suppressMenu(false).reselect();
23932 } else if (context.selectedIDs().indexOf(datum.id) >= 0) {
23933 var selectedIDs = _.without(context.selectedIDs(), datum.id);
23934 context.enter(selectedIDs.length ?
23935 iD.modes.Select(context, selectedIDs) :
23936 iD.modes.Browse(context));
23939 context.enter(iD.modes.Select(context, context.selectedIDs().concat([datum.id])));
23943 var behavior = function(selection) {
23945 .on('keydown.select', keydown)
23946 .on('keyup.select', keyup);
23948 selection.on('click.select', click);
23953 behavior.off = function(selection) {
23955 .on('keydown.select', null)
23956 .on('keyup.select', null);
23958 selection.on('click.select', null);
23965 iD.behavior.Tail = function() {
23969 tooltipSize = [0, 0],
23970 selectionSize = [0, 0];
23972 function tail(selection) {
23976 .on('resize.tail', function() { selectionSize = selection.dimensions(); });
23979 container.style('display', 'block');
23980 tooltipSize = container.dimensions();
23983 function mousemove() {
23984 if (container.style('display') === 'none') show();
23985 var xoffset = ((d3.event.clientX + tooltipSize[0] + xmargin) > selectionSize[0]) ?
23986 -tooltipSize[0] - xmargin : xmargin;
23987 container.classed('left', xoffset > 0);
23988 iD.util.setTransform(container, d3.event.clientX + xoffset, d3.event.clientY);
23991 function mouseleave() {
23992 if (d3.event.relatedTarget !== container.node()) {
23993 container.style('display', 'none');
23997 function mouseenter() {
23998 if (d3.event.relatedTarget !== container.node()) {
24003 container = d3.select(document.body)
24005 .style('display', 'none')
24006 .attr('class', 'tail tooltip-inner');
24008 container.append('div')
24012 .on('mousemove.tail', mousemove)
24013 .on('mouseenter.tail', mouseenter)
24014 .on('mouseleave.tail', mouseleave);
24017 .on('mousemove.tail', mousemove);
24019 tooltipSize = container.dimensions();
24020 selectionSize = selection.dimensions();
24023 tail.off = function(selection) {
24027 .on('mousemove.tail', null)
24031 .on('mousemove.tail', null)
24032 .on('mouseenter.tail', null)
24033 .on('mouseleave.tail', null);
24036 .on('resize.tail', null);
24039 tail.text = function(_) {
24040 if (!arguments.length) return text;
24048 iD.modes.AddArea = function(context) {
24052 title: t('modes.add_area.title'),
24053 description: t('modes.add_area.description'),
24057 var behavior = iD.behavior.AddWay(context)
24058 .tail(t('modes.add_area.tail'))
24059 .on('start', start)
24060 .on('startFromWay', startFromWay)
24061 .on('startFromNode', startFromNode),
24062 defaultTags = {area: 'yes'};
24064 function start(loc) {
24065 var graph = context.graph(),
24066 node = iD.Node({loc: loc}),
24067 way = iD.Way({tags: defaultTags});
24070 iD.actions.AddEntity(node),
24071 iD.actions.AddEntity(way),
24072 iD.actions.AddVertex(way.id, node.id),
24073 iD.actions.AddVertex(way.id, node.id));
24075 context.enter(iD.modes.DrawArea(context, way.id, graph));
24078 function startFromWay(loc, edge) {
24079 var graph = context.graph(),
24080 node = iD.Node({loc: loc}),
24081 way = iD.Way({tags: defaultTags});
24084 iD.actions.AddEntity(node),
24085 iD.actions.AddEntity(way),
24086 iD.actions.AddVertex(way.id, node.id),
24087 iD.actions.AddVertex(way.id, node.id),
24088 iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24090 context.enter(iD.modes.DrawArea(context, way.id, graph));
24093 function startFromNode(node) {
24094 var graph = context.graph(),
24095 way = iD.Way({tags: defaultTags});
24098 iD.actions.AddEntity(way),
24099 iD.actions.AddVertex(way.id, node.id),
24100 iD.actions.AddVertex(way.id, node.id));
24102 context.enter(iD.modes.DrawArea(context, way.id, graph));
24105 mode.enter = function() {
24106 context.install(behavior);
24109 mode.exit = function() {
24110 context.uninstall(behavior);
24115 iD.modes.AddLine = function(context) {
24119 title: t('modes.add_line.title'),
24120 description: t('modes.add_line.description'),
24124 var behavior = iD.behavior.AddWay(context)
24125 .tail(t('modes.add_line.tail'))
24126 .on('start', start)
24127 .on('startFromWay', startFromWay)
24128 .on('startFromNode', startFromNode);
24130 function start(loc) {
24131 var graph = context.graph(),
24132 node = iD.Node({loc: loc}),
24136 iD.actions.AddEntity(node),
24137 iD.actions.AddEntity(way),
24138 iD.actions.AddVertex(way.id, node.id));
24140 context.enter(iD.modes.DrawLine(context, way.id, graph));
24143 function startFromWay(loc, edge) {
24144 var graph = context.graph(),
24145 node = iD.Node({loc: loc}),
24149 iD.actions.AddEntity(node),
24150 iD.actions.AddEntity(way),
24151 iD.actions.AddVertex(way.id, node.id),
24152 iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24154 context.enter(iD.modes.DrawLine(context, way.id, graph));
24157 function startFromNode(node) {
24158 var way = iD.Way();
24161 iD.actions.AddEntity(way),
24162 iD.actions.AddVertex(way.id, node.id));
24164 context.enter(iD.modes.DrawLine(context, way.id, context.graph()));
24167 mode.enter = function() {
24168 context.install(behavior);
24171 mode.exit = function() {
24172 context.uninstall(behavior);
24177 iD.modes.AddPoint = function(context) {
24181 title: t('modes.add_point.title'),
24182 description: t('modes.add_point.description'),
24186 var behavior = iD.behavior.Draw(context)
24187 .tail(t('modes.add_point.tail'))
24189 .on('clickWay', addWay)
24190 .on('clickNode', addNode)
24191 .on('cancel', cancel)
24192 .on('finish', cancel);
24194 function add(loc) {
24195 var node = iD.Node({loc: loc});
24198 iD.actions.AddEntity(node),
24199 t('operations.add.annotation.point'));
24202 iD.modes.Select(context, [node.id])
24203 .suppressMenu(true)
24204 .newFeature(true));
24207 function addWay(loc) {
24211 function addNode(node) {
24215 function cancel() {
24216 context.enter(iD.modes.Browse(context));
24219 mode.enter = function() {
24220 context.install(behavior);
24223 mode.exit = function() {
24224 context.uninstall(behavior);
24229 iD.modes.Browse = function(context) {
24233 title: t('modes.browse.title'),
24234 description: t('modes.browse.description')
24238 iD.behavior.Paste(context),
24239 iD.behavior.Hover(context)
24240 .on('hover', context.ui().sidebar.hover),
24241 iD.behavior.Select(context),
24242 iD.behavior.Lasso(context),
24243 iD.modes.DragNode(context).behavior];
24245 mode.enter = function() {
24246 behaviors.forEach(function(behavior) {
24247 context.install(behavior);
24250 // Get focus on the body.
24251 if (document.activeElement && document.activeElement.blur) {
24252 document.activeElement.blur();
24256 context.ui().sidebar.show(sidebar);
24258 context.ui().sidebar.select(null);
24262 mode.exit = function() {
24263 context.ui().sidebar.hover.cancel();
24264 behaviors.forEach(function(behavior) {
24265 context.uninstall(behavior);
24269 context.ui().sidebar.hide();
24273 mode.sidebar = function(_) {
24274 if (!arguments.length) return sidebar;
24281 iD.modes.DragNode = function(context) {
24292 hover = iD.behavior.Hover(context)
24294 .on('hover', context.ui().sidebar.hover),
24295 edit = iD.behavior.Edit(context);
24297 function edge(point, size) {
24298 var pad = [30, 100, 30, 100];
24299 if (point[0] > size[0] - pad[0]) return [-10, 0];
24300 else if (point[0] < pad[2]) return [10, 0];
24301 else if (point[1] > size[1] - pad[1]) return [0, -10];
24302 else if (point[1] < pad[3]) return [0, 10];
24306 function startNudge(nudge) {
24307 if (nudgeInterval) window.clearInterval(nudgeInterval);
24308 nudgeInterval = window.setInterval(function() {
24309 context.pan(nudge);
24313 function stopNudge() {
24314 if (nudgeInterval) window.clearInterval(nudgeInterval);
24315 nudgeInterval = null;
24318 function moveAnnotation(entity) {
24319 return t('operations.move.annotation.' + entity.geometry(context.graph()));
24322 function connectAnnotation(entity) {
24323 return t('operations.connect.annotation.' + entity.geometry(context.graph()));
24326 function origin(entity) {
24327 return context.projection(entity.loc);
24330 function start(entity) {
24331 cancelled = d3.event.sourceEvent.shiftKey ||
24332 context.features().hasHiddenConnections(entity, context.graph());
24334 if (cancelled) return behavior.cancel();
24336 wasMidpoint = entity.type === 'midpoint';
24338 var midpoint = entity;
24339 entity = iD.Node();
24340 context.perform(iD.actions.AddMidpoint(midpoint, entity));
24342 var vertex = context.surface()
24343 .selectAll('.' + entity.id);
24344 behavior.target(vertex.node(), entity);
24348 iD.actions.Noop());
24351 activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
24352 activeIDs.push(entity.id);
24354 context.enter(mode);
24358 if (d3.event.sourceEvent.altKey) {
24362 return d3.event.sourceEvent.target.__data__ || {};
24365 // via https://gist.github.com/shawnbot/4166283
24366 function childOf(p, c) {
24367 if (p === c) return false;
24368 while (c && c !== p) c = c.parentNode;
24372 function move(entity) {
24373 if (cancelled) return;
24374 d3.event.sourceEvent.stopPropagation();
24376 var nudge = childOf(context.container().node(),
24377 d3.event.sourceEvent.toElement) &&
24378 edge(d3.event.point, context.map().dimensions());
24380 if (nudge) startNudge(nudge);
24383 var loc = context.map().mouseCoordinates();
24386 if (d.type === 'node' && d.id !== entity.id) {
24388 } else if (d.type === 'way' && !d3.select(d3.event.sourceEvent.target).classed('fill')) {
24389 loc = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection).loc;
24393 iD.actions.MoveNode(entity.id, loc),
24394 moveAnnotation(entity));
24397 function end(entity) {
24398 if (cancelled) return;
24402 if (d.type === 'way') {
24403 var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection);
24405 iD.actions.AddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity),
24406 connectAnnotation(d));
24408 } else if (d.type === 'node' && d.id !== entity.id) {
24410 iD.actions.Connect([d.id, entity.id]),
24411 connectAnnotation(d));
24413 } else if (wasMidpoint) {
24416 t('operations.add.annotation.vertex'));
24421 moveAnnotation(entity));
24424 var reselection = selectedIDs.filter(function(id) {
24425 return context.graph().hasEntity(id);
24428 if (reselection.length) {
24430 iD.modes.Select(context, reselection)
24431 .suppressMenu(true));
24433 context.enter(iD.modes.Browse(context));
24437 function cancel() {
24439 context.enter(iD.modes.Browse(context));
24442 function setActiveElements() {
24443 context.surface().selectAll(iD.util.entitySelector(activeIDs))
24444 .classed('active', true);
24447 var behavior = iD.behavior.drag()
24448 .delegate('g.node, g.point, g.midpoint')
24449 .surface(context.surface().node())
24451 .on('start', start)
24455 mode.enter = function() {
24456 context.install(hover);
24457 context.install(edit);
24460 .on('undone.drag-node', cancel);
24463 .on('drawn.drag-node', setActiveElements);
24465 setActiveElements();
24468 mode.exit = function() {
24469 context.ui().sidebar.hover.cancel();
24470 context.uninstall(hover);
24471 context.uninstall(edit);
24474 .on('undone.drag-node', null);
24477 .on('drawn.drag-node', null);
24480 .selectAll('.active')
24481 .classed('active', false);
24486 mode.selectedIDs = function(_) {
24487 if (!arguments.length) return selectedIDs;
24492 mode.behavior = behavior;
24496 iD.modes.DrawArea = function(context, wayId, baseGraph) {
24504 mode.enter = function() {
24505 var way = context.entity(wayId),
24506 headId = way.nodes[way.nodes.length - 2],
24507 tailId = way.first();
24509 behavior = iD.behavior.DrawWay(context, wayId, -1, mode, baseGraph)
24510 .tail(t('modes.draw_area.tail'));
24512 var addNode = behavior.addNode;
24514 behavior.addNode = function(node) {
24515 if (node.id === headId || node.id === tailId) {
24522 context.install(behavior);
24525 mode.exit = function() {
24526 context.uninstall(behavior);
24529 mode.selectedIDs = function() {
24535 iD.modes.DrawLine = function(context, wayId, baseGraph, affix) {
24543 mode.enter = function() {
24544 var way = context.entity(wayId),
24545 index = (affix === 'prefix') ? 0 : undefined,
24546 headId = (affix === 'prefix') ? way.first() : way.last();
24548 behavior = iD.behavior.DrawWay(context, wayId, index, mode, baseGraph)
24549 .tail(t('modes.draw_line.tail'));
24551 var addNode = behavior.addNode;
24553 behavior.addNode = function(node) {
24554 if (node.id === headId) {
24561 context.install(behavior);
24564 mode.exit = function() {
24565 context.uninstall(behavior);
24568 mode.selectedIDs = function() {
24574 iD.modes.Move = function(context, entityIDs) {
24580 var keybinding = d3.keybinding('move'),
24581 edit = iD.behavior.Edit(context),
24582 annotation = entityIDs.length === 1 ?
24583 t('operations.move.annotation.' + context.geometry(entityIDs[0])) :
24584 t('operations.move.annotation.multiple'),
24589 function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
24591 function edge(point, size) {
24592 var pad = [30, 100, 30, 100];
24593 if (point[0] > size[0] - pad[0]) return [-10, 0];
24594 else if (point[0] < pad[2]) return [10, 0];
24595 else if (point[1] > size[1] - pad[1]) return [0, -10];
24596 else if (point[1] < pad[3]) return [0, 10];
24600 function startNudge(nudge) {
24601 if (nudgeInterval) window.clearInterval(nudgeInterval);
24602 nudgeInterval = window.setInterval(function() {
24603 context.pan(nudge);
24605 var currMouse = context.mouse(),
24606 origMouse = context.projection(origin),
24607 delta = vecSub(vecSub(currMouse, origMouse), nudge),
24608 action = iD.actions.Move(entityIDs, delta, context.projection, cache);
24610 context.overwrite(action, annotation);
24615 function stopNudge() {
24616 if (nudgeInterval) window.clearInterval(nudgeInterval);
24617 nudgeInterval = null;
24621 var currMouse = context.mouse(),
24622 origMouse = context.projection(origin),
24623 delta = vecSub(currMouse, origMouse),
24624 action = iD.actions.Move(entityIDs, delta, context.projection, cache);
24626 context.overwrite(action, annotation);
24628 var nudge = edge(currMouse, context.map().dimensions());
24629 if (nudge) startNudge(nudge);
24633 function finish() {
24634 d3.event.stopPropagation();
24635 context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
24639 function cancel() {
24641 context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
24645 function undone() {
24646 context.enter(iD.modes.Browse(context));
24649 mode.enter = function() {
24650 origin = context.map().mouseCoordinates();
24653 context.install(edit);
24660 .on('mousemove.move', move)
24661 .on('click.move', finish);
24664 .on('undone.move', undone);
24670 d3.select(document)
24674 mode.exit = function() {
24677 context.uninstall(edit);
24680 .on('mousemove.move', null)
24681 .on('click.move', null);
24684 .on('undone.move', null);
24691 iD.modes.RotateWay = function(context, wayId) {
24697 var keybinding = d3.keybinding('rotate-way'),
24698 edit = iD.behavior.Edit(context);
24700 mode.enter = function() {
24701 context.install(edit);
24703 var annotation = t('operations.rotate.annotation.' + context.geometry(wayId)),
24704 way = context.graph().entity(wayId),
24705 nodes = _.uniq(context.graph().childNodes(way)),
24706 points = nodes.map(function(n) { return context.projection(n.loc); }),
24707 pivot = d3.geom.polygon(points).centroid(),
24714 function rotate() {
24716 var mousePoint = context.mouse(),
24717 newAngle = Math.atan2(mousePoint[1] - pivot[1], mousePoint[0] - pivot[0]);
24719 if (typeof angle === 'undefined') angle = newAngle;
24722 iD.actions.RotateWay(wayId, pivot, newAngle - angle, context.projection),
24728 function finish() {
24729 d3.event.stopPropagation();
24730 context.enter(iD.modes.Select(context, [wayId])
24731 .suppressMenu(true));
24734 function cancel() {
24736 context.enter(iD.modes.Select(context, [wayId])
24737 .suppressMenu(true));
24740 function undone() {
24741 context.enter(iD.modes.Browse(context));
24745 .on('mousemove.rotate-way', rotate)
24746 .on('click.rotate-way', finish);
24749 .on('undone.rotate-way', undone);
24755 d3.select(document)
24759 mode.exit = function() {
24760 context.uninstall(edit);
24763 .on('mousemove.rotate-way', null)
24764 .on('click.rotate-way', null);
24767 .on('undone.rotate-way', null);
24774 iD.modes.Save = function(context) {
24775 var ui = iD.ui.Commit(context)
24776 .on('cancel', cancel)
24779 function cancel() {
24780 context.enter(iD.modes.Browse(context));
24783 function save(e, tryAgain) {
24784 function withChildNodes(ids, graph) {
24785 return _.uniq(_.reduce(ids, function(result, id) {
24786 var e = graph.entity(id);
24787 if (e.type === 'way') {
24789 var cn = graph.childNodes(e);
24790 result.push.apply(result, _.pluck(_.filter(cn, 'version'), 'id'));
24792 /* eslint-disable no-console */
24793 if (typeof console !== 'undefined') console.error(err);
24794 /* eslint-enable no-console */
24801 var loading = iD.ui.Loading(context).message(t('save.uploading')).blocking(true),
24802 history = context.history(),
24803 origChanges = history.changes(iD.actions.DiscardTags(history.difference())),
24804 localGraph = context.graph(),
24805 remoteGraph = iD.Graph(history.base(), true),
24806 modified = _.filter(history.difference().summary(), {changeType: 'modified'}),
24807 toCheck = _.pluck(_.pluck(modified, 'entity'), 'id'),
24808 toLoad = withChildNodes(toCheck, localGraph),
24812 if (!tryAgain) history.perform(iD.actions.Noop()); // checkpoint
24813 context.container().call(loading);
24815 if (toCheck.length) {
24816 context.connection().loadMultiple(toLoad, loaded);
24822 // Reload modified entities into an alternate graph and check for conflicts..
24823 function loaded(err, result) {
24824 if (errors.length) return;
24828 msg: err.responseText,
24829 details: [ t('save.status_code', { code: err.status }) ]
24835 _.each(result.data, function(entity) {
24836 remoteGraph.replace(entity);
24837 toLoad = _.without(toLoad, entity.id);
24839 // Because loadMultiple doesn't download /full like loadEntity,
24840 // need to also load children that aren't already being checked..
24841 if (!entity.visible) return;
24842 if (entity.type === 'way') {
24843 loadMore.push.apply(loadMore,
24844 _.difference(entity.nodes, toCheck, toLoad, loadMore));
24845 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
24846 loadMore.push.apply(loadMore,
24847 _.difference(_.pluck(entity.members, 'id'), toCheck, toLoad, loadMore));
24851 if (loadMore.length) {
24852 toLoad.push.apply(toLoad, loadMore);
24853 context.connection().loadMultiple(loadMore, loaded);
24856 if (!toLoad.length) {
24863 function checkConflicts() {
24864 function choice(id, text, action) {
24865 return { id: id, text: text, action: function() { history.replace(action); } };
24867 function formatUser(d) {
24868 return '<a href="' + context.connection().userURL(d) + '" target="_blank">' + d + '</a>';
24870 function entityName(entity) {
24871 return iD.util.displayName(entity) || (iD.util.displayType(entity.id) + ' ' + entity.id);
24874 function compareVersions(local, remote) {
24875 if (local.version !== remote.version) return false;
24877 if (local.type === 'way') {
24878 var children = _.union(local.nodes, remote.nodes);
24880 for (var i = 0; i < children.length; i++) {
24881 var a = localGraph.hasEntity(children[i]),
24882 b = remoteGraph.hasEntity(children[i]);
24884 if (a && b && a.version !== b.version) return false;
24891 _.each(toCheck, function(id) {
24892 var local = localGraph.entity(id),
24893 remote = remoteGraph.entity(id);
24895 if (compareVersions(local, remote)) return;
24897 var action = iD.actions.MergeRemoteChanges,
24898 merge = action(id, localGraph, remoteGraph, formatUser);
24900 history.replace(merge);
24902 var mergeConflicts = merge.conflicts();
24903 if (!mergeConflicts.length) return; // merged safely
24905 var forceLocal = action(id, localGraph, remoteGraph).withOption('force_local'),
24906 forceRemote = action(id, localGraph, remoteGraph).withOption('force_remote'),
24907 keepMine = t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore')),
24908 keepTheirs = t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
24912 name: entityName(local),
24913 details: mergeConflicts,
24916 choice(id, keepMine, forceLocal),
24917 choice(id, keepTheirs, forceRemote)
24926 function finalize() {
24927 if (conflicts.length) {
24928 conflicts.sort(function(a,b) { return b.id.localeCompare(a.id); });
24930 } else if (errors.length) {
24933 var changes = history.changes(iD.actions.DiscardTags(history.difference()));
24934 if (changes.modified.length || changes.created.length || changes.deleted.length) {
24935 context.connection().putChangeset(
24938 history.imageryUsed(),
24939 function(err, changeset_id) {
24942 msg: err.responseText,
24943 details: [ t('save.status_code', { code: err.status }) ]
24947 history.clearSaved();
24948 success(e, changeset_id);
24949 // Add delay to allow for postgres replication #1646 #2678
24950 window.setTimeout(function() {
24956 } else { // changes were insignificant or reverted by user
24965 function showConflicts() {
24966 var selection = context.container()
24967 .select('#sidebar')
24969 .attr('class','sidebar-component');
24973 selection.call(iD.ui.Conflicts(context)
24975 .on('download', function() {
24976 var data = JXON.stringify(context.connection().osmChangeJXON('CHANGEME', origChanges)),
24977 win = window.open('data:text/xml,' + encodeURIComponent(data), '_blank');
24980 .on('cancel', function() {
24982 selection.remove();
24984 .on('save', function() {
24985 for (var i = 0; i < conflicts.length; i++) {
24986 if (conflicts[i].chosen === 1) { // user chose "keep theirs"
24987 var entity = context.hasEntity(conflicts[i].id);
24988 if (entity && entity.type === 'way') {
24989 var children = _.uniq(entity.nodes);
24990 for (var j = 0; j < children.length; j++) {
24991 history.replace(iD.actions.Revert(children[j]));
24994 history.replace(iD.actions.Revert(conflicts[i].id));
24998 selection.remove();
25005 function showErrors() {
25006 var selection = iD.ui.confirm(context.container());
25012 .select('.modal-section.header')
25014 .text(t('save.error'));
25016 addErrors(selection, errors);
25017 selection.okButton();
25021 function addErrors(selection, data) {
25022 var message = selection
25023 .select('.modal-section.message-text');
25025 var items = message
25026 .selectAll('.error-container')
25029 var enter = items.enter()
25031 .attr('class', 'error-container');
25035 .attr('class', 'error-description')
25037 .classed('hide-toggle', true)
25038 .text(function(d) { return d.msg || t('save.unknown_error_details'); })
25039 .on('click', function() {
25040 var error = d3.select(this),
25041 detail = d3.select(this.nextElementSibling),
25042 exp = error.classed('expanded');
25044 detail.style('display', exp ? 'none' : 'block');
25045 error.classed('expanded', !exp);
25047 d3.event.preventDefault();
25050 var details = enter
25052 .attr('class', 'error-detail-container')
25053 .style('display', 'none');
25057 .attr('class', 'error-detail-list')
25059 .data(function(d) { return d.details || []; })
25062 .attr('class', 'error-detail-item')
25063 .text(function(d) { return d; });
25072 function success(e, changeset_id) {
25073 context.enter(iD.modes.Browse(context)
25074 .sidebar(iD.ui.Success(context)
25079 .on('cancel', function() {
25080 context.ui().sidebar.hide();
25088 mode.enter = function() {
25089 context.connection().authenticate(function(err) {
25093 context.ui().sidebar.show(ui);
25098 mode.exit = function() {
25099 context.ui().sidebar.hide();
25104 iD.modes.Select = function(context, selectedIDs) {
25110 var keybinding = d3.keybinding('select'),
25113 iD.behavior.Copy(context),
25114 iD.behavior.Paste(context),
25115 iD.behavior.Breathe(context),
25116 iD.behavior.Hover(context),
25117 iD.behavior.Select(context),
25118 iD.behavior.Lasso(context),
25119 iD.modes.DragNode(context)
25120 .selectedIDs(selectedIDs)
25124 newFeature = false,
25125 suppressMenu = false;
25127 var wrap = context.container()
25128 .select('.inspector-wrap');
25131 function singular() {
25132 if (selectedIDs.length === 1) {
25133 return context.hasEntity(selectedIDs[0]);
25137 function closeMenu() {
25139 context.surface().call(radialMenu.close);
25143 function positionMenu() {
25144 if (suppressMenu || !radialMenu) { return; }
25146 var entity = singular();
25147 if (entity && context.geometry(entity.id) === 'relation') {
25148 suppressMenu = true;
25149 } else if (entity && entity.type === 'node') {
25150 radialMenu.center(context.projection(entity.loc));
25152 var point = context.mouse(),
25153 viewport = iD.geo.Extent(context.projection.clipExtent()).polygon();
25154 if (iD.geo.pointInPolygon(point, viewport)) {
25155 radialMenu.center(point);
25157 suppressMenu = true;
25162 function showMenu() {
25164 if (!suppressMenu && radialMenu) {
25165 context.surface().call(radialMenu);
25169 function toggleMenu() {
25170 if (d3.select('.radial-menu').empty()) {
25177 mode.selectedIDs = function() {
25178 return selectedIDs;
25181 mode.reselect = function() {
25182 var surfaceNode = context.surface().node();
25183 if (surfaceNode.focus) { // FF doesn't support it
25184 surfaceNode.focus();
25191 mode.newFeature = function(_) {
25192 if (!arguments.length) return newFeature;
25197 mode.suppressMenu = function(_) {
25198 if (!arguments.length) return suppressMenu;
25203 mode.enter = function() {
25204 function update() {
25206 if (_.any(selectedIDs, function(id) { return !context.hasEntity(id); })) {
25207 // Exit mode if selected entity gets undone
25208 context.enter(iD.modes.Browse(context));
25212 function dblclick() {
25213 var target = d3.select(d3.event.target),
25214 datum = target.datum();
25216 if (datum instanceof iD.Way && !target.classed('fill')) {
25217 var choice = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection),
25220 var prev = datum.nodes[choice.index - 1],
25221 next = datum.nodes[choice.index];
25224 iD.actions.AddMidpoint({loc: choice.loc, edge: [prev, next]}, node),
25225 t('operations.add.annotation.vertex'));
25227 d3.event.preventDefault();
25228 d3.event.stopPropagation();
25232 function selectElements(drawn) {
25233 var entity = singular();
25234 if (entity && context.geometry(entity.id) === 'relation') {
25235 suppressMenu = true;
25239 var selection = context.surface()
25240 .selectAll(iD.util.entityOrMemberSelector(selectedIDs, context.graph()));
25242 if (selection.empty()) {
25243 if (drawn) { // Exit mode if selected DOM elements have disappeared..
25244 context.enter(iD.modes.Browse(context));
25248 .classed('selected', true);
25253 if (!context.inIntro()) {
25254 context.enter(iD.modes.Browse(context));
25259 behaviors.forEach(function(behavior) {
25260 context.install(behavior);
25263 var operations = _.without(d3.values(iD.operations), iD.operations.Delete)
25264 .map(function(o) { return o(selectedIDs, context); })
25265 .filter(function(o) { return o.available(); });
25267 operations.unshift(iD.operations.Delete(selectedIDs, context));
25270 .on('⎋', ret, true)
25271 .on('↩', ret, true)
25272 .on('space', toggleMenu);
25274 operations.forEach(function(operation) {
25275 operation.keys.forEach(function(key) {
25276 keybinding.on(key, function() {
25277 if (!(context.inIntro() || operation.disabled())) {
25284 d3.select(document)
25287 radialMenu = iD.ui.RadialMenu(context, operations);
25289 context.ui().sidebar
25290 .select(singular() ? singular().id : null, newFeature);
25293 .on('undone.select', update)
25294 .on('redone.select', update);
25297 .on('move.select', closeMenu)
25298 .on('drawn.select', selectElements);
25302 var show = d3.event && !suppressMenu;
25308 timeout = window.setTimeout(function() {
25314 .on('dblclick.select', dblclick);
25317 if (selectedIDs.length > 1) {
25318 var entities = iD.ui.SelectionList(context, selectedIDs);
25319 context.ui().sidebar.show(entities);
25323 mode.exit = function() {
25324 if (timeout) window.clearTimeout(timeout);
25326 if (inspector) wrap.call(inspector.close);
25328 behaviors.forEach(function(behavior) {
25329 context.uninstall(behavior);
25334 radialMenu = undefined;
25337 .on('undone.select', null)
25338 .on('redone.select', null);
25341 .on('dblclick.select', null)
25342 .selectAll('.selected')
25343 .classed('selected', false);
25345 context.map().on('drawn.select', null);
25346 context.ui().sidebar.hide();
25351 iD.operations = {};
25352 iD.operations.Circularize = function(selectedIDs, context) {
25353 var entityId = selectedIDs[0],
25354 entity = context.entity(entityId),
25355 extent = entity.extent(context.graph()),
25356 geometry = context.geometry(entityId),
25357 action = iD.actions.Circularize(entityId, context.projection);
25359 var operation = function() {
25360 var annotation = t('operations.circularize.annotation.' + geometry);
25361 context.perform(action, annotation);
25364 operation.available = function() {
25365 return selectedIDs.length === 1 &&
25366 entity.type === 'way' &&
25367 _.uniq(entity.nodes).length > 1;
25370 operation.disabled = function() {
25372 if (extent.percentContainedIn(context.extent()) < 0.8) {
25373 reason = 'too_large';
25374 } else if (context.hasHiddenConnections(entityId)) {
25375 reason = 'connected_to_hidden';
25377 return action.disabled(context.graph()) || reason;
25380 operation.tooltip = function() {
25381 var disable = operation.disabled();
25383 t('operations.circularize.' + disable) :
25384 t('operations.circularize.description.' + geometry);
25387 operation.id = 'circularize';
25388 operation.keys = [t('operations.circularize.key')];
25389 operation.title = t('operations.circularize.title');
25393 iD.operations.Continue = function(selectedIDs, context) {
25394 var graph = context.graph(),
25395 entities = selectedIDs.map(function(id) { return graph.entity(id); }),
25396 geometries = _.extend({line: [], vertex: []},
25397 _.groupBy(entities, function(entity) { return entity.geometry(graph); })),
25398 vertex = geometries.vertex[0];
25400 function candidateWays() {
25401 return graph.parentWays(vertex).filter(function(parent) {
25402 return parent.geometry(graph) === 'line' &&
25403 parent.affix(vertex.id) &&
25404 (geometries.line.length === 0 || geometries.line[0] === parent);
25408 var operation = function() {
25409 var candidate = candidateWays()[0];
25410 context.enter(iD.modes.DrawLine(
25414 candidate.affix(vertex.id)));
25417 operation.available = function() {
25418 return geometries.vertex.length === 1 && geometries.line.length <= 1 &&
25419 !context.features().hasHiddenConnections(vertex, context.graph());
25422 operation.disabled = function() {
25423 var candidates = candidateWays();
25424 if (candidates.length === 0)
25425 return 'not_eligible';
25426 if (candidates.length > 1)
25430 operation.tooltip = function() {
25431 var disable = operation.disabled();
25433 t('operations.continue.' + disable) :
25434 t('operations.continue.description');
25437 operation.id = 'continue';
25438 operation.keys = [t('operations.continue.key')];
25439 operation.title = t('operations.continue.title');
25443 iD.operations.Delete = function(selectedIDs, context) {
25444 var action = iD.actions.DeleteMultiple(selectedIDs);
25446 var operation = function() {
25450 if (selectedIDs.length > 1) {
25451 annotation = t('operations.delete.annotation.multiple', {n: selectedIDs.length});
25454 var id = selectedIDs[0],
25455 entity = context.entity(id),
25456 geometry = context.geometry(id),
25457 parents = context.graph().parentWays(entity),
25458 parent = parents[0];
25460 annotation = t('operations.delete.annotation.' + geometry);
25462 // Select the next closest node in the way.
25463 if (geometry === 'vertex' && parents.length === 1 && parent.nodes.length > 2) {
25464 var nodes = parent.nodes,
25465 i = nodes.indexOf(id);
25469 } else if (i === nodes.length - 1) {
25472 var a = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc),
25473 b = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
25474 i = a < b ? i - 1 : i + 1;
25477 nextSelectedID = nodes[i];
25481 if (nextSelectedID && context.hasEntity(nextSelectedID)) {
25482 context.enter(iD.modes.Select(context, [nextSelectedID]));
25484 context.enter(iD.modes.Browse(context));
25492 operation.available = function() {
25496 operation.disabled = function() {
25498 if (_.any(selectedIDs, context.hasHiddenConnections)) {
25499 reason = 'connected_to_hidden';
25501 return action.disabled(context.graph()) || reason;
25504 operation.tooltip = function() {
25505 var disable = operation.disabled();
25507 t('operations.delete.' + disable) :
25508 t('operations.delete.description');
25511 operation.id = 'delete';
25512 operation.keys = [iD.ui.cmd('⌘⌫'), iD.ui.cmd('⌘⌦')];
25513 operation.title = t('operations.delete.title');
25517 iD.operations.Disconnect = function(selectedIDs, context) {
25518 var vertices = _.filter(selectedIDs, function vertex(entityId) {
25519 return context.geometry(entityId) === 'vertex';
25522 var entityId = vertices[0],
25523 action = iD.actions.Disconnect(entityId);
25525 if (selectedIDs.length > 1) {
25526 action.limitWays(_.without(selectedIDs, entityId));
25529 var operation = function() {
25530 context.perform(action, t('operations.disconnect.annotation'));
25533 operation.available = function() {
25534 return vertices.length === 1;
25537 operation.disabled = function() {
25539 if (_.any(selectedIDs, context.hasHiddenConnections)) {
25540 reason = 'connected_to_hidden';
25542 return action.disabled(context.graph()) || reason;
25545 operation.tooltip = function() {
25546 var disable = operation.disabled();
25548 t('operations.disconnect.' + disable) :
25549 t('operations.disconnect.description');
25552 operation.id = 'disconnect';
25553 operation.keys = [t('operations.disconnect.key')];
25554 operation.title = t('operations.disconnect.title');
25558 iD.operations.Merge = function(selectedIDs, context) {
25559 var join = iD.actions.Join(selectedIDs),
25560 merge = iD.actions.Merge(selectedIDs),
25561 mergePolygon = iD.actions.MergePolygon(selectedIDs);
25563 var operation = function() {
25564 var annotation = t('operations.merge.annotation', {n: selectedIDs.length}),
25567 if (!join.disabled(context.graph())) {
25569 } else if (!merge.disabled(context.graph())) {
25572 action = mergePolygon;
25575 context.perform(action, annotation);
25576 context.enter(iD.modes.Select(context, selectedIDs.filter(function(id) { return context.hasEntity(id); }))
25577 .suppressMenu(true));
25580 operation.available = function() {
25581 return selectedIDs.length >= 2;
25584 operation.disabled = function() {
25585 return join.disabled(context.graph()) &&
25586 merge.disabled(context.graph()) &&
25587 mergePolygon.disabled(context.graph());
25590 operation.tooltip = function() {
25591 var j = join.disabled(context.graph()),
25592 m = merge.disabled(context.graph()),
25593 p = mergePolygon.disabled(context.graph());
25595 if (j === 'restriction' && m && p)
25596 return t('operations.merge.restriction', {relation: context.presets().item('type/restriction').name()});
25598 if (p === 'incomplete_relation' && j && m)
25599 return t('operations.merge.incomplete_relation');
25602 return t('operations.merge.' + j);
25604 return t('operations.merge.description');
25607 operation.id = 'merge';
25608 operation.keys = [t('operations.merge.key')];
25609 operation.title = t('operations.merge.title');
25613 iD.operations.Move = function(selectedIDs, context) {
25614 var extent = selectedIDs.reduce(function(extent, id) {
25615 return extent.extend(context.entity(id).extent(context.graph()));
25616 }, iD.geo.Extent());
25618 var operation = function() {
25619 context.enter(iD.modes.Move(context, selectedIDs));
25622 operation.available = function() {
25623 return selectedIDs.length > 1 ||
25624 context.entity(selectedIDs[0]).type !== 'node';
25627 operation.disabled = function() {
25629 if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
25630 reason = 'too_large';
25631 } else if (_.any(selectedIDs, context.hasHiddenConnections)) {
25632 reason = 'connected_to_hidden';
25634 return iD.actions.Move(selectedIDs).disabled(context.graph()) || reason;
25637 operation.tooltip = function() {
25638 var disable = operation.disabled();
25640 t('operations.move.' + disable) :
25641 t('operations.move.description');
25644 operation.id = 'move';
25645 operation.keys = [t('operations.move.key')];
25646 operation.title = t('operations.move.title');
25650 iD.operations.Orthogonalize = function(selectedIDs, context) {
25651 var entityId = selectedIDs[0],
25652 entity = context.entity(entityId),
25653 extent = entity.extent(context.graph()),
25654 geometry = context.geometry(entityId),
25655 action = iD.actions.Orthogonalize(entityId, context.projection);
25657 var operation = function() {
25658 var annotation = t('operations.orthogonalize.annotation.' + geometry);
25659 context.perform(action, annotation);
25662 operation.available = function() {
25663 return selectedIDs.length === 1 &&
25664 entity.type === 'way' &&
25665 entity.isClosed() &&
25666 _.uniq(entity.nodes).length > 2;
25669 operation.disabled = function() {
25671 if (extent.percentContainedIn(context.extent()) < 0.8) {
25672 reason = 'too_large';
25673 } else if (context.hasHiddenConnections(entityId)) {
25674 reason = 'connected_to_hidden';
25676 return action.disabled(context.graph()) || reason;
25679 operation.tooltip = function() {
25680 var disable = operation.disabled();
25682 t('operations.orthogonalize.' + disable) :
25683 t('operations.orthogonalize.description.' + geometry);
25686 operation.id = 'orthogonalize';
25687 operation.keys = [t('operations.orthogonalize.key')];
25688 operation.title = t('operations.orthogonalize.title');
25692 iD.operations.Reverse = function(selectedIDs, context) {
25693 var entityId = selectedIDs[0];
25695 var operation = function() {
25697 iD.actions.Reverse(entityId),
25698 t('operations.reverse.annotation'));
25701 operation.available = function() {
25702 return selectedIDs.length === 1 &&
25703 context.geometry(entityId) === 'line';
25706 operation.disabled = function() {
25710 operation.tooltip = function() {
25711 return t('operations.reverse.description');
25714 operation.id = 'reverse';
25715 operation.keys = [t('operations.reverse.key')];
25716 operation.title = t('operations.reverse.title');
25720 iD.operations.Rotate = function(selectedIDs, context) {
25721 var entityId = selectedIDs[0],
25722 entity = context.entity(entityId),
25723 extent = entity.extent(context.graph()),
25724 geometry = context.geometry(entityId);
25726 var operation = function() {
25727 context.enter(iD.modes.RotateWay(context, entityId));
25730 operation.available = function() {
25731 if (selectedIDs.length !== 1 || entity.type !== 'way')
25733 if (geometry === 'area')
25735 if (entity.isClosed() &&
25736 context.graph().parentRelations(entity).some(function(r) { return r.isMultipolygon(); }))
25741 operation.disabled = function() {
25742 if (extent.percentContainedIn(context.extent()) < 0.8) {
25743 return 'too_large';
25744 } else if (context.hasHiddenConnections(entityId)) {
25745 return 'connected_to_hidden';
25751 operation.tooltip = function() {
25752 var disable = operation.disabled();
25754 t('operations.rotate.' + disable) :
25755 t('operations.rotate.description');
25758 operation.id = 'rotate';
25759 operation.keys = [t('operations.rotate.key')];
25760 operation.title = t('operations.rotate.title');
25764 iD.operations.Split = function(selectedIDs, context) {
25765 var vertices = _.filter(selectedIDs, function vertex(entityId) {
25766 return context.geometry(entityId) === 'vertex';
25769 var entityId = vertices[0],
25770 action = iD.actions.Split(entityId);
25772 if (selectedIDs.length > 1) {
25773 action.limitWays(_.without(selectedIDs, entityId));
25776 var operation = function() {
25779 var ways = action.ways(context.graph());
25780 if (ways.length === 1) {
25781 annotation = t('operations.split.annotation.' + context.geometry(ways[0].id));
25783 annotation = t('operations.split.annotation.multiple', {n: ways.length});
25786 var difference = context.perform(action, annotation);
25787 context.enter(iD.modes.Select(context, difference.extantIDs()));
25790 operation.available = function() {
25791 return vertices.length === 1;
25794 operation.disabled = function() {
25796 if (_.any(selectedIDs, context.hasHiddenConnections)) {
25797 reason = 'connected_to_hidden';
25799 return action.disabled(context.graph()) || reason;
25802 operation.tooltip = function() {
25803 var disable = operation.disabled();
25805 return t('operations.split.' + disable);
25808 var ways = action.ways(context.graph());
25809 if (ways.length === 1) {
25810 return t('operations.split.description.' + context.geometry(ways[0].id));
25812 return t('operations.split.description.multiple');
25816 operation.id = 'split';
25817 operation.keys = [t('operations.split.key')];
25818 operation.title = t('operations.split.title');
25822 iD.operations.Straighten = function(selectedIDs, context) {
25823 var entityId = selectedIDs[0],
25824 action = iD.actions.Straighten(entityId, context.projection);
25826 function operation() {
25827 var annotation = t('operations.straighten.annotation');
25828 context.perform(action, annotation);
25831 operation.available = function() {
25832 var entity = context.entity(entityId);
25833 return selectedIDs.length === 1 &&
25834 entity.type === 'way' &&
25835 !entity.isClosed() &&
25836 _.uniq(entity.nodes).length > 2;
25839 operation.disabled = function() {
25841 if (context.hasHiddenConnections(entityId)) {
25842 reason = 'connected_to_hidden';
25844 return action.disabled(context.graph()) || reason;
25847 operation.tooltip = function() {
25848 var disable = operation.disabled();
25850 t('operations.straighten.' + disable) :
25851 t('operations.straighten.description');
25854 operation.id = 'straighten';
25855 operation.keys = [t('operations.straighten.key')];
25856 operation.title = t('operations.straighten.title');
25860 iD.Connection = function(useHttps) {
25861 if (typeof useHttps !== 'boolean') {
25862 useHttps = window.location.protocol === 'https:';
25865 var event = d3.dispatch('authenticating', 'authenticated', 'auth', 'loading', 'loaded'),
25866 protocol = useHttps ? 'https:' : 'http:',
25867 url = protocol + '//www.openstreetmap.org',
25873 url: protocol + '//www.openstreetmap.org',
25874 oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
25875 oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
25876 loading: authenticating,
25877 done: authenticated
25881 memberStr = 'member',
25884 relationStr = 'relation',
25889 connection.changesetURL = function(changesetId) {
25890 return url + '/changeset/' + changesetId;
25893 connection.changesetsURL = function(center, zoom) {
25894 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
25895 return url + '/history#map=' +
25896 Math.floor(zoom) + '/' +
25897 center[1].toFixed(precision) + '/' +
25898 center[0].toFixed(precision);
25901 connection.entityURL = function(entity) {
25902 return url + '/' + entity.type + '/' + entity.osmId();
25905 connection.userURL = function(username) {
25906 return url + '/user/' + username;
25909 connection.loadFromURL = function(url, callback) {
25910 function done(err, dom) {
25911 return callback(err, parse(dom));
25913 return d3.xml(url).get(done);
25916 connection.loadEntity = function(id, callback) {
25917 var type = iD.Entity.id.type(id),
25918 osmID = iD.Entity.id.toOSM(id);
25920 connection.loadFromURL(
25921 url + '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''),
25922 function(err, entities) {
25923 if (callback) callback(err, {data: entities});
25927 connection.loadEntityVersion = function(id, version, callback) {
25928 var type = iD.Entity.id.type(id),
25929 osmID = iD.Entity.id.toOSM(id);
25931 connection.loadFromURL(
25932 url + '/api/0.6/' + type + '/' + osmID + '/' + version,
25933 function(err, entities) {
25934 if (callback) callback(err, {data: entities});
25938 connection.loadMultiple = function(ids, callback) {
25939 _.each(_.groupBy(_.uniq(ids), iD.Entity.id.type), function(v, k) {
25940 var type = k + 's',
25941 osmIDs = _.map(v, iD.Entity.id.toOSM);
25943 _.each(_.chunk(osmIDs, 150), function(arr) {
25944 connection.loadFromURL(
25945 url + '/api/0.6/' + type + '?' + type + '=' + arr.join(),
25946 function(err, entities) {
25947 if (callback) callback(err, {data: entities});
25953 function authenticating() {
25954 event.authenticating();
25957 function authenticated() {
25958 event.authenticated();
25961 function getLoc(attrs) {
25962 var lon = attrs.lon && attrs.lon.value,
25963 lat = attrs.lat && attrs.lat.value;
25964 return [parseFloat(lon), parseFloat(lat)];
25967 function getNodes(obj) {
25968 var elems = obj.getElementsByTagName(ndStr),
25969 nodes = new Array(elems.length);
25970 for (var i = 0, l = elems.length; i < l; i++) {
25971 nodes[i] = 'n' + elems[i].attributes.ref.value;
25976 function getTags(obj) {
25977 var elems = obj.getElementsByTagName(tagStr),
25979 for (var i = 0, l = elems.length; i < l; i++) {
25980 var attrs = elems[i].attributes;
25981 tags[attrs.k.value] = attrs.v.value;
25986 function getMembers(obj) {
25987 var elems = obj.getElementsByTagName(memberStr),
25988 members = new Array(elems.length);
25989 for (var i = 0, l = elems.length; i < l; i++) {
25990 var attrs = elems[i].attributes;
25992 id: attrs.type.value[0] + attrs.ref.value,
25993 type: attrs.type.value,
25994 role: attrs.role.value
26000 function getVisible(attrs) {
26001 return (!attrs.visible || attrs.visible.value !== 'false');
26005 node: function nodeData(obj) {
26006 var attrs = obj.attributes;
26007 return new iD.Node({
26008 id: iD.Entity.id.fromOSM(nodeStr, attrs.id.value),
26009 loc: getLoc(attrs),
26010 version: attrs.version.value,
26011 user: attrs.user && attrs.user.value,
26012 tags: getTags(obj),
26013 visible: getVisible(attrs)
26017 way: function wayData(obj) {
26018 var attrs = obj.attributes;
26019 return new iD.Way({
26020 id: iD.Entity.id.fromOSM(wayStr, attrs.id.value),
26021 version: attrs.version.value,
26022 user: attrs.user && attrs.user.value,
26023 tags: getTags(obj),
26024 nodes: getNodes(obj),
26025 visible: getVisible(attrs)
26029 relation: function relationData(obj) {
26030 var attrs = obj.attributes;
26031 return new iD.Relation({
26032 id: iD.Entity.id.fromOSM(relationStr, attrs.id.value),
26033 version: attrs.version.value,
26034 user: attrs.user && attrs.user.value,
26035 tags: getTags(obj),
26036 members: getMembers(obj),
26037 visible: getVisible(attrs)
26042 function parse(dom) {
26043 if (!dom || !dom.childNodes) return;
26045 var root = dom.childNodes[0],
26046 children = root.childNodes,
26049 for (var i = 0, l = children.length; i < l; i++) {
26050 var child = children[i],
26051 parser = parsers[child.nodeName];
26053 entities.push(parser(child));
26060 connection.authenticated = function() {
26061 return oauth.authenticated();
26064 // Generate Changeset XML. Returns a string.
26065 connection.changesetJXON = function(tags) {
26069 tag: _.map(tags, function(value, key) {
26070 return { '@k': key, '@v': value };
26079 // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
26080 // XML. Returns a string.
26081 connection.osmChangeJXON = function(changeset_id, changes) {
26082 function nest(x, order) {
26084 for (var i = 0; i < x.length; i++) {
26085 var tagName = Object.keys(x[i])[0];
26086 if (!groups[tagName]) groups[tagName] = [];
26087 groups[tagName].push(x[i][tagName]);
26090 order.forEach(function(o) {
26091 if (groups[o]) ordered[o] = groups[o];
26096 function rep(entity) {
26097 return entity.asJXON(changeset_id);
26103 '@generator': 'iD',
26104 'create': nest(changes.created.map(rep), ['node', 'way', 'relation']),
26105 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
26106 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true})
26111 connection.changesetTags = function(comment, imageryUsed) {
26112 var detected = iD.detect(),
26114 created_by: 'iD ' + iD.version,
26115 imagery_used: imageryUsed.join(';').substr(0, 255),
26116 host: (window.location.origin + window.location.pathname).substr(0, 255),
26117 locale: detected.locale
26121 tags.comment = comment.substr(0, 255);
26127 connection.putChangeset = function(changes, comment, imageryUsed, callback) {
26130 path: '/api/0.6/changeset/create',
26131 options: { header: { 'Content-Type': 'text/xml' } },
26132 content: JXON.stringify(connection.changesetJXON(connection.changesetTags(comment, imageryUsed)))
26133 }, function(err, changeset_id) {
26134 if (err) return callback(err);
26137 path: '/api/0.6/changeset/' + changeset_id + '/upload',
26138 options: { header: { 'Content-Type': 'text/xml' } },
26139 content: JXON.stringify(connection.osmChangeJXON(changeset_id, changes))
26141 if (err) return callback(err);
26142 // POST was successful, safe to call the callback.
26143 // Still attempt to close changeset, but ignore response because #2667
26144 // Add delay to allow for postgres replication #1646 #2678
26145 window.setTimeout(function() { callback(null, changeset_id); }, 2500);
26148 path: '/api/0.6/changeset/' + changeset_id + '/close',
26149 options: { header: { 'Content-Type': 'text/xml' } }
26150 }, d3.functor(true));
26155 connection.userDetails = function(callback) {
26157 callback(undefined, userDetails);
26161 function done(err, user_details) {
26162 if (err) return callback(err);
26164 var u = user_details.getElementsByTagName('user')[0],
26165 img = u.getElementsByTagName('img'),
26168 if (img && img[0] && img[0].getAttribute('href')) {
26169 image_url = img[0].getAttribute('href');
26173 display_name: u.attributes.display_name.value,
26174 image_url: image_url,
26175 id: u.attributes.id.value
26178 callback(undefined, userDetails);
26181 oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
26184 connection.status = function(callback) {
26185 function done(capabilities) {
26186 var apiStatus = capabilities.getElementsByTagName('status');
26187 callback(undefined, apiStatus[0].getAttribute('api'));
26189 d3.xml(url + '/api/capabilities').get()
26191 .on('error', callback);
26194 function abortRequest(i) { i.abort(); }
26196 connection.tileZoom = function(_) {
26197 if (!arguments.length) return tileZoom;
26202 connection.loadTiles = function(projection, dimensions, callback) {
26206 var s = projection.scale() * 2 * Math.PI,
26207 z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
26208 ts = 256 * Math.pow(2, z - tileZoom),
26210 s / 2 - projection.translate()[0],
26211 s / 2 - projection.translate()[1]];
26213 var tiles = d3.geo.tile()
26214 .scaleExtent([tileZoom, tileZoom])
26217 .translate(projection.translate())()
26218 .map(function(tile) {
26219 var x = tile[0] * ts - origin[0],
26220 y = tile[1] * ts - origin[1];
26223 id: tile.toString(),
26224 extent: iD.geo.Extent(
26225 projection.invert([x, y + ts]),
26226 projection.invert([x + ts, y]))
26230 function bboxUrl(tile) {
26231 return url + '/api/0.6/map?bbox=' + tile.extent.toParam();
26234 _.filter(inflight, function(v, i) {
26235 var wanted = _.find(tiles, function(tile) {
26236 return i === tile.id;
26238 if (!wanted) delete inflight[i];
26240 }).map(abortRequest);
26242 tiles.forEach(function(tile) {
26245 if (loadedTiles[id] || inflight[id]) return;
26247 if (_.isEmpty(inflight)) {
26251 inflight[id] = connection.loadFromURL(bboxUrl(tile), function(err, parsed) {
26252 loadedTiles[id] = true;
26253 delete inflight[id];
26255 if (callback) callback(err, _.extend({data: parsed}, tile));
26257 if (_.isEmpty(inflight)) {
26264 connection.switch = function(options) {
26266 oauth.options(_.extend({
26267 loading: authenticating,
26268 done: authenticated
26271 connection.flush();
26275 connection.toggle = function(_) {
26280 connection.flush = function() {
26281 userDetails = undefined;
26282 _.forEach(inflight, abortRequest);
26288 connection.loadedTiles = function(_) {
26289 if (!arguments.length) return loadedTiles;
26294 connection.logout = function() {
26295 userDetails = undefined;
26301 connection.authenticate = function(callback) {
26302 userDetails = undefined;
26303 function done(err, res) {
26305 if (callback) callback(err, res);
26307 return oauth.authenticate(done);
26310 return d3.rebind(connection, event, 'on');
26313 iD.Difference represents the difference between two graphs.
26314 It knows how to calculate the set of entities that were
26315 created, modified, or deleted, and also contains the logic
26316 for recursively extending a difference to the complete set
26317 of entities that will require a redraw, taking into account
26318 child and parent relationships.
26320 iD.Difference = function(base, head) {
26321 var changes = {}, length = 0;
26323 function changed(h, b) {
26324 return h !== b && !_.isEqual(_.omit(h, 'v'), _.omit(b, 'v'));
26327 _.each(head.entities, function(h, id) {
26328 var b = base.entities[id];
26329 if (changed(h, b)) {
26330 changes[id] = {base: b, head: h};
26335 _.each(base.entities, function(b, id) {
26336 var h = head.entities[id];
26337 if (!changes[id] && changed(h, b)) {
26338 changes[id] = {base: b, head: h};
26343 function addParents(parents, result) {
26344 for (var i = 0; i < parents.length; i++) {
26345 var parent = parents[i];
26347 if (parent.id in result)
26350 result[parent.id] = parent;
26351 addParents(head.parentRelations(parent), result);
26355 var difference = {};
26357 difference.length = function() {
26361 difference.changes = function() {
26365 difference.extantIDs = function() {
26367 _.each(changes, function(change, id) {
26368 if (change.head) result.push(id);
26373 difference.modified = function() {
26375 _.each(changes, function(change) {
26376 if (change.base && change.head) result.push(change.head);
26381 difference.created = function() {
26383 _.each(changes, function(change) {
26384 if (!change.base && change.head) result.push(change.head);
26389 difference.deleted = function() {
26391 _.each(changes, function(change) {
26392 if (change.base && !change.head) result.push(change.base);
26397 difference.summary = function() {
26400 function addEntity(entity, graph, changeType) {
26401 relevant[entity.id] = {
26404 changeType: changeType
26408 function addParents(entity) {
26409 var parents = head.parentWays(entity);
26410 for (var j = parents.length - 1; j >= 0; j--) {
26411 var parent = parents[j];
26412 if (!(parent.id in relevant)) addEntity(parent, head, 'modified');
26416 _.each(changes, function(change) {
26417 if (change.head && change.head.geometry(head) !== 'vertex') {
26418 addEntity(change.head, head, change.base ? 'modified' : 'created');
26420 } else if (change.base && change.base.geometry(base) !== 'vertex') {
26421 addEntity(change.base, base, 'deleted');
26423 } else if (change.base && change.head) { // modified vertex
26424 var moved = !_.isEqual(change.base.loc, change.head.loc),
26425 retagged = !_.isEqual(change.base.tags, change.head.tags);
26428 addParents(change.head);
26431 if (retagged || (moved && change.head.hasInterestingTags())) {
26432 addEntity(change.head, head, 'modified');
26435 } else if (change.head && change.head.hasInterestingTags()) { // created vertex
26436 addEntity(change.head, head, 'created');
26438 } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
26439 addEntity(change.base, base, 'deleted');
26443 return d3.values(relevant);
26446 difference.complete = function(extent) {
26447 var result = {}, id, change;
26449 for (id in changes) {
26450 change = changes[id];
26452 var h = change.head,
26457 (!h || !h.intersects(extent, head)) &&
26458 (!b || !b.intersects(extent, base)))
26463 if (entity.type === 'way') {
26464 var nh = h ? h.nodes : [],
26465 nb = b ? b.nodes : [],
26468 diff = _.difference(nh, nb);
26469 for (i = 0; i < diff.length; i++) {
26470 result[diff[i]] = head.hasEntity(diff[i]);
26473 diff = _.difference(nb, nh);
26474 for (i = 0; i < diff.length; i++) {
26475 result[diff[i]] = head.hasEntity(diff[i]);
26479 addParents(head.parentWays(entity), result);
26480 addParents(head.parentRelations(entity), result);
26488 iD.Entity = function(attrs) {
26489 // For prototypal inheritance.
26490 if (this instanceof iD.Entity) return;
26492 // Create the appropriate subtype.
26493 if (attrs && attrs.type) {
26494 return iD.Entity[attrs.type].apply(this, arguments);
26495 } else if (attrs && attrs.id) {
26496 return iD.Entity[iD.Entity.id.type(attrs.id)].apply(this, arguments);
26499 // Initialize a generic Entity (used only in tests).
26500 return (new iD.Entity()).initialize(arguments);
26503 iD.Entity.id = function(type) {
26504 return iD.Entity.id.fromOSM(type, iD.Entity.id.next[type]--);
26507 iD.Entity.id.next = {node: -1, way: -1, relation: -1};
26509 iD.Entity.id.fromOSM = function(type, id) {
26510 return type[0] + id;
26513 iD.Entity.id.toOSM = function(id) {
26514 return id.slice(1);
26517 iD.Entity.id.type = function(id) {
26518 return {'n': 'node', 'w': 'way', 'r': 'relation'}[id[0]];
26521 // A function suitable for use as the second argument to d3.selection#data().
26522 iD.Entity.key = function(entity) {
26523 return entity.id + 'v' + (entity.v || 0);
26526 iD.Entity.prototype = {
26529 initialize: function(sources) {
26530 for (var i = 0; i < sources.length; ++i) {
26531 var source = sources[i];
26532 for (var prop in source) {
26533 if (Object.prototype.hasOwnProperty.call(source, prop)) {
26534 if (source[prop] === undefined) {
26537 this[prop] = source[prop];
26543 if (!this.id && this.type) {
26544 this.id = iD.Entity.id(this.type);
26546 if (!this.hasOwnProperty('visible')) {
26547 this.visible = true;
26551 Object.freeze(this);
26552 Object.freeze(this.tags);
26554 if (this.loc) Object.freeze(this.loc);
26555 if (this.nodes) Object.freeze(this.nodes);
26556 if (this.members) Object.freeze(this.members);
26563 // Returns an array so that we can support deep copying ways and relations.
26564 // The first array element will contain this.copy, followed by any descendants.
26565 return [iD.Entity(this, {id: undefined, user: undefined, version: undefined})];
26568 osmId: function() {
26569 return iD.Entity.id.toOSM(this.id);
26572 isNew: function() {
26573 return this.osmId() < 0;
26576 update: function(attrs) {
26577 return iD.Entity(this, attrs, {v: 1 + (this.v || 0)});
26580 mergeTags: function(tags) {
26581 var merged = _.clone(this.tags), changed = false;
26582 for (var k in tags) {
26583 var t1 = merged[k],
26588 } else if (t1 !== t2) {
26590 merged[k] = _.union(t1.split(/;\s*/), t2.split(/;\s*/)).join(';');
26593 return changed ? this.update({tags: merged}) : this;
26596 intersects: function(extent, resolver) {
26597 return this.extent(resolver).intersects(extent);
26600 isUsed: function(resolver) {
26601 return _.without(Object.keys(this.tags), 'area').length > 0 ||
26602 resolver.parentRelations(this).length > 0;
26605 hasInterestingTags: function() {
26606 return _.keys(this.tags).some(iD.interestingTag);
26609 isHighwayIntersection: function() {
26613 deprecatedTags: function() {
26614 var tags = _.pairs(this.tags);
26615 var deprecated = {};
26617 iD.data.deprecated.forEach(function(d) {
26618 var match = _.pairs(d.old)[0];
26619 tags.forEach(function(t) {
26620 if (t[0] === match[0] &&
26621 (t[1] === match[1] || match[1] === '*')) {
26622 deprecated[t[0]] = t[1];
26630 iD.Graph = function(other, mutable) {
26631 if (!(this instanceof iD.Graph)) return new iD.Graph(other, mutable);
26633 if (other instanceof iD.Graph) {
26634 var base = other.base();
26635 this.entities = _.assign(Object.create(base.entities), other.entities);
26636 this._parentWays = _.assign(Object.create(base.parentWays), other._parentWays);
26637 this._parentRels = _.assign(Object.create(base.parentRels), other._parentRels);
26640 this.entities = Object.create({});
26641 this._parentWays = Object.create({});
26642 this._parentRels = Object.create({});
26643 this.rebase(other || [], [this]);
26646 this.transients = {};
26647 this._childNodes = {};
26648 this.frozen = !mutable;
26651 iD.Graph.prototype = {
26652 hasEntity: function(id) {
26653 return this.entities[id];
26656 entity: function(id) {
26657 var entity = this.entities[id];
26659 throw new Error('entity ' + id + ' not found');
26664 transient: function(entity, key, fn) {
26665 var id = entity.id,
26666 transients = this.transients[id] ||
26667 (this.transients[id] = {});
26669 if (transients[key] !== undefined) {
26670 return transients[key];
26673 transients[key] = fn.call(entity);
26675 return transients[key];
26678 parentWays: function(entity) {
26679 var parents = this._parentWays[entity.id],
26683 for (var i = 0; i < parents.length; i++) {
26684 result.push(this.entity(parents[i]));
26690 isPoi: function(entity) {
26691 var parentWays = this._parentWays[entity.id];
26692 return !parentWays || parentWays.length === 0;
26695 isShared: function(entity) {
26696 var parentWays = this._parentWays[entity.id];
26697 return parentWays && parentWays.length > 1;
26700 parentRelations: function(entity) {
26701 var parents = this._parentRels[entity.id],
26705 for (var i = 0; i < parents.length; i++) {
26706 result.push(this.entity(parents[i]));
26712 childNodes: function(entity) {
26713 if (this._childNodes[entity.id]) return this._childNodes[entity.id];
26714 if (!entity.nodes) return [];
26717 for (var i = 0; i < entity.nodes.length; i++) {
26718 nodes[i] = this.entity(entity.nodes[i]);
26721 if (iD.debug) Object.freeze(nodes);
26723 this._childNodes[entity.id] = nodes;
26724 return this._childNodes[entity.id];
26729 'entities': iD.util.getPrototypeOf(this.entities),
26730 'parentWays': iD.util.getPrototypeOf(this._parentWays),
26731 'parentRels': iD.util.getPrototypeOf(this._parentRels)
26735 // Unlike other graph methods, rebase mutates in place. This is because it
26736 // is used only during the history operation that merges newly downloaded
26737 // data into each state. To external consumers, it should appear as if the
26738 // graph always contained the newly downloaded data.
26739 rebase: function(entities, stack, force) {
26740 var base = this.base(),
26743 for (i = 0; i < entities.length; i++) {
26744 var entity = entities[i];
26746 if (!entity.visible || (!force && base.entities[entity.id]))
26749 // Merging data into the base graph
26750 base.entities[entity.id] = entity;
26751 this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
26753 // Restore provisionally-deleted nodes that are discovered to have an extant parent
26754 if (entity.type === 'way') {
26755 for (j = 0; j < entity.nodes.length; j++) {
26756 id = entity.nodes[j];
26757 for (k = 1; k < stack.length; k++) {
26758 var ents = stack[k].entities;
26759 if (ents.hasOwnProperty(id) && ents[id] === undefined) {
26767 for (i = 0; i < stack.length; i++) {
26768 stack[i]._updateRebased();
26772 _updateRebased: function() {
26773 var base = this.base(),
26774 i, k, child, id, keys;
26776 keys = Object.keys(this._parentWays);
26777 for (i = 0; i < keys.length; i++) {
26779 if (base.parentWays[child]) {
26780 for (k = 0; k < base.parentWays[child].length; k++) {
26781 id = base.parentWays[child][k];
26782 if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentWays[child], id)) {
26783 this._parentWays[child].push(id);
26789 keys = Object.keys(this._parentRels);
26790 for (i = 0; i < keys.length; i++) {
26792 if (base.parentRels[child]) {
26793 for (k = 0; k < base.parentRels[child].length; k++) {
26794 id = base.parentRels[child][k];
26795 if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentRels[child], id)) {
26796 this._parentRels[child].push(id);
26802 this.transients = {};
26804 // this._childNodes is not updated, under the assumption that
26805 // ways are always downloaded with their child nodes.
26808 // Updates calculated properties (parentWays, parentRels) for the specified change
26809 _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
26811 parentWays = parentWays || this._parentWays;
26812 parentRels = parentRels || this._parentRels;
26814 var type = entity && entity.type || oldentity && oldentity.type,
26815 removed, added, ways, rels, i;
26818 if (type === 'way') {
26820 // Update parentWays
26821 if (oldentity && entity) {
26822 removed = _.difference(oldentity.nodes, entity.nodes);
26823 added = _.difference(entity.nodes, oldentity.nodes);
26824 } else if (oldentity) {
26825 removed = oldentity.nodes;
26827 } else if (entity) {
26829 added = entity.nodes;
26831 for (i = 0; i < removed.length; i++) {
26832 parentWays[removed[i]] = _.without(parentWays[removed[i]], oldentity.id);
26834 for (i = 0; i < added.length; i++) {
26835 ways = _.without(parentWays[added[i]], entity.id);
26836 ways.push(entity.id);
26837 parentWays[added[i]] = ways;
26840 } else if (type === 'relation') {
26842 // Update parentRels
26843 if (oldentity && entity) {
26844 removed = _.difference(oldentity.members, entity.members);
26845 added = _.difference(entity.members, oldentity);
26846 } else if (oldentity) {
26847 removed = oldentity.members;
26849 } else if (entity) {
26851 added = entity.members;
26853 for (i = 0; i < removed.length; i++) {
26854 parentRels[removed[i].id] = _.without(parentRels[removed[i].id], oldentity.id);
26856 for (i = 0; i < added.length; i++) {
26857 rels = _.without(parentRels[added[i].id], entity.id);
26858 rels.push(entity.id);
26859 parentRels[added[i].id] = rels;
26864 replace: function(entity) {
26865 if (this.entities[entity.id] === entity)
26868 return this.update(function() {
26869 this._updateCalculated(this.entities[entity.id], entity);
26870 this.entities[entity.id] = entity;
26874 remove: function(entity) {
26875 return this.update(function() {
26876 this._updateCalculated(entity, undefined);
26877 this.entities[entity.id] = undefined;
26881 revert: function(id) {
26882 var baseEntity = this.base().entities[id],
26883 headEntity = this.entities[id];
26885 if (headEntity === baseEntity)
26888 return this.update(function() {
26889 this._updateCalculated(headEntity, baseEntity);
26890 delete this.entities[id];
26894 update: function() {
26895 var graph = this.frozen ? iD.Graph(this, true) : this;
26897 for (var i = 0; i < arguments.length; i++) {
26898 arguments[i].call(graph, graph);
26901 if (this.frozen) graph.frozen = true;
26906 // Obliterates any existing entities
26907 load: function(entities) {
26908 var base = this.base();
26909 this.entities = Object.create(base.entities);
26911 for (var i in entities) {
26912 this.entities[i] = entities[i];
26913 this._updateCalculated(base.entities[i], this.entities[i]);
26919 iD.History = function(context) {
26920 var stack, index, tree,
26921 imageryUsed = ['Bing'],
26922 dispatch = d3.dispatch('change', 'undone', 'redone'),
26923 lock = iD.util.SessionMutex('lock');
26925 function perform(actions) {
26926 actions = Array.prototype.slice.call(actions);
26930 if (!_.isFunction(_.last(actions))) {
26931 annotation = actions.pop();
26934 var graph = stack[index].graph;
26935 for (var i = 0; i < actions.length; i++) {
26936 graph = actions[i](graph);
26941 annotation: annotation,
26942 imageryUsed: imageryUsed
26946 function change(previous) {
26947 var difference = iD.Difference(previous, history.graph());
26948 dispatch.change(difference);
26952 // iD uses namespaced keys so multiple installations do not conflict
26953 function getKey(n) {
26954 return 'iD_' + window.location.origin + '_' + n;
26958 graph: function() {
26959 return stack[index].graph;
26963 return stack[0].graph;
26966 merge: function(entities, extent) {
26967 stack[0].graph.rebase(entities, _.pluck(stack, 'graph'), false);
26968 tree.rebase(entities, false);
26970 dispatch.change(undefined, extent);
26973 perform: function() {
26974 var previous = stack[index].graph;
26976 stack = stack.slice(0, index + 1);
26977 stack.push(perform(arguments));
26980 return change(previous);
26983 replace: function() {
26984 var previous = stack[index].graph;
26986 // assert(index == stack.length - 1)
26987 stack[index] = perform(arguments);
26989 return change(previous);
26993 var previous = stack[index].graph;
26998 return change(previous);
27002 // Same as calling pop and then perform
27003 overwrite: function() {
27004 var previous = stack[index].graph;
27010 stack = stack.slice(0, index + 1);
27011 stack.push(perform(arguments));
27014 return change(previous);
27018 var previous = stack[index].graph;
27020 // Pop to the next annotated state.
27021 while (index > 0) {
27023 if (stack[index].annotation) break;
27027 return change(previous);
27031 var previous = stack[index].graph;
27033 while (index < stack.length - 1) {
27035 if (stack[index].annotation) break;
27039 return change(previous);
27042 undoAnnotation: function() {
27045 if (stack[i].annotation) return stack[i].annotation;
27050 redoAnnotation: function() {
27052 while (i <= stack.length - 1) {
27053 if (stack[i].annotation) return stack[i].annotation;
27058 intersects: function(extent) {
27059 return tree.intersects(extent, stack[index].graph);
27062 difference: function() {
27063 var base = stack[0].graph,
27064 head = stack[index].graph;
27065 return iD.Difference(base, head);
27068 changes: function(action) {
27069 var base = stack[0].graph,
27070 head = stack[index].graph;
27073 head = action(head);
27076 var difference = iD.Difference(base, head);
27079 modified: difference.modified(),
27080 created: difference.created(),
27081 deleted: difference.deleted()
27085 validate: function(changes) {
27086 return _(iD.validations)
27087 .map(function(fn) { return fn()(changes, stack[index].graph); })
27092 hasChanges: function() {
27093 return this.difference().length() > 0;
27096 imageryUsed: function(sources) {
27098 imageryUsed = sources;
27101 return _(stack.slice(1, index + 1))
27102 .pluck('imageryUsed')
27105 .without(undefined, 'Custom')
27110 reset: function() {
27111 stack = [{graph: iD.Graph()}];
27113 tree = iD.Tree(stack[0].graph);
27118 toJSON: function() {
27119 if (!this.hasChanges()) return;
27121 var allEntities = {},
27125 var s = stack.map(function(i) {
27126 var modified = [], deleted = [];
27128 _.forEach(i.graph.entities, function(entity, id) {
27130 var key = iD.Entity.key(entity);
27131 allEntities[key] = entity;
27132 modified.push(key);
27137 // make sure that the originals of changed or deleted entities get merged
27138 // into the base of the stack after restoring the data from JSON.
27139 if (id in base.graph.entities) {
27140 baseEntities[id] = base.graph.entities[id];
27142 // get originals of parent entities too
27143 _.forEach(base.graph._parentWays[id], function(parentId) {
27144 if (parentId in base.graph.entities) {
27145 baseEntities[parentId] = base.graph.entities[parentId];
27152 if (modified.length) x.modified = modified;
27153 if (deleted.length) x.deleted = deleted;
27154 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
27155 if (i.annotation) x.annotation = i.annotation;
27160 return JSON.stringify({
27162 entities: _.values(allEntities),
27163 baseEntities: _.values(baseEntities),
27165 nextIDs: iD.Entity.id.next,
27170 fromJSON: function(json, loadChildNodes) {
27171 var h = JSON.parse(json),
27172 loadComplete = true;
27174 iD.Entity.id.next = h.nextIDs;
27177 if (h.version === 2 || h.version === 3) {
27178 var allEntities = {};
27180 h.entities.forEach(function(entity) {
27181 allEntities[iD.Entity.key(entity)] = iD.Entity(entity);
27184 if (h.version === 3) {
27185 // This merges originals for changed entities into the base of
27186 // the stack even if the current stack doesn't have them (for
27187 // example when iD has been restarted in a different region)
27188 var baseEntities = h.baseEntities.map(function(d) { return iD.Entity(d); });
27189 stack[0].graph.rebase(baseEntities, _.pluck(stack, 'graph'), true);
27190 tree.rebase(baseEntities, true);
27192 // When we restore a modified way, we also need to fetch any missing
27193 // childnodes that would normally have been downloaded with it.. #2142
27194 if (loadChildNodes) {
27195 var missing = _(baseEntities)
27196 .filter('type', 'way')
27200 .reject(function(n) { return stack[0].graph.hasEntity(n); })
27203 if (!_.isEmpty(missing)) {
27204 loadComplete = false;
27205 context.redrawEnable(false);
27207 var loading = iD.ui.Loading(context).blocking(true);
27208 context.container().call(loading);
27210 var childNodesLoaded = function(err, result) {
27212 var visible = _.groupBy(result.data, 'visible');
27213 if (!_.isEmpty(visible.true)) {
27214 missing = _.difference(missing, _.pluck(visible.true, 'id'));
27215 stack[0].graph.rebase(visible.true, _.pluck(stack, 'graph'), true);
27216 tree.rebase(visible.true, true);
27219 // fetch older versions of nodes that were deleted..
27220 _.each(visible.false, function(entity) {
27221 context.connection()
27222 .loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
27226 if (err || _.isEmpty(missing)) {
27228 context.redrawEnable(true);
27233 context.connection().loadMultiple(missing, childNodesLoaded);
27238 stack = h.stack.map(function(d) {
27239 var entities = {}, entity;
27242 d.modified.forEach(function(key) {
27243 entity = allEntities[key];
27244 entities[entity.id] = entity;
27249 d.deleted.forEach(function(id) {
27250 entities[id] = undefined;
27255 graph: iD.Graph(stack[0].graph).load(entities),
27256 annotation: d.annotation,
27257 imageryUsed: d.imageryUsed
27261 } else { // original version
27262 stack = h.stack.map(function(d) {
27265 for (var i in d.entities) {
27266 var entity = d.entities[i];
27267 entities[i] = entity === 'undefined' ? undefined : iD.Entity(entity);
27270 d.graph = iD.Graph(stack[0].graph).load(entities);
27275 if (loadComplete) {
27283 if (lock.locked()) context.storage(getKey('saved_history'), history.toJSON() || null);
27287 clearSaved: function() {
27288 context.debouncedSave.cancel();
27289 if (lock.locked()) context.storage(getKey('saved_history'), null);
27294 return lock.lock();
27297 unlock: function() {
27301 // is iD not open in another window and it detects that
27302 // there's a history stored in localStorage that's recoverable?
27303 restorableChanges: function() {
27304 return lock.locked() && !!context.storage(getKey('saved_history'));
27307 // load history from a version stored in localStorage
27308 restore: function() {
27309 if (!lock.locked()) return;
27311 var json = context.storage(getKey('saved_history'));
27312 if (json) history.fromJSON(json, true);
27321 return d3.rebind(history, dispatch, 'on');
27323 iD.Node = iD.Entity.node = function iD_Node() {
27324 if (!(this instanceof iD_Node)) {
27325 return (new iD_Node()).initialize(arguments);
27326 } else if (arguments.length) {
27327 this.initialize(arguments);
27331 iD.Node.prototype = Object.create(iD.Entity.prototype);
27333 _.extend(iD.Node.prototype, {
27336 extent: function() {
27337 return new iD.geo.Extent(this.loc);
27340 geometry: function(graph) {
27341 return graph.transient(this, 'geometry', function() {
27342 return graph.isPoi(this) ? 'point' : 'vertex';
27346 move: function(loc) {
27347 return this.update({loc: loc});
27350 isIntersection: function(resolver) {
27351 return resolver.transient(this, 'isIntersection', function() {
27352 return resolver.parentWays(this).filter(function(parent) {
27353 return (parent.tags.highway ||
27354 parent.tags.waterway ||
27355 parent.tags.railway ||
27356 parent.tags.aeroway) &&
27357 parent.geometry(resolver) === 'line';
27362 isHighwayIntersection: function(resolver) {
27363 return resolver.transient(this, 'isHighwayIntersection', function() {
27364 return resolver.parentWays(this).filter(function(parent) {
27365 return parent.tags.highway && parent.geometry(resolver) === 'line';
27370 asJXON: function(changeset_id) {
27373 '@id': this.osmId(),
27374 '@lon': this.loc[0],
27375 '@lat': this.loc[1],
27376 '@version': (this.version || 0),
27377 tag: _.map(this.tags, function(v, k) {
27378 return { keyAttributes: { k: k, v: v } };
27382 if (changeset_id) r.node['@changeset'] = changeset_id;
27386 asGeoJSON: function() {
27389 coordinates: this.loc
27393 iD.Relation = iD.Entity.relation = function iD_Relation() {
27394 if (!(this instanceof iD_Relation)) {
27395 return (new iD_Relation()).initialize(arguments);
27396 } else if (arguments.length) {
27397 this.initialize(arguments);
27401 iD.Relation.prototype = Object.create(iD.Entity.prototype);
27403 iD.Relation.creationOrder = function(a, b) {
27404 var aId = parseInt(iD.Entity.id.toOSM(a.id), 10);
27405 var bId = parseInt(iD.Entity.id.toOSM(b.id), 10);
27407 if (aId < 0 || bId < 0) return aId - bId;
27411 _.extend(iD.Relation.prototype, {
27415 copy: function(deep, resolver, replacements) {
27416 var copy = iD.Entity.prototype.copy.call(this);
27417 if (!deep || !resolver || !this.isComplete(resolver)) {
27422 i, oldmember, oldid, newid, children;
27424 replacements = replacements || {};
27425 replacements[this.id] = copy[0].id;
27427 for (i = 0; i < this.members.length; i++) {
27428 oldmember = this.members[i];
27429 oldid = oldmember.id;
27430 newid = replacements[oldid];
27432 children = resolver.entity(oldid).copy(true, resolver, replacements);
27433 newid = replacements[oldid] = children[0].id;
27434 copy = copy.concat(children);
27436 members.push({id: newid, type: oldmember.type, role: oldmember.role});
27439 copy[0] = copy[0].update({members: members});
27443 extent: function(resolver, memo) {
27444 return resolver.transient(this, 'extent', function() {
27445 if (memo && memo[this.id]) return iD.geo.Extent();
27447 memo[this.id] = true;
27449 var extent = iD.geo.Extent();
27450 for (var i = 0; i < this.members.length; i++) {
27451 var member = resolver.hasEntity(this.members[i].id);
27453 extent._extend(member.extent(resolver, memo));
27460 geometry: function(graph) {
27461 return graph.transient(this, 'geometry', function() {
27462 return this.isMultipolygon() ? 'area' : 'relation';
27466 isDegenerate: function() {
27467 return this.members.length === 0;
27470 // Return an array of members, each extended with an 'index' property whose value
27471 // is the member index.
27472 indexedMembers: function() {
27473 var result = new Array(this.members.length);
27474 for (var i = 0; i < this.members.length; i++) {
27475 result[i] = _.extend({}, this.members[i], {index: i});
27480 // Return the first member with the given role. A copy of the member object
27481 // is returned, extended with an 'index' property whose value is the member index.
27482 memberByRole: function(role) {
27483 for (var i = 0; i < this.members.length; i++) {
27484 if (this.members[i].role === role) {
27485 return _.extend({}, this.members[i], {index: i});
27490 // Return the first member with the given id. A copy of the member object
27491 // is returned, extended with an 'index' property whose value is the member index.
27492 memberById: function(id) {
27493 for (var i = 0; i < this.members.length; i++) {
27494 if (this.members[i].id === id) {
27495 return _.extend({}, this.members[i], {index: i});
27500 // Return the first member with the given id and role. A copy of the member object
27501 // is returned, extended with an 'index' property whose value is the member index.
27502 memberByIdAndRole: function(id, role) {
27503 for (var i = 0; i < this.members.length; i++) {
27504 if (this.members[i].id === id && this.members[i].role === role) {
27505 return _.extend({}, this.members[i], {index: i});
27510 addMember: function(member, index) {
27511 var members = this.members.slice();
27512 members.splice(index === undefined ? members.length : index, 0, member);
27513 return this.update({members: members});
27516 updateMember: function(member, index) {
27517 var members = this.members.slice();
27518 members.splice(index, 1, _.extend({}, members[index], member));
27519 return this.update({members: members});
27522 removeMember: function(index) {
27523 var members = this.members.slice();
27524 members.splice(index, 1);
27525 return this.update({members: members});
27528 removeMembersWithID: function(id) {
27529 var members = _.reject(this.members, function(m) { return m.id === id; });
27530 return this.update({members: members});
27533 // Wherever a member appears with id `needle.id`, replace it with a member
27534 // with id `replacement.id`, type `replacement.type`, and the original role,
27535 // unless a member already exists with that id and role. Return an updated
27537 replaceMember: function(needle, replacement) {
27538 if (!this.memberById(needle.id))
27543 for (var i = 0; i < this.members.length; i++) {
27544 var member = this.members[i];
27545 if (member.id !== needle.id) {
27546 members.push(member);
27547 } else if (!this.memberByIdAndRole(replacement.id, member.role)) {
27548 members.push({id: replacement.id, type: replacement.type, role: member.role});
27552 return this.update({members: members});
27555 asJXON: function(changeset_id) {
27558 '@id': this.osmId(),
27559 '@version': this.version || 0,
27560 member: _.map(this.members, function(member) {
27561 return { keyAttributes: { type: member.type, role: member.role, ref: iD.Entity.id.toOSM(member.id) } };
27563 tag: _.map(this.tags, function(v, k) {
27564 return { keyAttributes: { k: k, v: v } };
27568 if (changeset_id) r.relation['@changeset'] = changeset_id;
27572 asGeoJSON: function(resolver) {
27573 return resolver.transient(this, 'GeoJSON', function () {
27574 if (this.isMultipolygon()) {
27576 type: 'MultiPolygon',
27577 coordinates: this.multipolygon(resolver)
27581 type: 'FeatureCollection',
27582 properties: this.tags,
27583 features: this.members.map(function (member) {
27584 return _.extend({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
27591 area: function(resolver) {
27592 return resolver.transient(this, 'area', function() {
27593 return d3.geo.area(this.asGeoJSON(resolver));
27597 isMultipolygon: function() {
27598 return this.tags.type === 'multipolygon';
27601 isComplete: function(resolver) {
27602 for (var i = 0; i < this.members.length; i++) {
27603 if (!resolver.hasEntity(this.members[i].id)) {
27610 isRestriction: function() {
27611 return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
27614 // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
27615 // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
27617 // This corresponds to the structure needed for rendering a multipolygon path using a
27618 // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
27620 // In the case of invalid geometries, this function will still return a result which
27621 // includes the nodes of all way members, but some Nds may be unclosed and some inner
27622 // rings not matched with the intended outer ring.
27624 multipolygon: function(resolver) {
27625 var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
27626 inners = this.members.filter(function(m) { return 'inner' === m.role; });
27628 outers = iD.geo.joinWays(outers, resolver);
27629 inners = iD.geo.joinWays(inners, resolver);
27631 outers = outers.map(function(outer) { return _.pluck(outer.nodes, 'loc'); });
27632 inners = inners.map(function(inner) { return _.pluck(inner.nodes, 'loc'); });
27634 var result = outers.map(function(o) {
27635 // Heuristic for detecting counterclockwise winding order. Assumes
27636 // that OpenStreetMap polygons are not hemisphere-spanning.
27637 return [d3.geo.area({type: 'Polygon', coordinates: [o]}) > 2 * Math.PI ? o.reverse() : o];
27640 function findOuter(inner) {
27643 for (o = 0; o < outers.length; o++) {
27645 if (iD.geo.polygonContainsPolygon(outer, inner))
27649 for (o = 0; o < outers.length; o++) {
27651 if (iD.geo.polygonIntersectsPolygon(outer, inner))
27656 for (var i = 0; i < inners.length; i++) {
27657 var inner = inners[i];
27659 if (d3.geo.area({type: 'Polygon', coordinates: [inner]}) < 2 * Math.PI) {
27660 inner = inner.reverse();
27663 var o = findOuter(inners[i]);
27664 if (o !== undefined)
27665 result[o].push(inners[i]);
27667 result.push([inners[i]]); // Invalid geometry
27675 'chair_lift': true,
27676 'mixed_lift': true,
27681 'magic_carpet': true,
27686 'motorway_link': true
27692 'piste:halfpipe': true
27716 iD.interestingTag = function (key) {
27717 return key !== 'attribution' &&
27718 key !== 'created_by' &&
27719 key !== 'source' &&
27721 key.indexOf('tiger:') !== 0;
27724 iD.Tree = function(head) {
27725 var rtree = rbush(),
27728 function extentRectangle(extent) {
27737 function entityRectangle(entity) {
27738 var rect = extentRectangle(entity.extent(head));
27739 rect.id = entity.id;
27740 rectangles[entity.id] = rect;
27744 function updateParents(entity, insertions, memo) {
27745 head.parentWays(entity).forEach(function(parent) {
27746 if (rectangles[parent.id]) {
27747 rtree.remove(rectangles[parent.id]);
27748 insertions[parent.id] = parent;
27752 head.parentRelations(entity).forEach(function(parent) {
27753 if (memo[entity.id]) return;
27754 memo[entity.id] = true;
27755 if (rectangles[parent.id]) {
27756 rtree.remove(rectangles[parent.id]);
27757 insertions[parent.id] = parent;
27759 updateParents(parent, insertions, memo);
27765 tree.rebase = function(entities, force) {
27766 var insertions = {};
27768 for (var i = 0; i < entities.length; i++) {
27769 var entity = entities[i];
27771 if (!entity.visible)
27774 if (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id]) {
27777 } else if (rectangles[entity.id]) {
27778 rtree.remove(rectangles[entity.id]);
27782 insertions[entity.id] = entity;
27783 updateParents(entity, insertions, {});
27786 rtree.load(_.map(insertions, entityRectangle));
27791 tree.intersects = function(extent, graph) {
27792 if (graph !== head) {
27793 var diff = iD.Difference(head, graph),
27798 diff.deleted().forEach(function(entity) {
27799 rtree.remove(rectangles[entity.id]);
27800 delete rectangles[entity.id];
27803 diff.modified().forEach(function(entity) {
27804 rtree.remove(rectangles[entity.id]);
27805 insertions[entity.id] = entity;
27806 updateParents(entity, insertions, {});
27809 diff.created().forEach(function(entity) {
27810 insertions[entity.id] = entity;
27813 rtree.load(_.map(insertions, entityRectangle));
27816 return rtree.search(extentRectangle(extent)).map(function(rect) {
27817 return head.entity(rect.id);
27823 iD.Way = iD.Entity.way = function iD_Way() {
27824 if (!(this instanceof iD_Way)) {
27825 return (new iD_Way()).initialize(arguments);
27826 } else if (arguments.length) {
27827 this.initialize(arguments);
27831 iD.Way.prototype = Object.create(iD.Entity.prototype);
27833 _.extend(iD.Way.prototype, {
27837 copy: function(deep, resolver) {
27838 var copy = iD.Entity.prototype.copy.call(this);
27840 if (!deep || !resolver) {
27846 i, oldid, newid, child;
27848 for (i = 0; i < this.nodes.length; i++) {
27849 oldid = this.nodes[i];
27850 newid = replacements[oldid];
27852 child = resolver.entity(oldid).copy();
27853 newid = replacements[oldid] = child[0].id;
27854 copy = copy.concat(child);
27859 copy[0] = copy[0].update({nodes: nodes});
27863 extent: function(resolver) {
27864 return resolver.transient(this, 'extent', function() {
27865 var extent = iD.geo.Extent();
27866 for (var i = 0; i < this.nodes.length; i++) {
27867 var node = resolver.hasEntity(this.nodes[i]);
27869 extent._extend(node.extent());
27876 first: function() {
27877 return this.nodes[0];
27881 return this.nodes[this.nodes.length - 1];
27884 contains: function(node) {
27885 return this.nodes.indexOf(node) >= 0;
27888 affix: function(node) {
27889 if (this.nodes[0] === node) return 'prefix';
27890 if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
27893 layer: function() {
27894 // explicit layer tag, clamp between -10, 10..
27895 if (this.tags.layer !== undefined) {
27896 return Math.max(-10, Math.min(+(this.tags.layer), 10));
27899 // implied layer tag..
27900 if (this.tags.location === 'overground') return 1;
27901 if (this.tags.location === 'underground') return -1;
27902 if (this.tags.location === 'underwater') return -10;
27904 if (this.tags.power === 'line') return 10;
27905 if (this.tags.power === 'minor_line') return 10;
27906 if (this.tags.aerialway) return 10;
27907 if (this.tags.bridge) return 1;
27908 if (this.tags.cutting) return -1;
27909 if (this.tags.tunnel) return -1;
27910 if (this.tags.waterway) return -1;
27911 if (this.tags.man_made === 'pipeline') return -10;
27912 if (this.tags.boundary) return -10;
27916 isOneWay: function() {
27917 // explicit oneway tag..
27918 if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; }
27919 if (['no', '0'].indexOf(this.tags.oneway) !== -1) { return false; }
27921 // implied oneway tag..
27922 for (var key in this.tags) {
27923 if (key in iD.oneWayTags && (this.tags[key] in iD.oneWayTags[key]))
27929 isClosed: function() {
27930 return this.nodes.length > 0 && this.first() === this.last();
27933 isConvex: function(resolver) {
27934 if (!this.isClosed() || this.isDegenerate()) return null;
27936 var nodes = _.uniq(resolver.childNodes(this)),
27937 coords = _.pluck(nodes, 'loc'),
27938 curr = 0, prev = 0;
27940 for (var i = 0; i < coords.length; i++) {
27941 var o = coords[(i+1) % coords.length],
27943 b = coords[(i+2) % coords.length],
27944 res = iD.geo.cross(o, a, b);
27946 curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
27949 } else if (prev && curr !== prev) {
27957 isArea: function() {
27958 if (this.tags.area === 'yes')
27960 if (!this.isClosed() || this.tags.area === 'no')
27962 for (var key in this.tags)
27963 if (key in iD.areaKeys && !(this.tags[key] in iD.areaKeys[key]))
27968 isDegenerate: function() {
27969 return _.uniq(this.nodes).length < (this.isArea() ? 3 : 2);
27972 areAdjacent: function(n1, n2) {
27973 for (var i = 0; i < this.nodes.length; i++) {
27974 if (this.nodes[i] === n1) {
27975 if (this.nodes[i - 1] === n2) return true;
27976 if (this.nodes[i + 1] === n2) return true;
27982 geometry: function(graph) {
27983 return graph.transient(this, 'geometry', function() {
27984 return this.isArea() ? 'area' : 'line';
27988 addNode: function(id, index) {
27989 var nodes = this.nodes.slice();
27990 nodes.splice(index === undefined ? nodes.length : index, 0, id);
27991 return this.update({nodes: nodes});
27994 updateNode: function(id, index) {
27995 var nodes = this.nodes.slice();
27996 nodes.splice(index, 1, id);
27997 return this.update({nodes: nodes});
28000 replaceNode: function(needle, replacement) {
28001 if (this.nodes.indexOf(needle) < 0)
28004 var nodes = this.nodes.slice();
28005 for (var i = 0; i < nodes.length; i++) {
28006 if (nodes[i] === needle) {
28007 nodes[i] = replacement;
28010 return this.update({nodes: nodes});
28013 removeNode: function(id) {
28016 for (var i = 0; i < this.nodes.length; i++) {
28017 var node = this.nodes[i];
28018 if (node !== id && nodes[nodes.length - 1] !== node) {
28023 // Preserve circularity
28024 if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] !== nodes[0]) {
28025 nodes.push(nodes[0]);
28028 return this.update({nodes: nodes});
28031 asJXON: function(changeset_id) {
28034 '@id': this.osmId(),
28035 '@version': this.version || 0,
28036 nd: _.map(this.nodes, function(id) {
28037 return { keyAttributes: { ref: iD.Entity.id.toOSM(id) } };
28039 tag: _.map(this.tags, function(v, k) {
28040 return { keyAttributes: { k: k, v: v } };
28044 if (changeset_id) r.way['@changeset'] = changeset_id;
28048 asGeoJSON: function(resolver) {
28049 return resolver.transient(this, 'GeoJSON', function() {
28050 var coordinates = _.pluck(resolver.childNodes(this), 'loc');
28051 if (this.isArea() && this.isClosed()) {
28054 coordinates: [coordinates]
28058 type: 'LineString',
28059 coordinates: coordinates
28065 area: function(resolver) {
28066 return resolver.transient(this, 'area', function() {
28067 var nodes = resolver.childNodes(this);
28071 coordinates: [_.pluck(nodes, 'loc')]
28074 if (!this.isClosed() && nodes.length) {
28075 json.coordinates[0].push(nodes[0].loc);
28078 var area = d3.geo.area(json);
28080 // Heuristic for detecting counterclockwise winding order. Assumes
28081 // that OpenStreetMap polygons are not hemisphere-spanning.
28082 if (area > 2 * Math.PI) {
28083 json.coordinates[0] = json.coordinates[0].reverse();
28084 area = d3.geo.area(json);
28087 return isNaN(area) ? 0 : area;
28091 iD.Background = function(context) {
28092 var dispatch = d3.dispatch('change'),
28093 baseLayer = iD.TileLayer()
28094 .projection(context.projection),
28095 gpxLayer = iD.GpxLayer(context, dispatch)
28096 .projection(context.projection),
28097 mapillaryLayer = iD.MapillaryLayer(context),
28098 overlayLayers = [];
28100 var backgroundSources;
28102 function findSource(id) {
28103 return _.find(backgroundSources, function(d) {
28104 return d.id && d.id === id;
28108 function updateImagery() {
28109 var b = background.baseLayerSource(),
28110 o = overlayLayers.map(function (d) { return d.source().id; }).join(','),
28111 q = iD.util.stringQs(location.hash.substring(1));
28114 if (id === 'custom') {
28115 id = 'custom:' + b.template;
28121 delete q.background;
28130 location.replace('#' + iD.util.qsString(q, true));
28132 var imageryUsed = [b.imageryUsed()];
28134 overlayLayers.forEach(function (d) {
28135 var source = d.source();
28136 if (!source.isLocatorOverlay()) {
28137 imageryUsed.push(source.imageryUsed());
28141 if (background.showsGpxLayer()) {
28142 imageryUsed.push('Local GPX');
28145 context.history().imageryUsed(imageryUsed);
28148 function background(selection) {
28149 var base = selection.selectAll('.background-layer')
28152 base.enter().insert('div', '.layer-data')
28153 .attr('class', 'layer-layer background-layer');
28155 base.call(baseLayer);
28157 var overlays = selection.selectAll('.layer-overlay')
28158 .data(overlayLayers, function(d) { return d.source().name(); });
28160 overlays.enter().insert('div', '.layer-data')
28161 .attr('class', 'layer-layer layer-overlay');
28163 overlays.each(function(layer) {
28164 d3.select(this).call(layer);
28170 var gpx = selection.selectAll('.layer-gpx')
28173 gpx.enter().insert('div')
28174 .attr('class', 'layer-layer layer-gpx');
28176 gpx.call(gpxLayer);
28178 var mapillary = selection.selectAll('.layer-mapillary')
28181 mapillary.enter().insert('div')
28182 .attr('class', 'layer-layer layer-mapillary');
28184 mapillary.call(mapillaryLayer);
28187 background.sources = function(extent) {
28188 return backgroundSources.filter(function(source) {
28189 return source.intersects(extent);
28193 background.dimensions = function(_) {
28194 baseLayer.dimensions(_);
28195 gpxLayer.dimensions(_);
28196 mapillaryLayer.dimensions(_);
28198 overlayLayers.forEach(function(layer) {
28199 layer.dimensions(_);
28203 background.baseLayerSource = function(d) {
28204 if (!arguments.length) return baseLayer.source();
28206 baseLayer.source(d);
28213 background.bing = function() {
28214 background.baseLayerSource(findSource('Bing'));
28217 background.hasGpxLayer = function() {
28218 return !_.isEmpty(gpxLayer.geojson());
28221 background.showsGpxLayer = function() {
28222 return background.hasGpxLayer() && gpxLayer.enable();
28225 function toDom(x) {
28226 return (new DOMParser()).parseFromString(x, 'text/xml');
28229 background.gpxLayerFiles = function(fileList) {
28230 var f = fileList[0],
28231 reader = new FileReader();
28233 reader.onload = function(e) {
28234 gpxLayer.geojson(toGeoJSON.gpx(toDom(e.target.result)));
28235 iD.ui.MapInMap.gpxLayer.geojson(toGeoJSON.gpx(toDom(e.target.result)));
28236 background.zoomToGpxLayer();
28240 reader.readAsText(f);
28243 background.zoomToGpxLayer = function() {
28244 if (background.hasGpxLayer()) {
28245 var map = context.map(),
28246 viewport = map.trimmedExtent().polygon(),
28247 coords = _.reduce(gpxLayer.geojson().features, function(coords, feature) {
28248 var c = feature.geometry.coordinates;
28249 return _.union(coords, feature.geometry.type === 'Point' ? [c] : c);
28252 if (!iD.geo.polygonIntersectsPolygon(viewport, coords, true)) {
28253 var extent = iD.geo.Extent(d3.geo.bounds(gpxLayer.geojson()));
28254 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
28259 background.toggleGpxLayer = function() {
28260 gpxLayer.enable(!gpxLayer.enable());
28261 iD.ui.MapInMap.gpxLayer.enable(!iD.ui.MapInMap.gpxLayer.enable());
28265 background.showsMapillaryLayer = function() {
28266 return mapillaryLayer.enable();
28269 background.toggleMapillaryLayer = function() {
28270 mapillaryLayer.enable(!mapillaryLayer.enable());
28274 background.showsLayer = function(d) {
28275 return d === baseLayer.source() ||
28276 (d.id === 'custom' && baseLayer.source().id === 'custom') ||
28277 overlayLayers.some(function(l) { return l.source() === d; });
28280 background.overlayLayerSources = function() {
28281 return overlayLayers.map(function (l) { return l.source(); });
28284 background.toggleOverlayLayer = function(d) {
28287 for (var i = 0; i < overlayLayers.length; i++) {
28288 layer = overlayLayers[i];
28289 if (layer.source() === d) {
28290 overlayLayers.splice(i, 1);
28297 layer = iD.TileLayer()
28299 .projection(context.projection)
28300 .dimensions(baseLayer.dimensions());
28302 overlayLayers.push(layer);
28307 background.nudge = function(d, zoom) {
28308 baseLayer.source().nudge(d, zoom);
28313 background.offset = function(d) {
28314 if (!arguments.length) return baseLayer.source().offset();
28315 baseLayer.source().offset(d);
28320 background.load = function(imagery) {
28321 function parseMap(qmap) {
28322 if (!qmap) return false;
28323 var args = qmap.split('/').map(Number);
28324 if (args.length < 3 || args.some(isNaN)) return false;
28325 return iD.geo.Extent([args[1], args[2]]);
28328 var q = iD.util.stringQs(location.hash.substring(1)),
28329 chosen = q.background || q.layer,
28330 extent = parseMap(q.map),
28333 backgroundSources = imagery.map(function(source) {
28334 if (source.type === 'bing') {
28335 return iD.BackgroundSource.Bing(source, dispatch);
28337 return iD.BackgroundSource(source);
28341 backgroundSources.unshift(iD.BackgroundSource.None());
28343 if (!chosen && extent) {
28344 best = _.find(this.sources(extent), function(s) { return s.best(); });
28347 if (chosen && chosen.indexOf('custom:') === 0) {
28348 background.baseLayerSource(iD.BackgroundSource.Custom(chosen.replace(/^custom:/, '')));
28350 background.baseLayerSource(findSource(chosen) || best || findSource('Bing') || backgroundSources[1]);
28353 var locator = _.find(backgroundSources, function(d) {
28354 return d.overlay && d.default;
28358 background.toggleOverlayLayer(locator);
28361 var overlays = (q.overlays || '').split(',');
28362 overlays.forEach(function(overlay) {
28363 overlay = findSource(overlay);
28364 if (overlay) background.toggleOverlayLayer(overlay);
28369 d3.text(gpx, function(err, gpxTxt) {
28371 gpxLayer.geojson(toGeoJSON.gpx(toDom(gpxTxt)));
28372 iD.ui.MapInMap.gpxLayer.geojson(toGeoJSON.gpx(toDom(gpxTxt)));
28379 return d3.rebind(background, dispatch, 'on');
28381 iD.BackgroundSource = function(data) {
28382 var source = _.clone(data),
28384 name = source.name,
28385 best = !!source.best;
28387 source.scaleExtent = data.scaleExtent || [0, 20];
28388 source.overzoom = data.overzoom !== false;
28390 source.offset = function(_) {
28391 if (!arguments.length) return offset;
28396 source.nudge = function(_, zoomlevel) {
28397 offset[0] += _[0] / Math.pow(2, zoomlevel);
28398 offset[1] += _[1] / Math.pow(2, zoomlevel);
28402 source.name = function() {
28406 source.best = function() {
28410 source.imageryUsed = function() {
28411 return source.id || name;
28414 source.url = function(coord) {
28415 return data.template
28416 .replace('{x}', coord[0])
28417 .replace('{y}', coord[1])
28418 // TMS-flipped y coordinate
28419 .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
28420 .replace(/\{z(oom)?\}/, coord[2])
28421 .replace(/\{switch:([^}]+)\}/, function(s, r) {
28422 var subdomains = r.split(',');
28423 return subdomains[(coord[0] + coord[1]) % subdomains.length];
28425 .replace('{u}', function() {
28427 for (var zoom = coord[2]; zoom > 0; zoom--) {
28429 var mask = 1 << (zoom - 1);
28430 if ((coord[0] & mask) !== 0) b++;
28431 if ((coord[1] & mask) !== 0) b += 2;
28438 source.intersects = function(extent) {
28439 extent = extent.polygon();
28440 return !data.polygon || data.polygon.some(function(polygon) {
28441 return iD.geo.polygonIntersectsPolygon(polygon, extent, true);
28445 source.validZoom = function(z) {
28446 return source.scaleExtent[0] <= z &&
28447 (source.overzoom || source.scaleExtent[1] > z);
28450 source.isLocatorOverlay = function() {
28451 return name === 'Locator Overlay';
28454 source.copyrightNotices = function() {};
28459 iD.BackgroundSource.Bing = function(data, dispatch) {
28460 // http://msdn.microsoft.com/en-us/library/ff701716.aspx
28461 // http://msdn.microsoft.com/en-us/library/ff701701.aspx
28463 data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
28465 var bing = iD.BackgroundSource(data),
28466 key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU', // Same as P2 and JOSM
28467 url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' +
28468 key + '&jsonp={callback}',
28471 d3.jsonp(url, function(json) {
28472 providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
28474 attribution: provider.attribution,
28475 areas: provider.coverageAreas.map(function(area) {
28477 zoom: [area.zoomMin, area.zoomMax],
28478 extent: iD.geo.Extent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
28486 bing.copyrightNotices = function(zoom, extent) {
28487 zoom = Math.min(zoom, 21);
28488 return providers.filter(function(provider) {
28489 return _.any(provider.areas, function(area) {
28490 return extent.intersects(area.extent) &&
28491 area.zoom[0] <= zoom &&
28492 area.zoom[1] >= zoom;
28494 }).map(function(provider) {
28495 return provider.attribution;
28499 bing.logo = 'bing_maps.png';
28500 bing.terms_url = 'http://opengeodata.org/microsoft-imagery-details';
28505 iD.BackgroundSource.None = function() {
28506 var source = iD.BackgroundSource({id: 'none', template: ''});
28508 source.name = function() {
28509 return t('background.none');
28512 source.imageryUsed = function() {
28519 iD.BackgroundSource.Custom = function(template) {
28520 var source = iD.BackgroundSource({id: 'custom', template: template});
28522 source.name = function() {
28523 return t('background.custom');
28526 source.imageryUsed = function() {
28527 return 'Custom (' + template + ')';
28532 iD.Features = function(context) {
28533 var major_roads = {
28535 'motorway_link': true,
28537 'trunk_link': true,
28539 'primary_link': true,
28541 'secondary_link': true,
28543 'tertiary_link': true,
28544 'residential': true
28547 var minor_roads = {
28549 'living_street': true,
28551 'unclassified': true,
28561 'pedestrian': true,
28565 var past_futures = {
28567 'construction': true,
28569 'dismantled': true,
28572 'demolished': true,
28573 'obliterated': true
28576 var dispatch = d3.dispatch('change', 'redraw'),
28584 function update() {
28585 _hidden = features.hidden();
28590 function defineFeature(k, filter, max) {
28594 enabled: true, // whether the user wants it enabled..
28596 currentMax: (max || Infinity),
28597 defaultMax: (max || Infinity),
28598 enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
28599 disable: function() { this.enabled = false; this.currentMax = 0; },
28600 hidden: function() { return !context.editable() || this.count > this.currentMax * _cullFactor; },
28601 autoHidden: function() { return this.hidden() && this.currentMax > 0; }
28606 defineFeature('points', function isPoint(entity, resolver, geometry) {
28607 return geometry === 'point';
28610 defineFeature('major_roads', function isMajorRoad(entity) {
28611 return major_roads[entity.tags.highway];
28614 defineFeature('minor_roads', function isMinorRoad(entity) {
28615 return minor_roads[entity.tags.highway];
28618 defineFeature('paths', function isPath(entity) {
28619 return paths[entity.tags.highway];
28622 defineFeature('buildings', function isBuilding(entity) {
28624 !!entity.tags['building:part'] ||
28625 (!!entity.tags.building && entity.tags.building !== 'no') ||
28626 entity.tags.amenity === 'shelter' ||
28627 entity.tags.parking === 'multi-storey' ||
28628 entity.tags.parking === 'sheds' ||
28629 entity.tags.parking === 'carports' ||
28630 entity.tags.parking === 'garage_boxes'
28634 defineFeature('landuse', function isLanduse(entity, resolver, geometry) {
28635 return geometry === 'area' &&
28636 !_features.buildings.filter(entity) &&
28637 !_features.water.filter(entity);
28640 defineFeature('boundaries', function isBoundary(entity) {
28641 return !!entity.tags.boundary;
28644 defineFeature('water', function isWater(entity) {
28646 !!entity.tags.waterway ||
28647 entity.tags.natural === 'water' ||
28648 entity.tags.natural === 'coastline' ||
28649 entity.tags.natural === 'bay' ||
28650 entity.tags.landuse === 'pond' ||
28651 entity.tags.landuse === 'basin' ||
28652 entity.tags.landuse === 'reservoir' ||
28653 entity.tags.landuse === 'salt_pond'
28657 defineFeature('rail', function isRail(entity) {
28659 !!entity.tags.railway ||
28660 entity.tags.landuse === 'railway'
28662 major_roads[entity.tags.highway] ||
28663 minor_roads[entity.tags.highway] ||
28664 paths[entity.tags.highway]
28668 defineFeature('power', function isPower(entity) {
28669 return !!entity.tags.power;
28672 // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
28673 defineFeature('past_future', function isPastFuture(entity) {
28675 major_roads[entity.tags.highway] ||
28676 minor_roads[entity.tags.highway] ||
28677 paths[entity.tags.highway]
28678 ) { return false; }
28680 var strings = Object.keys(entity.tags);
28682 for (var i = 0; i < strings.length; i++) {
28683 var s = strings[i];
28684 if (past_futures[s] || past_futures[entity.tags[s]]) { return true; }
28689 // Lines or areas that don't match another feature filter.
28690 // IMPORTANT: The 'others' feature must be the last one defined,
28691 // so that code in getMatches can skip this test if `hasMatch = true`
28692 defineFeature('others', function isOther(entity, resolver, geometry) {
28693 return (geometry === 'line' || geometry === 'area');
28697 function features() {}
28699 features.features = function() {
28703 features.keys = function() {
28707 features.enabled = function(k) {
28708 if (!arguments.length) {
28709 return _.filter(_keys, function(k) { return _features[k].enabled; });
28711 return _features[k] && _features[k].enabled;
28714 features.disabled = function(k) {
28715 if (!arguments.length) {
28716 return _.reject(_keys, function(k) { return _features[k].enabled; });
28718 return _features[k] && !_features[k].enabled;
28721 features.hidden = function(k) {
28722 if (!arguments.length) {
28723 return _.filter(_keys, function(k) { return _features[k].hidden(); });
28725 return _features[k] && _features[k].hidden();
28728 features.autoHidden = function(k) {
28729 if (!arguments.length) {
28730 return _.filter(_keys, function(k) { return _features[k].autoHidden(); });
28732 return _features[k] && _features[k].autoHidden();
28735 features.enable = function(k) {
28736 if (_features[k] && !_features[k].enabled) {
28737 _features[k].enable();
28742 features.disable = function(k) {
28743 if (_features[k] && _features[k].enabled) {
28744 _features[k].disable();
28749 features.toggle = function(k) {
28750 if (_features[k]) {
28751 (function(f) { return f.enabled ? f.disable() : f.enable(); }(_features[k]));
28756 features.resetStats = function() {
28757 _.each(_features, function(f) { f.count = 0; });
28761 features.gatherStats = function(d, resolver, dimensions) {
28762 var needsRedraw = false,
28763 type = _.groupBy(d, function(ent) { return ent.type; }),
28764 entities = [].concat(type.relation || [], type.way || [], type.node || []),
28765 currHidden, geometry, matches;
28767 _.each(_features, function(f) { f.count = 0; });
28769 // adjust the threshold for point/building culling based on viewport size..
28770 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
28771 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
28773 for (var i = 0; i < entities.length; i++) {
28774 geometry = entities[i].geometry(resolver);
28775 if (!(geometry === 'vertex' || geometry === 'relation')) {
28776 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
28777 for (var j = 0; j < matches.length; j++) {
28778 _features[matches[j]].count++;
28783 currHidden = features.hidden();
28784 if (currHidden !== _hidden) {
28785 _hidden = currHidden;
28786 needsRedraw = true;
28790 return needsRedraw;
28793 features.stats = function() {
28794 _.each(_keys, function(k) { _stats[k] = _features[k].count; });
28798 features.clear = function(d) {
28799 for (var i = 0; i < d.length; i++) {
28800 features.clearEntity(d[i]);
28804 features.clearEntity = function(entity) {
28805 delete _cache[iD.Entity.key(entity)];
28808 features.reset = function() {
28812 features.getMatches = function(entity, resolver, geometry) {
28813 if (geometry === 'vertex' || geometry === 'relation') return {};
28815 var ent = iD.Entity.key(entity);
28816 if (!_cache[ent]) {
28820 if (!_cache[ent].matches) {
28824 for (var i = 0; i < _keys.length; i++) {
28825 if (_keys[i] === 'others') {
28826 if (hasMatch) continue;
28828 // Multipolygon members:
28830 // 1. is a way that hasn't matched other "interesting" feature rules,
28831 // 2. and it belongs to a single parent multipolygon relation
28832 // ...then match whatever feature rules the parent multipolygon has matched.
28833 // see #2548, #2887
28836 // For this to work, getMatches must be called on relations before ways.
28838 if (entity.type === 'way') {
28839 var parents = features.getParents(entity, resolver, geometry);
28840 if (parents.length === 1 && parents[0].isMultipolygon()) {
28841 var pkey = iD.Entity.key(parents[0]);
28842 if (_cache[pkey] && _cache[pkey].matches) {
28843 matches = _.clone(_cache[pkey].matches);
28850 if (_features[_keys[i]].filter(entity, resolver, geometry)) {
28851 matches[_keys[i]] = hasMatch = true;
28854 _cache[ent].matches = matches;
28857 return _cache[ent].matches;
28860 features.getParents = function(entity, resolver, geometry) {
28861 if (geometry === 'point') return [];
28863 var ent = iD.Entity.key(entity);
28864 if (!_cache[ent]) {
28868 if (!_cache[ent].parents) {
28870 if (geometry === 'vertex') {
28871 parents = resolver.parentWays(entity);
28872 } else { // 'line', 'area', 'relation'
28873 parents = resolver.parentRelations(entity);
28875 _cache[ent].parents = parents;
28877 return _cache[ent].parents;
28880 features.isHiddenFeature = function(entity, resolver, geometry) {
28881 if (!_hidden.length) return false;
28882 if (!entity.version) return false;
28884 var matches = features.getMatches(entity, resolver, geometry);
28886 for (var i = 0; i < _hidden.length; i++) {
28887 if (matches[_hidden[i]]) return true;
28892 features.isHiddenChild = function(entity, resolver, geometry) {
28893 if (!_hidden.length) return false;
28894 if (!entity.version || geometry === 'point') return false;
28896 var parents = features.getParents(entity, resolver, geometry);
28897 if (!parents.length) return false;
28899 for (var i = 0; i < parents.length; i++) {
28900 if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
28907 features.hasHiddenConnections = function(entity, resolver) {
28908 if (!_hidden.length) return false;
28909 var childNodes, connections;
28911 if (entity.type === 'midpoint') {
28912 childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
28915 childNodes = entity.nodes ? resolver.childNodes(entity) : [];
28916 connections = features.getParents(entity, resolver, entity.geometry(resolver));
28919 // gather ways connected to child nodes..
28920 connections = _.reduce(childNodes, function(result, e) {
28921 return resolver.isShared(e) ? _.union(result, resolver.parentWays(e)) : result;
28924 return connections.length ? _.any(connections, function(e) {
28925 return features.isHidden(e, resolver, e.geometry(resolver));
28929 features.isHidden = function(entity, resolver, geometry) {
28930 if (!_hidden.length) return false;
28931 if (!entity.version) return false;
28933 var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
28934 return fn(entity, resolver, geometry);
28937 features.filter = function(d, resolver) {
28938 if (!_hidden.length) return d;
28941 for (var i = 0; i < d.length; i++) {
28943 if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
28944 result.push(entity);
28950 return d3.rebind(features, dispatch, 'on');
28952 iD.GpxLayer = function(context) {
28958 function render(selection) {
28959 svg = selection.selectAll('svg')
28965 svg.style('display', enable ? 'block' : 'none');
28974 .attr('class', 'gpx');
28976 var path = d3.geo.path()
28977 .projection(projection);
28982 if (typeof gj.features !== 'undefined') {
28992 .attr('class', 'gpx')
28993 .text(function(d) {
28994 return d.properties.desc || d.properties.name;
28996 .attr('x', function(d) {
28997 var centroid = path.centroid(d);
28998 return centroid[0] + 5;
29000 .attr('y', function(d) {
29001 var centroid = path.centroid(d);
29002 return centroid[1];
29007 render.projection = function(_) {
29008 if (!arguments.length) return projection;
29013 render.enable = function(_) {
29014 if (!arguments.length) return enable;
29019 render.geojson = function(_) {
29020 if (!arguments.length) return gj;
29025 render.dimensions = function(_) {
29026 if (!arguments.length) return svg.dimensions();
29031 render.id = 'layer-gpx';
29034 d3.event.stopPropagation();
29035 d3.event.preventDefault();
29036 d3.event.dataTransfer.dropEffect = 'copy';
29040 .attr('dropzone', 'copy')
29041 .on('drop.localgpx', function() {
29042 d3.event.stopPropagation();
29043 d3.event.preventDefault();
29044 if (!iD.detect().filedrop) return;
29045 context.background().gpxLayerFiles(d3.event.dataTransfer.files);
29047 .on('dragenter.localgpx', over)
29048 .on('dragexit.localgpx', over)
29049 .on('dragover.localgpx', over);
29053 iD.Map = function(context) {
29054 var dimensions = [1, 1],
29055 dispatch = d3.dispatch('move', 'drawn'),
29056 projection = context.projection,
29057 zoom = d3.behavior.zoom()
29058 .translate(projection.translate())
29059 .scale(projection.scale() * 2 * Math.PI)
29060 .scaleExtent([1024, 256 * Math.pow(2, 24)])
29061 .on('zoom', zoomPan),
29062 dblclickEnabled = true,
29063 redrawEnabled = true,
29065 transformed = false,
29067 points = iD.svg.Points(projection, context),
29068 vertices = iD.svg.Vertices(projection, context),
29069 lines = iD.svg.Lines(projection),
29070 areas = iD.svg.Areas(projection),
29071 midpoints = iD.svg.Midpoints(projection, context),
29072 labels = iD.svg.Labels(projection, context),
29073 supersurface, surface,
29077 function map(selection) {
29079 .on('change.map', redraw);
29080 context.background()
29081 .on('change.map', redraw);
29083 .on('redraw.map', redraw);
29086 .on('dblclick.map', dblClick)
29089 supersurface = selection.append('div')
29090 .attr('id', 'supersurface');
29092 // Need a wrapper div because Opera can't cope with an absolutely positioned
29093 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
29094 var dataLayer = supersurface.append('div')
29095 .attr('class', 'layer-layer layer-data');
29097 map.surface = surface = dataLayer.append('svg')
29098 .on('mousedown.zoom', function() {
29099 if (d3.event.button === 2) {
29100 d3.event.stopPropagation();
29103 .on('mouseup.zoom', function() {
29104 if (resetTransform()) redraw();
29106 .attr('id', 'surface')
29107 .call(iD.svg.Surface(context));
29109 supersurface.call(context.background());
29111 surface.on('mousemove.map', function() {
29112 mousemove = d3.event;
29115 surface.on('mouseover.vertices', function() {
29116 if (map.editable() && !transformed) {
29117 var hover = d3.event.target.__data__;
29118 surface.call(vertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29119 dispatch.drawn({full: false});
29123 surface.on('mouseout.vertices', function() {
29124 if (map.editable() && !transformed) {
29125 var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__;
29126 surface.call(vertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29127 dispatch.drawn({full: false});
29131 context.on('enter.map', function() {
29132 if (map.editable() && !transformed) {
29133 var all = context.intersects(map.extent()),
29134 filter = d3.functor(true),
29135 graph = context.graph();
29137 all = context.features().filter(all, graph);
29138 surface.call(vertices, graph, all, filter, map.extent(), map.zoom());
29139 surface.call(midpoints, graph, all, filter, map.trimmedExtent());
29140 dispatch.drawn({full: false});
29144 map.dimensions(selection.dimensions());
29146 labels.supersurface(supersurface);
29149 function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }
29151 function drawVector(difference, extent) {
29152 var graph = context.graph(),
29153 features = context.features(),
29154 all = context.intersects(map.extent()),
29158 var complete = difference.complete(map.extent());
29159 data = _.compact(_.values(complete));
29160 filter = function(d) { return d.id in complete; };
29161 features.clear(data);
29164 // force a full redraw if gatherStats detects that a feature
29165 // should be auto-hidden (e.g. points or buildings)..
29166 if (features.gatherStats(all, graph, dimensions)) {
29167 extent = undefined;
29171 data = context.intersects(map.extent().intersection(extent));
29172 var set = d3.set(_.pluck(data, 'id'));
29173 filter = function(d) { return set.has(d.id); };
29177 filter = d3.functor(true);
29181 data = features.filter(data, graph);
29184 .call(vertices, graph, data, filter, map.extent(), map.zoom())
29185 .call(lines, graph, data, filter)
29186 .call(areas, graph, data, filter)
29187 .call(midpoints, graph, data, filter, map.trimmedExtent())
29188 .call(labels, graph, data, filter, dimensions, !difference && !extent)
29189 .call(points, data, filter);
29191 dispatch.drawn({full: true});
29194 function editOff() {
29195 context.features().resetStats();
29196 surface.selectAll('.layer *').remove();
29197 dispatch.drawn({full: true});
29200 function dblClick() {
29201 if (!dblclickEnabled) {
29202 d3.event.preventDefault();
29203 d3.event.stopImmediatePropagation();
29207 function zoomPan() {
29208 if (Math.log(d3.event.scale) / Math.LN2 - 8 < minzoom) {
29209 surface.interrupt();
29210 iD.ui.flash(context.container())
29211 .select('.content')
29212 .text(t('cannot_zoom'));
29213 setZoom(context.minEditableZoom(), true);
29215 dispatch.move(map);
29220 .translate(d3.event.translate)
29221 .scale(d3.event.scale / (2 * Math.PI));
29223 var scale = d3.event.scale / transformStart[0],
29224 tX = (d3.event.translate[0] / scale - transformStart[1][0]) * scale,
29225 tY = (d3.event.translate[1] / scale - transformStart[1][1]) * scale;
29227 transformed = true;
29228 iD.util.setTransform(supersurface, tX, tY, scale);
29231 dispatch.move(map);
29234 function resetTransform() {
29235 if (!transformed) return false;
29236 iD.util.setTransform(supersurface, 0, 0);
29237 transformed = false;
29241 function redraw(difference, extent) {
29242 if (!surface || !redrawEnabled) return;
29244 clearTimeout(timeoutId);
29246 // If we are in the middle of a zoom/pan, we can't do differenced redraws.
29247 // It would result in artifacts where differenced entities are redrawn with
29248 // one transform and unchanged entities with another.
29249 if (resetTransform()) {
29250 difference = extent = undefined;
29253 var zoom = String(~~map.zoom());
29254 if (surface.attr('data-zoom') !== zoom) {
29255 surface.attr('data-zoom', zoom)
29256 .classed('low-zoom', zoom <= 16);
29260 supersurface.call(context.background());
29263 if (map.editable()) {
29264 context.loadTiles(projection, dimensions);
29265 drawVector(difference, extent);
29271 projection.scale() * 2 * Math.PI,
29272 projection.translate().slice()];
29278 function queueRedraw() {
29279 clearTimeout(timeoutId);
29280 timeoutId = setTimeout(function() { redraw(); }, 300);
29283 function pointLocation(p) {
29284 var translate = projection.translate(),
29285 scale = projection.scale() * 2 * Math.PI;
29286 return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
29289 function locationPoint(l) {
29290 var translate = projection.translate(),
29291 scale = projection.scale() * 2 * Math.PI;
29292 return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
29295 map.mouse = function() {
29296 var e = mousemove || d3.event, s;
29297 while ((s = e.sourceEvent)) e = s;
29301 map.mouseCoordinates = function() {
29302 return projection.invert(map.mouse());
29305 map.dblclickEnable = function(_) {
29306 if (!arguments.length) return dblclickEnabled;
29307 dblclickEnabled = _;
29311 map.redrawEnable = function(_) {
29312 if (!arguments.length) return redrawEnabled;
29317 function interpolateZoom(_) {
29318 var k = projection.scale(),
29319 t = projection.translate();
29321 surface.node().__chart__ = {
29328 projection.scale(k).translate(t); // undo setZoom projection changes
29330 zoom.event(surface.transition());
29333 function setZoom(_, force) {
29334 if (_ === map.zoom() && !force)
29336 var scale = 256 * Math.pow(2, _),
29337 center = pxCenter(),
29338 l = pointLocation(center);
29339 scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
29340 projection.scale(scale / (2 * Math.PI));
29342 var t = projection.translate();
29343 l = locationPoint(l);
29344 t[0] += center[0] - l[0];
29345 t[1] += center[1] - l[1];
29346 projection.translate(t);
29347 zoom.translate(projection.translate());
29351 function setCenter(_) {
29352 var c = map.center();
29353 if (_[0] === c[0] && _[1] === c[1])
29355 var t = projection.translate(),
29357 ll = projection(_);
29358 projection.translate([
29359 t[0] - ll[0] + pxC[0],
29360 t[1] - ll[1] + pxC[1]]);
29361 zoom.translate(projection.translate());
29365 map.pan = function(d) {
29366 var t = projection.translate();
29369 projection.translate(t);
29370 zoom.translate(projection.translate());
29371 dispatch.move(map);
29375 map.dimensions = function(_) {
29376 if (!arguments.length) return dimensions;
29377 var center = map.center();
29379 surface.dimensions(dimensions);
29380 context.background().dimensions(dimensions);
29381 projection.clipExtent([[0, 0], dimensions]);
29382 mouse = iD.util.fastMouse(supersurface.node());
29387 function zoomIn(integer) {
29388 interpolateZoom(~~map.zoom() + integer);
29391 function zoomOut(integer) {
29392 interpolateZoom(~~map.zoom() - integer);
29395 map.zoomIn = function() { zoomIn(1); };
29396 map.zoomInFurther = function() { zoomIn(4); };
29398 map.zoomOut = function() { zoomOut(1); };
29399 map.zoomOutFurther = function() { zoomOut(4); };
29401 map.center = function(loc) {
29402 if (!arguments.length) {
29403 return projection.invert(pxCenter());
29406 if (setCenter(loc)) {
29407 dispatch.move(map);
29413 map.zoom = function(z) {
29414 if (!arguments.length) {
29415 return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
29419 surface.interrupt();
29420 iD.ui.flash(context.container())
29421 .select('.content')
29422 .text(t('cannot_zoom'));
29423 z = context.minEditableZoom();
29427 dispatch.move(map);
29433 map.zoomTo = function(entity, zoomLimits) {
29434 var extent = entity.extent(context.graph());
29435 if (!isFinite(extent.area())) return;
29437 var zoom = map.trimmedExtentZoom(extent);
29438 zoomLimits = zoomLimits || [context.minEditableZoom(), 20];
29439 map.centerZoom(extent.center(), Math.min(Math.max(zoom, zoomLimits[0]), zoomLimits[1]));
29442 map.centerZoom = function(loc, z) {
29443 var centered = setCenter(loc),
29444 zoomed = setZoom(z);
29446 if (centered || zoomed) {
29447 dispatch.move(map);
29453 map.centerEase = function(loc) {
29454 var from = map.center().slice(),
29458 surface.one('mousedown.ease', function() {
29462 d3.timer(function() {
29463 if (stop) return true;
29464 map.center(iD.geo.interp(from, loc, (t += 1) / 10));
29470 map.extent = function(_) {
29471 if (!arguments.length) {
29472 return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
29473 projection.invert([dimensions[0], 0]));
29475 var extent = iD.geo.Extent(_);
29476 map.centerZoom(extent.center(), map.extentZoom(extent));
29480 map.trimmedExtent = function(_) {
29481 if (!arguments.length) {
29482 var headerY = 60, footerY = 30, pad = 10;
29483 return new iD.geo.Extent(projection.invert([pad, dimensions[1] - footerY - pad]),
29484 projection.invert([dimensions[0] - pad, headerY + pad]));
29486 var extent = iD.geo.Extent(_);
29487 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
29491 function calcZoom(extent, dim) {
29492 var tl = projection([extent[0][0], extent[1][1]]),
29493 br = projection([extent[1][0], extent[0][1]]);
29495 // Calculate maximum zoom that fits extent
29496 var hFactor = (br[0] - tl[0]) / dim[0],
29497 vFactor = (br[1] - tl[1]) / dim[1],
29498 hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
29499 vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
29500 newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
29505 map.extentZoom = function(_) {
29506 return calcZoom(iD.geo.Extent(_), dimensions);
29509 map.trimmedExtentZoom = function(_) {
29510 var trimY = 120, trimX = 40,
29511 trimmed = [dimensions[0] - trimX, dimensions[1] - trimY];
29512 return calcZoom(iD.geo.Extent(_), trimmed);
29515 map.editable = function() {
29516 return map.zoom() >= context.minEditableZoom();
29519 map.minzoom = function(_) {
29520 if (!arguments.length) return minzoom;
29525 return d3.rebind(map, dispatch, 'on');
29527 iD.MapillaryLayer = function (context) {
29528 var enable = false,
29532 function show(image) {
29534 .classed('selected', function(d) {
29535 return currentImage && d.key === currentImage.key;
29538 div.classed('hidden', false)
29539 .classed('temp', image !== currentImage);
29541 div.selectAll('img')
29542 .attr('src', 'https://d1cuyjsrcm0gby.cloudfront.net/' + image.key + '/thumb-320.jpg');
29545 .attr('href', 'http://mapillary.com/map/im/' + image.key);
29549 currentImage = undefined;
29552 .classed('selected', false);
29554 div.classed('hidden', true);
29557 function transform(image) {
29558 var t = 'translate(' + context.projection(image.loc) + ')';
29559 if (image.ca) t += 'rotate(' + image.ca + ',0,0)';
29563 function render(selection) {
29564 svg = selection.selectAll('svg')
29567 svg.enter().append('svg')
29568 .on('click', function() {
29569 var image = d3.event.target.__data__;
29570 if (currentImage === image) {
29573 currentImage = image;
29577 .on('mouseover', function() {
29578 show(d3.event.target.__data__);
29580 .on('mouseout', function() {
29581 if (currentImage) {
29582 show(currentImage);
29588 svg.style('display', enable ? 'block' : 'none');
29590 div = context.container().selectAll('.mapillary-image')
29593 var enter = div.enter().append('div')
29594 .attr('class', 'mapillary-image');
29596 enter.append('button')
29599 .call(iD.svg.Icon('#icon-close'));
29601 enter.append('img');
29605 .attr('class', 'link')
29606 .attr('target', '_blank')
29607 .call(iD.svg.Icon('#icon-out-link', 'inline'))
29609 .text(t('mapillary.view_on_mapillary'));
29620 // Update existing images while waiting for new ones to load.
29622 .attr('transform', transform);
29624 var extent = context.map().extent();
29629 request = d3.json('https://a.mapillary.com/v2/search/s/geojson?client_id=NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzoxNjQ3MDY4ZTUxY2QzNGI2&min_lat=' +
29630 extent[0][1] + '&max_lat=' + extent[1][1] + '&min_lon=' +
29631 extent[0][0] + '&max_lon=' + extent[1][0] + '&max_results=100&geojson=true',
29632 function (error, data) {
29637 for (var i = 0; i < data.features.length; i++) {
29638 var sequence = data.features[i];
29639 for (var j = 0; j < sequence.geometry.coordinates.length; j++) {
29641 key: sequence.properties.keys[j],
29642 ca: sequence.properties.cas[j],
29643 loc: sequence.geometry.coordinates[j]
29645 if (images.length >= 1000) break;
29649 var g = svg.selectAll('g')
29650 .data(images, function(d) { return d.key; });
29652 var enter = g.enter().append('g')
29653 .attr('class', 'image');
29655 enter.append('path')
29656 .attr('class', 'viewfield')
29657 .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
29658 .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');
29660 enter.append('circle')
29665 g.attr('transform', transform);
29672 render.enable = function(_) {
29673 if (!arguments.length) return enable;
29678 render.dimensions = function(_) {
29679 if (!arguments.length) return svg.dimensions();
29686 iD.TileLayer = function() {
29687 var tileSize = 256,
29688 tile = d3.geo.tile(),
29693 transformProp = iD.util.prefixCSSProperty('Transform'),
29694 source = d3.functor('');
29696 function tileSizeAtZoom(d, z) {
29697 return (tileSize * Math.pow(2, z - d[2])) / tileSize;
29700 function atZoom(t, distance) {
29701 var power = Math.pow(2, distance);
29703 Math.floor(t[0] * power),
29704 Math.floor(t[1] * power),
29708 function lookUp(d) {
29709 for (var up = -1; up > -d[2]; up--) {
29710 var tile = atZoom(d, up);
29711 if (cache[source.url(tile)] !== false) {
29717 function uniqueBy(a, n) {
29718 var o = [], seen = {};
29719 for (var i = 0; i < a.length; i++) {
29720 if (seen[a[i][n]] === undefined) {
29722 seen[a[i][n]] = true;
29728 function addSource(d) {
29729 d.push(source.url(d));
29733 // Update tiles based on current state of `projection`.
29734 function background(selection) {
29735 tile.scale(projection.scale() * 2 * Math.PI)
29736 .translate(projection.translate());
29739 projection.scale() * Math.PI - projection.translate()[0],
29740 projection.scale() * Math.PI - projection.translate()[1]];
29742 z = Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.log(2) - 8, 0);
29747 // Derive the tiles onscreen, remove those offscreen and position them.
29748 // Important that this part not depend on `projection` because it's
29749 // rentered when tiles load/error (see #644).
29750 function render(selection) {
29753 if (source.validZoom(z)) {
29754 tile().forEach(function(d) {
29756 if (d[3] === '') return;
29757 if (typeof d[3] !== 'string') return; // Workaround for chrome crash https://github.com/openstreetmap/iD/issues/2295
29759 if (cache[d[3]] === false && lookUp(d)) {
29760 requests.push(addSource(lookUp(d)));
29764 requests = uniqueBy(requests, 3).filter(function(r) {
29765 // don't re-request tiles which have failed in the past
29766 return cache[r[3]] !== false;
29770 var pixelOffset = [
29771 source.offset()[0] * Math.pow(2, z),
29772 source.offset()[1] * Math.pow(2, z)
29776 cache[d[3]] = true;
29780 .classed('tile-loaded', true);
29784 function error(d) {
29785 cache[d[3]] = false;
29793 function imageTransform(d) {
29794 var _ts = tileSize * Math.pow(2, z - d[2]);
29795 var scale = tileSizeAtZoom(d, z);
29796 return 'translate(' +
29797 ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0]) + 'px,' +
29798 ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1]) + 'px)' +
29799 'scale(' + scale + ',' + scale + ')';
29802 var image = selection
29804 .data(requests, function(d) { return d[3]; });
29807 .style(transformProp, imageTransform)
29808 .classed('tile-removing', true)
29810 var tile = d3.select(this);
29811 window.setTimeout(function() {
29812 if (tile.classed('tile-removing')) {
29818 image.enter().append('img')
29819 .attr('class', 'tile')
29820 .attr('src', function(d) { return d[3]; })
29821 .on('error', error)
29825 .style(transformProp, imageTransform)
29826 .classed('tile-removing', false);
29829 background.projection = function(_) {
29830 if (!arguments.length) return projection;
29835 background.dimensions = function(_) {
29836 if (!arguments.length) return tile.size();
29841 background.source = function(_) {
29842 if (!arguments.length) return source;
29845 tile.scaleExtent(source.scaleExtent);
29852 PointTransform: function(projection) {
29853 return function(entity) {
29854 // http://jsperf.com/short-array-join
29855 var pt = projection(entity.loc);
29856 return 'translate(' + pt[0] + ',' + pt[1] + ')';
29860 Path: function(projection, graph, polygon) {
29862 clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
29863 project = projection.stream,
29864 path = d3.geo.path()
29865 .projection({stream: function(output) { return polygon ? project(output) : project(clip(output)); }});
29867 return function(entity) {
29868 if (entity.id in cache) {
29869 return cache[entity.id];
29871 return cache[entity.id] = path(entity.asGeoJSON(graph));
29876 OneWaySegments: function(projection, graph, dt) {
29877 return function(entity) {
29883 clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
29884 coordinates = graph.childNodes(entity).map(function(n) {
29888 if (entity.tags.oneway === '-1') coordinates.reverse();
29891 type: 'LineString',
29892 coordinates: coordinates
29893 }, projection.stream(clip({
29894 lineStart: function() {},
29895 lineEnd: function() {
29898 point: function(x, y) {
29902 var span = iD.geo.euclideanDistance(a, b) - offset;
29905 var angle = Math.atan2(b[1] - a[1], b[0] - a[0]),
29906 dx = dt * Math.cos(angle),
29907 dy = dt * Math.sin(angle),
29908 p = [a[0] + offset * Math.cos(angle),
29909 a[1] + offset * Math.sin(angle)];
29911 var segment = 'M' + a[0] + ',' + a[1] +
29912 'L' + p[0] + ',' + p[1];
29914 for (span -= dt; span >= 0; span -= dt) {
29917 segment += 'L' + p[0] + ',' + p[1];
29920 segment += 'L' + b[0] + ',' + b[1];
29921 segments.push({id: entity.id, index: i, d: segment});
29936 MultipolygonMemberTags: function(graph) {
29937 return function(entity) {
29938 var tags = entity.tags;
29939 graph.parentRelations(entity).forEach(function(relation) {
29940 if (relation.isMultipolygon()) {
29941 tags = _.extend({}, relation.tags, tags);
29948 iD.svg.Areas = function(projection) {
29949 // Patterns only work in Firefox when set directly on element.
29950 // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
29952 wetland: 'wetland',
29955 construction: 'construction',
29956 military: 'construction',
29957 cemetery: 'cemetery',
29958 grave_yard: 'cemetery',
29961 farmland: 'farmland',
29965 var patternKeys = ['landuse', 'natural', 'amenity'];
29967 function setPattern(d) {
29968 for (var i = 0; i < patternKeys.length; i++) {
29969 if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) {
29970 this.style.fill = this.style.stroke = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")';
29974 this.style.fill = this.style.stroke = '';
29977 return function drawAreas(surface, graph, entities, filter) {
29978 var path = iD.svg.Path(projection, graph, true),
29982 for (var i = 0; i < entities.length; i++) {
29983 var entity = entities[i];
29984 if (entity.geometry(graph) !== 'area') continue;
29986 multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph);
29987 if (multipolygon) {
29988 areas[multipolygon.id] = {
29989 entity: multipolygon.mergeTags(entity.tags),
29990 area: Math.abs(entity.area(graph))
29992 } else if (!areas[entity.id]) {
29993 areas[entity.id] = {
29995 area: Math.abs(entity.area(graph))
30000 areas = d3.values(areas).filter(function hasPath(a) { return path(a.entity); });
30001 areas.sort(function areaSort(a, b) { return b.area - a.area; });
30002 areas = _.pluck(areas, 'entity');
30004 var strokes = areas.filter(function(area) {
30005 return area.type === 'way';
30015 var clipPaths = surface.selectAll('defs').selectAll('.clipPath')
30017 .data(data.clip, iD.Entity.key);
30020 .append('clipPath')
30021 .attr('class', 'clipPath')
30022 .attr('id', function(entity) { return entity.id + '-clippath'; })
30025 clipPaths.selectAll('path')
30031 var areagroup = surface
30032 .select('.layer-areas')
30033 .selectAll('g.areagroup')
30034 .data(['fill', 'shadow', 'stroke']);
30038 .attr('class', function(d) { return 'layer areagroup area-' + d; });
30040 var paths = areagroup
30043 .data(function(layer) { return data[layer]; }, iD.Entity.key);
30045 // Remove exiting areas first, so they aren't included in the `fills`
30046 // array used for sorting below (https://github.com/openstreetmap/iD/issues/1903).
30050 var fills = surface.selectAll('.area-fill path.area')[0];
30052 var bisect = d3.bisector(function(node) {
30053 return -node.__data__.area(graph);
30056 function sortedByArea(entity) {
30057 if (this.__data__ === 'fill') {
30058 return fills[bisect(fills, -entity.area(graph))];
30063 .insert('path', sortedByArea)
30064 .each(function(entity) {
30065 var layer = this.parentNode.__data__;
30067 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
30069 if (layer === 'fill') {
30070 this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)');
30071 setPattern.apply(this, arguments);
30074 .call(iD.svg.TagClasses());
30081 A standalone SVG element that contains only a `defs` sub-element. To be
30082 used once globally, since defs IDs must be unique within a document.
30084 iD.svg.Defs = function(context) {
30086 function SVGSpriteDefinition(id, href) {
30087 return function(defs) {
30088 d3.xml(href, 'image/svg+xml', function(err, svg) {
30090 defs.node().appendChild(
30091 d3.select(svg.documentElement).attr('id', id).node()
30097 return function (selection) {
30098 var defs = selection.append('defs');
30101 defs.append('marker')
30103 id: 'oneway-marker',
30104 viewBox: '0 0 10 10',
30109 markerUnits: 'strokeWidth',
30113 .attr('class', 'oneway')
30114 .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')
30115 .attr('stroke', 'none')
30116 .attr('fill', '#000')
30117 .attr('opacity', '0.5');
30120 var patterns = defs.selectAll('pattern')
30122 // pattern name, pattern image name
30123 ['wetland', 'wetland'],
30124 ['construction', 'construction'],
30125 ['cemetery', 'cemetery'],
30126 ['orchard', 'orchard'],
30127 ['farmland', 'farmland'],
30136 return 'pattern-' + d[0];
30140 patternUnits: 'userSpaceOnUse'
30143 patterns.append('rect')
30149 'class': function (d) {
30150 return 'pattern-color-' + d[0];
30154 patterns.append('image')
30161 .attr('xlink:href', function (d) {
30162 return context.imagePath('pattern/' + d[1] + '.png');
30167 .data([12, 18, 20, 32, 45])
30168 .enter().append('clipPath')
30169 .attr('id', function (d) {
30170 return 'clip-square-' + d;
30175 .attr('width', function (d) {
30178 .attr('height', function (d) {
30182 defs.call(SVGSpriteDefinition(
30184 context.imagePath('iD-sprite.svg')));
30186 defs.call(SVGSpriteDefinition(
30188 context.imagePath('maki-sprite.svg')));
30191 iD.svg.Icon = function(name, svgklass, useklass) {
30192 return function (selection) {
30193 selection.selectAll('svg')
30197 .attr('class', 'icon ' + (svgklass || ''))
30199 .attr('xlink:href', name)
30200 .attr('class', useklass);
30203 iD.svg.Labels = function(projection, context) {
30204 var path = d3.geo.path().projection(projection);
30206 // Replace with dict and iterate over entities tags instead?
30207 var label_stack = [
30208 ['line', 'aeroway'],
30209 ['line', 'highway'],
30210 ['line', 'railway'],
30211 ['line', 'waterway'],
30212 ['area', 'aeroway'],
30213 ['area', 'amenity'],
30214 ['area', 'building'],
30215 ['area', 'historic'],
30216 ['area', 'leisure'],
30217 ['area', 'man_made'],
30218 ['area', 'natural'],
30220 ['area', 'tourism'],
30221 ['point', 'aeroway'],
30222 ['point', 'amenity'],
30223 ['point', 'building'],
30224 ['point', 'historic'],
30225 ['point', 'leisure'],
30226 ['point', 'man_made'],
30227 ['point', 'natural'],
30229 ['point', 'tourism'],
30235 var default_size = 12;
30237 var font_sizes = label_stack.map(function(d) {
30238 var style = iD.util.getStyle('text.' + d[0] + '.tag-' + d[1]),
30239 m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30240 if (m) return parseInt(m[1], 10);
30242 style = iD.util.getStyle('text.' + d[0]);
30243 m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30244 if (m) return parseInt(m[1], 10);
30246 return default_size;
30251 var pointOffsets = [
30252 [15, -11, 'start'], // right
30253 [10, -11, 'start'], // unused right now
30257 var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25,
30258 75, 20, 80, 15, 95, 10, 90, 5, 95];
30261 var noIcons = ['building', 'landuse', 'natural'];
30262 function blacklisted(preset) {
30263 return _.any(noIcons, function(s) {
30264 return preset.id.indexOf(s) >= 0;
30268 function get(array, prop) {
30269 return function(d, i) { return array[i][prop]; };
30272 var textWidthCache = {};
30274 function textWidth(text, size, elem) {
30275 var c = textWidthCache[size];
30276 if (!c) c = textWidthCache[size] = {};
30282 c[text] = elem.getComputedTextLength();
30286 var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
30287 if (str === null) {
30288 return size / 3 * 2 * text.length;
30290 return size / 3 * (2 * text.length + str.length);
30295 function drawLineLabels(group, entities, filter, classes, labels) {
30296 var texts = group.selectAll('text.' + classes)
30298 .data(entities, iD.Entity.key);
30302 .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
30303 .append('textPath')
30304 .attr('class', 'textpath');
30307 texts.selectAll('.textpath')
30309 .data(entities, iD.Entity.key)
30311 'startOffset': '50%',
30312 'xlink:href': function(d) { return '#labelpath-' + d.id; }
30314 .text(iD.util.displayName);
30316 texts.exit().remove();
30319 function drawLinePaths(group, entities, filter, classes, labels) {
30320 var halos = group.selectAll('path')
30322 .data(entities, iD.Entity.key);
30326 .style('stroke-width', get(labels, 'font-size'))
30327 .attr('id', function(d) { return 'labelpath-' + d.id; })
30328 .attr('class', classes);
30330 halos.attr('d', get(labels, 'lineString'));
30332 halos.exit().remove();
30335 function drawPointLabels(group, entities, filter, classes, labels) {
30337 var texts = group.selectAll('text.' + classes)
30339 .data(entities, iD.Entity.key);
30343 .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; });
30345 texts.attr('x', get(labels, 'x'))
30346 .attr('y', get(labels, 'y'))
30347 .style('text-anchor', get(labels, 'textAnchor'))
30348 .text(iD.util.displayName)
30349 .each(function(d, i) { textWidth(iD.util.displayName(d), labels[i].height, this); });
30351 texts.exit().remove();
30355 function drawAreaLabels(group, entities, filter, classes, labels) {
30356 entities = entities.filter(hasText);
30357 labels = labels.filter(hasText);
30358 return drawPointLabels(group, entities, filter, classes, labels);
30360 function hasText(d, i) {
30361 return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
30365 function drawAreaIcons(group, entities, filter, classes, labels) {
30366 var icons = group.selectAll('use')
30368 .data(entities, iD.Entity.key);
30372 .attr('class', 'icon areaicon')
30373 .attr('width', '18px')
30374 .attr('height', '18px');
30376 icons.attr('transform', get(labels, 'transform'))
30377 .attr('xlink:href', function(d) {
30378 var icon = context.presets().match(d, context.graph()).icon;
30379 return '#' + icon + (icon === 'hairdresser' ? '-24': '-18'); // workaround: maki hairdresser-18 broken?
30383 icons.exit().remove();
30386 function reverse(p) {
30387 var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
30388 return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
30391 function lineString(nodes) {
30392 return 'M' + nodes.join('L');
30395 function subpath(nodes, from, to) {
30396 function segmentLength(i) {
30397 var dx = nodes[i][0] - nodes[i + 1][0];
30398 var dy = nodes[i][1] - nodes[i + 1][1];
30399 return Math.sqrt(dx * dx + dy * dy);
30403 start, end, i0, i1;
30404 for (var i = 0; i < nodes.length - 1; i++) {
30405 var current = segmentLength(i);
30407 if (!start && sofar + current >= from) {
30408 portion = (from - sofar) / current;
30410 nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30411 nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30415 if (!end && sofar + current >= to) {
30416 portion = (to - sofar) / current;
30418 nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30419 nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30426 var ret = nodes.slice(i0, i1);
30427 ret.unshift(start);
30433 function hideOnMouseover() {
30434 var layers = d3.select(this)
30435 .selectAll('.layer-label, .layer-halo');
30437 layers.selectAll('.proximate')
30438 .classed('proximate', false);
30440 var mouse = context.mouse(),
30442 rect = [mouse[0] - pad, mouse[1] - pad, mouse[0] + pad, mouse[1] + pad],
30443 ids = _.pluck(rtree.search(rect), 'id');
30445 if (!ids.length) return;
30446 layers.selectAll('.' + ids.join(', .'))
30447 .classed('proximate', true);
30450 var rtree = rbush(),
30453 function labels(surface, graph, entities, filter, dimensions, fullRedraw) {
30455 var hidePoints = !surface.select('.node.point').node();
30457 var labelable = [], i, k, entity;
30458 for (i = 0; i < label_stack.length; i++) labelable.push([]);
30464 for (i = 0; i < entities.length; i++) {
30465 rtree.remove(rectangles[entities[i].id]);
30469 // Split entities into groups specified by label_stack
30470 for (i = 0; i < entities.length; i++) {
30471 entity = entities[i];
30472 var geometry = entity.geometry(graph);
30474 if (geometry === 'vertex')
30476 if (hidePoints && geometry === 'point')
30479 var preset = geometry === 'area' && context.presets().match(entity, graph),
30480 icon = preset && !blacklisted(preset) && preset.icon;
30482 if (!icon && !iD.util.displayName(entity))
30485 for (k = 0; k < label_stack.length; k++) {
30486 if (geometry === label_stack[k][0] && entity.tags[label_stack[k][1]]) {
30487 labelable[k].push(entity);
30505 // Try and find a valid label for labellable entities
30506 for (k = 0; k < labelable.length; k++) {
30507 var font_size = font_sizes[k];
30508 for (i = 0; i < labelable[k].length; i++) {
30509 entity = labelable[k][i];
30510 var name = iD.util.displayName(entity),
30511 width = name && textWidth(name, font_size),
30513 if (entity.geometry(graph) === 'point') {
30514 p = getPointLabel(entity, width, font_size);
30515 } else if (entity.geometry(graph) === 'line') {
30516 p = getLineLabel(entity, width, font_size);
30517 } else if (entity.geometry(graph) === 'area') {
30518 p = getAreaLabel(entity, width, font_size);
30521 p.classes = entity.geometry(graph) + ' tag-' + label_stack[k][1];
30522 positions[entity.geometry(graph)].push(p);
30523 labelled[entity.geometry(graph)].push(entity);
30528 function getPointLabel(entity, width, height) {
30529 var coord = projection(entity.loc),
30531 offset = pointOffsets[0],
30535 x: coord[0] + offset[0],
30536 y: coord[1] + offset[1],
30537 textAnchor: offset[2]
30539 var rect = [p.x - m, p.y - m, p.x + width + m, p.y + height + m];
30540 if (tryInsert(rect, entity.id)) return p;
30544 function getLineLabel(entity, width, height) {
30545 var nodes = _.pluck(graph.childNodes(entity), 'loc').map(projection),
30546 length = iD.geo.pathLength(nodes);
30547 if (length < width + 20) return;
30549 for (var i = 0; i < lineOffsets.length; i++) {
30550 var offset = lineOffsets[i],
30551 middle = offset / 100 * length,
30552 start = middle - width/2;
30553 if (start < 0 || start + width > length) continue;
30554 var sub = subpath(nodes, start, start + width),
30555 rev = reverse(sub),
30557 Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
30558 Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
30559 Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
30560 Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
30562 if (rev) sub = sub.reverse();
30563 if (tryInsert(rect, entity.id)) return {
30564 'font-size': height + 2,
30565 lineString: lineString(sub),
30566 startOffset: offset + '%'
30571 function getAreaLabel(entity, width, height) {
30572 var centroid = path.centroid(entity.asGeoJSON(graph, true)),
30573 extent = entity.extent(graph),
30574 entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
30577 if (isNaN(centroid[0]) || entitywidth < 20) return;
30579 var iconX = centroid[0] - (iconSize/2),
30580 iconY = centroid[1] - (iconSize/2),
30581 textOffset = iconSize + 5;
30584 transform: 'translate(' + iconX + ',' + iconY + ')'
30587 if (width && entitywidth >= width + 20) {
30589 p.y = centroid[1] + textOffset;
30590 p.textAnchor = 'middle';
30592 rect = [p.x - width/2, p.y, p.x + width/2, p.y + height + textOffset];
30594 rect = [iconX, iconY, iconX + iconSize, iconY + iconSize];
30597 if (tryInsert(rect, entity.id)) return p;
30601 function tryInsert(rect, id) {
30602 // Check that label is visible
30603 if (rect[0] < 0 || rect[1] < 0 || rect[2] > dimensions[0] ||
30604 rect[3] > dimensions[1]) return false;
30605 var v = rtree.search(rect).length === 0;
30608 rtree.insert(rect);
30609 rectangles[id] = rect;
30614 var label = surface.select('.layer-label'),
30615 halo = surface.select('.layer-halo');
30618 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
30619 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
30622 drawLinePaths(halo, labelled.line, filter, '', positions.line);
30623 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
30624 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
30627 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
30628 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
30629 drawAreaIcons(label, labelled.area, filter, 'arealabel-icon', positions.area);
30632 labels.supersurface = function(supersurface) {
30634 .on('mousemove.hidelabels', hideOnMouseover)
30635 .on('mousedown.hidelabels', function () {
30636 supersurface.on('mousemove.hidelabels', null);
30638 .on('mouseup.hidelabels', function () {
30639 supersurface.on('mousemove.hidelabels', hideOnMouseover);
30645 iD.svg.Lines = function(projection) {
30647 var highway_stack = {
30662 function waystack(a, b) {
30663 var as = 0, bs = 0;
30665 if (a.tags.highway) { as -= highway_stack[a.tags.highway]; }
30666 if (b.tags.highway) { bs -= highway_stack[b.tags.highway]; }
30670 return function drawLines(surface, graph, entities, filter) {
30671 var ways = [], pathdata = {}, onewaydata = {},
30672 getPath = iD.svg.Path(projection, graph);
30674 for (var i = 0; i < entities.length; i++) {
30675 var entity = entities[i],
30676 outer = iD.geo.simpleMultipolygonOuterMember(entity, graph);
30678 ways.push(entity.mergeTags(outer.tags));
30679 } else if (entity.geometry(graph) === 'line') {
30684 ways = ways.filter(getPath);
30686 pathdata = _.groupBy(ways, function(way) { return way.layer(); });
30688 _.forOwn(pathdata, function(v, k) {
30689 onewaydata[k] = _(v)
30690 .filter(function(d) { return d.isOneWay(); })
30691 .map(iD.svg.OneWaySegments(projection, graph, 35))
30696 var layergroup = surface
30697 .select('.layer-lines')
30698 .selectAll('g.layergroup')
30699 .data(d3.range(-10, 11));
30703 .attr('class', function(d) { return 'layer layergroup layer' + String(d); });
30706 var linegroup = layergroup
30707 .selectAll('g.linegroup')
30708 .data(['shadow', 'casing', 'stroke']);
30712 .attr('class', function(d) { return 'layer linegroup line-' + d; });
30715 var lines = linegroup
30719 function() { return pathdata[this.parentNode.parentNode.__data__] || []; },
30723 // Optimization: call simple TagClasses only on enter selection. This
30724 // works because iD.Entity.key is defined to include the entity v attribute.
30727 .attr('class', function(d) { return 'way line ' + this.parentNode.__data__ + ' ' + d.id; })
30728 .call(iD.svg.TagClasses());
30732 .attr('d', getPath)
30733 .call(iD.svg.TagClasses().tags(iD.svg.MultipolygonMemberTags(graph)));
30739 var onewaygroup = layergroup
30740 .selectAll('g.onewaygroup')
30743 onewaygroup.enter()
30745 .attr('class', 'layer onewaygroup');
30748 var oneways = onewaygroup
30752 function() { return onewaydata[this.parentNode.parentNode.__data__] || []; },
30753 function(d) { return [d.id, d.index]; }
30758 .attr('class', 'oneway')
30759 .attr('marker-mid', 'url(#oneway-marker)');
30762 .attr('d', function(d) { return d.d; });
30764 if (iD.detect().ie) {
30765 oneways.each(function() { this.parentNode.insertBefore(this, this); });
30773 iD.svg.Midpoints = function(projection, context) {
30774 return function drawMidpoints(surface, graph, entities, filter, extent) {
30775 var poly = extent.polygon(),
30778 for (var i = 0; i < entities.length; i++) {
30779 var entity = entities[i];
30781 if (entity.type !== 'way')
30783 if (!filter(entity))
30785 if (context.selectedIDs().indexOf(entity.id) < 0)
30788 var nodes = graph.childNodes(entity);
30789 for (var j = 0; j < nodes.length - 1; j++) {
30793 id = [a.id, b.id].sort().join('-');
30795 if (midpoints[id]) {
30796 midpoints[id].parents.push(entity);
30798 if (iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
30799 var point = iD.geo.interp(a.loc, b.loc, 0.5),
30802 if (extent.intersects(point)) {
30805 for (var k = 0; k < 4; k++) {
30806 point = iD.geo.lineIntersection([a.loc, b.loc], [poly[k], poly[k+1]]);
30808 iD.geo.euclideanDistance(projection(a.loc), projection(point)) > 20 &&
30809 iD.geo.euclideanDistance(projection(b.loc), projection(point)) > 20)
30822 edge: [a.id, b.id],
30831 function midpointFilter(d) {
30832 if (midpoints[d.id])
30835 for (var i = 0; i < d.parents.length; i++)
30836 if (filter(d.parents[i]))
30842 var groups = surface.select('.layer-hit').selectAll('g.midpoint')
30843 .filter(midpointFilter)
30844 .data(_.values(midpoints), function(d) { return d.id; });
30846 var enter = groups.enter()
30847 .insert('g', ':first-child')
30848 .attr('class', 'midpoint');
30850 enter.append('polygon')
30851 .attr('points', '-6,8 10,0 -6,-8')
30852 .attr('class', 'shadow');
30854 enter.append('polygon')
30855 .attr('points', '-3,4 5,0 -3,-4')
30856 .attr('class', 'fill');
30859 .attr('transform', function(d) {
30860 var translate = iD.svg.PointTransform(projection),
30861 a = context.entity(d.edge[0]),
30862 b = context.entity(d.edge[1]),
30863 angle = Math.round(iD.geo.angle(a, b, projection) * (180 / Math.PI));
30864 return translate(d) + ' rotate(' + angle + ')';
30866 .call(iD.svg.TagClasses().tags(
30867 function(d) { return d.parents[0].tags; }
30870 // Propagate data bindings.
30871 groups.select('polygon.shadow');
30872 groups.select('polygon.fill');
30878 iD.svg.Points = function(projection, context) {
30879 function markerPath(selection, klass) {
30881 .attr('class', klass)
30882 .attr('transform', 'translate(-8, -23)')
30883 .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');
30886 function sortY(a, b) {
30887 return b.loc[1] - a.loc[1];
30890 return function drawPoints(surface, entities, filter) {
30891 var graph = context.graph(),
30892 wireframe = surface.classed('fill-wireframe'),
30893 points = wireframe ? [] : _.filter(entities, function(e) {
30894 return e.geometry(graph) === 'point';
30897 points.sort(sortY);
30899 var groups = surface.select('.layer-hit').selectAll('g.point')
30901 .data(points, iD.Entity.key);
30903 var group = groups.enter()
30905 .attr('class', function(d) { return 'node point ' + d.id; })
30908 group.append('path')
30909 .call(markerPath, 'shadow');
30911 group.append('path')
30912 .call(markerPath, 'stroke');
30914 group.append('use')
30915 .attr('transform', 'translate(-6, -20)')
30916 .attr('class', 'icon')
30917 .attr('width', '12px')
30918 .attr('height', '12px');
30920 groups.attr('transform', iD.svg.PointTransform(projection))
30921 .call(iD.svg.TagClasses());
30923 // Selecting the following implicitly
30924 // sets the data (point entity) on the element
30925 groups.select('.shadow');
30926 groups.select('.stroke');
30927 groups.select('.icon')
30928 .attr('xlink:href', function(entity) {
30929 var preset = context.presets().match(entity, context.graph());
30930 return preset.icon ? '#' + preset.icon + '-12' : '';
30937 iD.svg.Surface = function() {
30938 return function (selection) {
30939 selection.selectAll('defs')
30944 var layers = selection.selectAll('.layer')
30945 .data(['areas', 'lines', 'hit', 'halo', 'label']);
30947 layers.enter().append('g')
30948 .attr('class', function(d) { return 'layer layer-' + d; });
30951 iD.svg.TagClasses = function() {
30953 'building', 'highway', 'railway', 'waterway', 'aeroway',
30954 'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse',
30958 'proposed', 'construction', 'disused', 'abandoned', 'dismantled',
30959 'razed', 'demolished', 'obliterated'
30962 'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
30963 'surface', 'tracktype', 'crossing'
30965 tagClassRe = /^tag-/,
30966 tags = function(entity) { return entity.tags; };
30969 var tagClasses = function(selection) {
30970 selection.each(function tagClassesEach(entity) {
30971 var value = this.className,
30972 classes, primary, status;
30974 if (value.baseVal !== undefined) value = value.baseVal;
30976 classes = value.trim().split(/\s+/).filter(function(name) {
30977 return name.length && !tagClassRe.test(name);
30980 var t = tags(entity), i, k, v;
30982 // pick at most one primary classification tag..
30983 for (i = 0; i < primaries.length; i++) {
30986 if (!v || v === 'no') continue;
30989 if (statuses.indexOf(v) !== -1) { // e.g. `railway=abandoned`
30991 classes += ' tag-' + k;
30993 classes += ' tag-' + k + ' tag-' + k + '-' + v;
30999 // add at most one status tag, only if relates to primary tag..
31001 for (i = 0; i < statuses.length; i++) {
31004 if (!v || v === 'no') continue;
31006 if (v === 'yes') { // e.g. `railway=rail + abandoned=yes`
31009 else if (primary && primary === v) { // e.g. `railway=rail + abandoned=railway`
31011 } else if (!primary && primaries.indexOf(v) !== -1) { // e.g. `abandoned=railway`
31014 classes += ' tag-' + v;
31015 } // else ignore e.g. `highway=path + abandoned=railway`
31022 classes += ' tag-status tag-status-' + status;
31025 // add any secondary (structure) tags
31026 for (i = 0; i < secondaries.length; i++) {
31027 k = secondaries[i];
31029 if (!v || v === 'no') continue;
31030 classes += ' tag-' + k + ' tag-' + k + '-' + v;
31033 // For highways, look for surface tagging..
31034 if (primary === 'highway') {
31035 var paved = (t.highway !== 'track');
31038 if (k in iD.pavedTags) {
31039 paved = !!iD.pavedTags[k][v];
31044 classes += ' tag-unpaved';
31048 classes = classes.trim();
31050 if (classes !== value) {
31051 d3.select(this).attr('class', classes);
31056 tagClasses.tags = function(_) {
31057 if (!arguments.length) return tags;
31064 iD.svg.Turns = function(projection) {
31065 return function(surface, graph, turns) {
31066 function key(turn) {
31067 return [turn.from.node + turn.via.node + turn.to.node].join('-');
31070 function icon(turn) {
31071 var u = turn.u ? '-u' : '';
31072 if (!turn.restriction)
31073 return '#turn-yes' + u;
31074 var restriction = graph.entity(turn.restriction).tags.restriction;
31076 (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u;
31079 var groups = surface.select('.layer-hit').selectAll('g.turn')
31083 var enter = groups.enter().append('g')
31084 .attr('class', 'turn');
31086 var nEnter = enter.filter(function (turn) { return !turn.u; });
31088 nEnter.append('rect')
31089 .attr('transform', 'translate(-22, -12)')
31090 .attr('width', '44')
31091 .attr('height', '24');
31093 nEnter.append('use')
31094 .attr('transform', 'translate(-22, -12)')
31095 .attr('width', '44')
31096 .attr('height', '24');
31099 var uEnter = enter.filter(function (turn) { return turn.u; });
31101 uEnter.append('circle')
31104 uEnter.append('use')
31105 .attr('transform', 'translate(-16, -16)')
31106 .attr('width', '32')
31107 .attr('height', '32');
31112 .attr('transform', function (turn) {
31113 var v = graph.entity(turn.via.node),
31114 t = graph.entity(turn.to.node),
31115 a = iD.geo.angle(v, t, projection),
31116 p = projection(v.loc),
31117 r = turn.u ? 0 : 60;
31119 return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' +
31120 'rotate(' + a * 180 / Math.PI + ')';
31123 groups.select('use')
31124 .attr('xlink:href', icon);
31126 groups.select('rect');
31127 groups.select('circle');
31137 iD.svg.Vertices = function(projection, context) {
31139 // z16-, z17, z18+, tagged
31140 shadow: [6, 7.5, 7.5, 11.5],
31141 stroke: [2.5, 3.5, 3.5, 7],
31142 fill: [1, 1.5, 1.5, 1.5]
31147 function siblingAndChildVertices(ids, graph, extent) {
31150 function addChildVertices(entity) {
31151 if (!context.features().isHiddenFeature(entity, graph, entity.geometry(graph))) {
31153 if (entity.type === 'way') {
31154 for (i = 0; i < entity.nodes.length; i++) {
31155 addChildVertices(graph.entity(entity.nodes[i]));
31157 } else if (entity.type === 'relation') {
31158 for (i = 0; i < entity.members.length; i++) {
31159 var member = context.hasEntity(entity.members[i].id);
31161 addChildVertices(member);
31164 } else if (entity.intersects(extent, graph)) {
31165 vertices[entity.id] = entity;
31170 ids.forEach(function(id) {
31171 var entity = context.hasEntity(id);
31172 if (entity && entity.type === 'node') {
31173 vertices[entity.id] = entity;
31174 context.graph().parentWays(entity).forEach(function(entity) {
31175 addChildVertices(entity);
31177 } else if (entity) {
31178 addChildVertices(entity);
31185 function draw(selection, vertices, klass, graph, zoom) {
31191 } else if (zoom < 18) {
31197 var groups = selection.data(vertices, function(entity) {
31198 return iD.Entity.key(entity);
31201 function icon(entity) {
31202 if (entity.id in icons) return icons[entity.id];
31204 entity.hasInterestingTags() &&
31205 context.presets().match(entity, graph).icon;
31206 return icons[entity.id];
31209 function setClass(klass) {
31210 return function(entity) {
31211 this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id);
31215 function setAttributes(selection) {
31216 ['shadow','stroke','fill'].forEach(function(klass) {
31217 var rads = radiuses[klass];
31218 selection.selectAll('.' + klass)
31219 .each(function(entity) {
31220 var i = z && icon(entity),
31222 r = rads[i ? 3 : z];
31223 this.setAttribute('cx', c);
31224 this.setAttribute('cy', -c);
31225 this.setAttribute('r', r);
31226 if (i && klass === 'fill') {
31227 this.setAttribute('visibility', 'hidden');
31229 this.removeAttribute('visibility');
31234 selection.selectAll('use')
31237 this.removeAttribute('visibility');
31239 this.setAttribute('visibility', 'hidden');
31244 var enter = groups.enter()
31246 .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; });
31248 enter.append('circle')
31249 .each(setClass('shadow'));
31251 enter.append('circle')
31252 .each(setClass('stroke'));
31254 // Vertices with icons get a `use`.
31255 enter.filter(function(d) { return icon(d); })
31257 .attr('transform', 'translate(-6, -6)')
31258 .attr('xlink:href', function(d) { return '#' + icon(d) + '-12'; })
31259 .attr('width', '12px')
31260 .attr('height', '12px')
31261 .each(setClass('icon'));
31263 // Vertices with tags get a fill.
31264 enter.filter(function(d) { return d.hasInterestingTags(); })
31266 .each(setClass('fill'));
31269 .attr('transform', iD.svg.PointTransform(projection))
31270 .classed('shared', function(entity) { return graph.isShared(entity); })
31271 .call(setAttributes);
31277 function drawVertices(surface, graph, entities, filter, extent, zoom) {
31278 var selected = siblingAndChildVertices(context.selectedIDs(), graph, extent),
31279 wireframe = surface.classed('fill-wireframe'),
31282 for (var i = 0; i < entities.length; i++) {
31283 var entity = entities[i],
31284 geometry = entity.geometry(graph);
31286 if (wireframe && geometry === 'point') {
31287 vertices.push(entity);
31291 if (geometry !== 'vertex')
31294 if (entity.id in selected ||
31295 entity.hasInterestingTags() ||
31296 entity.isIntersection(graph)) {
31297 vertices.push(entity);
31301 surface.select('.layer-hit').selectAll('g.vertex.vertex-persistent')
31303 .call(draw, vertices, 'vertex-persistent', graph, zoom);
31305 drawHover(surface, graph, extent, zoom);
31308 function drawHover(surface, graph, extent, zoom) {
31309 var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent) : {};
31311 surface.select('.layer-hit').selectAll('g.vertex.vertex-hover')
31312 .call(draw, d3.values(hovered), 'vertex-hover', graph, zoom);
31315 drawVertices.drawHover = function(surface, graph, _, extent, zoom) {
31318 drawHover(surface, graph, extent, zoom);
31322 return drawVertices;
31324 iD.ui = function(context) {
31325 function render(container) {
31326 var map = context.map();
31328 if (iD.detect().opera) container.classed('opera', true);
31330 var hash = iD.behavior.Hash(context);
31334 if (!hash.hadHash) {
31335 map.centerZoom([0, 0], 2);
31338 container.append('svg')
31339 .attr('id', 'defs')
31340 .call(iD.svg.Defs(context));
31342 container.append('div')
31343 .attr('id', 'sidebar')
31344 .attr('class', 'col4')
31347 var content = container.append('div')
31348 .attr('id', 'content');
31350 var bar = content.append('div')
31352 .attr('class', 'fillD');
31354 var m = content.append('div')
31358 content.append('div')
31359 .attr('class', 'map-in-map')
31360 .style('display', 'none')
31361 .call(iD.ui.MapInMap(context));
31363 content.append('div')
31364 .attr('class', 'infobox fillD2')
31365 .style('display', 'none')
31366 .call(iD.ui.Info(context));
31369 .attr('class', 'spacer col4');
31371 var limiter = bar.append('div')
31372 .attr('class', 'limiter');
31374 limiter.append('div')
31375 .attr('class', 'button-wrap joined col3')
31376 .call(iD.ui.Modes(context), limiter);
31378 limiter.append('div')
31379 .attr('class', 'button-wrap joined col1')
31380 .call(iD.ui.UndoRedo(context));
31382 limiter.append('div')
31383 .attr('class', 'button-wrap col1')
31384 .call(iD.ui.Save(context));
31387 .attr('class', 'full-screen')
31388 .call(iD.ui.FullScreen(context));
31391 .attr('class', 'spinner')
31392 .call(iD.ui.Spinner(context));
31394 var controls = bar.append('div')
31395 .attr('class', 'map-controls');
31397 controls.append('div')
31398 .attr('class', 'map-control zoombuttons')
31399 .call(iD.ui.Zoom(context));
31401 controls.append('div')
31402 .attr('class', 'map-control geolocate-control')
31403 .call(iD.ui.Geolocate(map));
31405 controls.append('div')
31406 .attr('class', 'map-control background-control')
31407 .call(iD.ui.Background(context));
31409 controls.append('div')
31410 .attr('class', 'map-control map-data-control')
31411 .call(iD.ui.MapData(context));
31413 controls.append('div')
31414 .attr('class', 'map-control help-control')
31415 .call(iD.ui.Help(context));
31417 var about = content.append('div')
31418 .attr('id', 'about');
31420 about.append('div')
31421 .attr('id', 'attrib')
31422 .call(iD.ui.Attribution(context));
31424 var footer = about.append('div')
31425 .attr('id', 'footer')
31426 .attr('class', 'fillD');
31428 footer.append('div')
31429 .attr('class', 'api-status')
31430 .call(iD.ui.Status(context));
31432 footer.append('div')
31433 .attr('id', 'scale-block')
31434 .call(iD.ui.Scale(context));
31436 var aboutList = footer.append('div')
31437 .attr('id', 'info-block')
31439 .attr('id', 'about-list');
31441 if (!context.embed()) {
31442 aboutList.call(iD.ui.Account(context));
31445 aboutList.append('li')
31447 .attr('target', '_blank')
31448 .attr('tabindex', -1)
31449 .attr('href', 'http://github.com/openstreetmap/iD')
31452 var issueLinks = aboutList.append('li');
31454 issueLinks.append('a')
31455 .attr('target', '_blank')
31456 .attr('tabindex', -1)
31457 .attr('href', 'https://github.com/openstreetmap/iD/issues')
31458 .call(iD.svg.Icon('#icon-bug', 'light'))
31459 .call(bootstrap.tooltip()
31460 .title(t('report_a_bug'))
31464 issueLinks.append('a')
31465 .attr('target', '_blank')
31466 .attr('tabindex', -1)
31467 .attr('href', 'https://github.com/openstreetmap/iD/blob/master/CONTRIBUTING.md#translating')
31468 .call(iD.svg.Icon('#icon-translate', 'light'))
31469 .call(bootstrap.tooltip()
31470 .title(t('help_translate'))
31474 aboutList.append('li')
31475 .attr('class', 'feature-warning')
31476 .attr('tabindex', -1)
31477 .call(iD.ui.FeatureInfo(context));
31479 aboutList.append('li')
31480 .attr('class', 'user-list')
31481 .attr('tabindex', -1)
31482 .call(iD.ui.Contributors(context));
31484 window.onbeforeunload = function() {
31485 return context.save();
31488 window.onunload = function() {
31489 context.history().unlock();
31492 var mapDimensions = map.dimensions();
31494 d3.select(window).on('resize.editor', function() {
31495 mapDimensions = m.dimensions();
31496 map.dimensions(m.dimensions());
31500 return function() {
31501 d3.event.preventDefault();
31502 if (!context.inIntro()) context.pan(d);
31509 var keybinding = d3.keybinding('main')
31510 .on('⌫', function() { d3.event.preventDefault(); })
31511 .on('←', pan([pa, 0]))
31512 .on('↑', pan([0, pa]))
31513 .on('→', pan([-pa, 0]))
31514 .on('↓', pan([0, -pa]))
31515 .on('⇧←', pan([mapDimensions[0], 0]))
31516 .on('⇧↑', pan([0, mapDimensions[1]]))
31517 .on('⇧→', pan([-mapDimensions[0], 0]))
31518 .on('⇧↓', pan([0, -mapDimensions[1]]))
31519 .on(iD.ui.cmd('⌘←'), pan([mapDimensions[0], 0]))
31520 .on(iD.ui.cmd('⌘↑'), pan([0, mapDimensions[1]]))
31521 .on(iD.ui.cmd('⌘→'), pan([-mapDimensions[0], 0]))
31522 .on(iD.ui.cmd('⌘↓'), pan([0, -mapDimensions[1]]));
31524 d3.select(document)
31527 context.enter(iD.modes.Browse(context));
31529 context.container()
31530 .call(iD.ui.Splash(context))
31531 .call(iD.ui.Restore(context));
31533 var authenticating = iD.ui.Loading(context)
31534 .message(t('loading_auth'));
31536 context.connection()
31537 .on('authenticating.ui', function() {
31538 context.container()
31539 .call(authenticating);
31541 .on('authenticated.ui', function() {
31542 authenticating.close();
31546 function ui(container) {
31547 context.container(container);
31548 context.loadLocale(function() {
31553 ui.sidebar = iD.ui.Sidebar(context);
31558 iD.ui.tooltipHtml = function(text, key) {
31559 var s = '<span>' + text + '</span>';
31561 s += '<div class="keyhint-wrap">' +
31562 '<span> ' + (t('tooltip_keyhint')) + ' </span>' +
31563 '<span class="keyhint"> ' + key + '</span></div>';
31567 iD.ui.Account = function(context) {
31568 var connection = context.connection();
31570 function update(selection) {
31571 if (!connection.authenticated()) {
31572 selection.selectAll('#userLink, #logoutLink')
31573 .classed('hide', true);
31577 connection.userDetails(function(err, details) {
31578 var userLink = selection.select('#userLink'),
31579 logoutLink = selection.select('#logoutLink');
31582 logoutLink.html('');
31586 selection.selectAll('#userLink, #logoutLink')
31587 .classed('hide', false);
31590 userLink.append('a')
31591 .attr('href', connection.userURL(details.display_name))
31592 .attr('target', '_blank');
31594 // Add thumbnail or dont
31595 if (details.image_url) {
31596 userLink.append('img')
31597 .attr('class', 'icon pre-text user-icon')
31598 .attr('src', details.image_url);
31601 .call(iD.svg.Icon('#icon-avatar', 'pre-text light'));
31605 userLink.append('span')
31606 .attr('class', 'label')
31607 .text(details.display_name);
31609 logoutLink.append('a')
31610 .attr('class', 'logout')
31613 .on('click.logout', function() {
31614 d3.event.preventDefault();
31615 connection.logout();
31620 return function(selection) {
31621 selection.append('li')
31622 .attr('id', 'logoutLink')
31623 .classed('hide', true);
31625 selection.append('li')
31626 .attr('id', 'userLink')
31627 .classed('hide', true);
31629 connection.on('auth.account', function() { update(selection); });
31633 iD.ui.Attribution = function(context) {
31636 function attribution(data, klass) {
31637 var div = selection.selectAll('.' + klass)
31642 .attr('class', klass);
31644 var background = div.selectAll('.attribution')
31645 .data(data, function(d) { return d.name(); });
31649 .attr('class', 'attribution')
31650 .each(function(d) {
31651 if (d.terms_html) {
31653 .html(d.terms_html);
31657 var source = d.terms_text || d.id || d.name();
31660 source = '<img class="source-image" src="' + context.imagePath(d.logo) + '">';
31666 .attr('href', d.terms_url)
31667 .attr('target', '_blank')
31678 var copyright = background.selectAll('.copyright-notice')
31679 .data(function(d) {
31680 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
31681 return notice ? [notice] : [];
31686 .attr('class', 'copyright-notice');
31688 copyright.text(String);
31694 function update() {
31695 attribution([context.background().baseLayerSource()], 'base-layer-attribution');
31696 attribution(context.background().overlayLayerSources().filter(function (s) {
31697 return s.validZoom(context.map().zoom());
31698 }), 'overlay-layer-attribution');
31701 return function(select) {
31702 selection = select;
31704 context.background()
31705 .on('change.attribution', update);
31708 .on('move.attribution', _.throttle(update, 400, {leading: false}));
31713 iD.ui.Background = function(context) {
31715 opacities = [1, 0.75, 0.5, 0.25],
31719 ['right', [-1, 0]],
31720 ['bottom', [0, 1]]],
31721 opacityDefault = (context.storage('background-opacity') !== null) ?
31722 (+context.storage('background-opacity')) : 1.0,
31723 customTemplate = context.storage('background-custom-template') || '';
31725 // Can be 0 from <1.3.0 use or due to issue #1923.
31726 if (opacityDefault === 0) opacityDefault = 1.0;
31728 function background(selection) {
31730 function sortSources(a, b) {
31731 return a.best() ? -1
31733 : a.id === 'none' ? 1
31734 : b.id === 'none' ? -1
31735 : d3.ascending(a, b);
31738 function setOpacity(d) {
31739 var bg = context.container().selectAll('.background-layer')
31741 .style('opacity', d)
31742 .attr('data-opacity', d);
31744 if (!iD.detect().opera) {
31745 iD.util.setTransform(bg, 0, 0);
31748 opacityList.selectAll('li')
31749 .classed('active', function(_) { return _ === d; });
31751 context.storage('background-opacity', d);
31754 function selectLayer() {
31755 function active(d) {
31756 return context.background().showsLayer(d);
31759 content.selectAll('.layer, .custom_layer')
31760 .classed('active', active)
31761 .selectAll('input')
31762 .property('checked', active);
31765 function clickSetSource(d) {
31766 d3.event.preventDefault();
31767 context.background().baseLayerSource(d);
31771 function editCustom() {
31772 d3.event.preventDefault();
31773 var template = window.prompt(t('background.custom_prompt'), customTemplate);
31775 template.indexOf('google.com') !== -1 ||
31776 template.indexOf('googleapis.com') !== -1 ||
31777 template.indexOf('google.ru') !== -1) {
31781 setCustom(template);
31784 function setCustom(template) {
31785 context.background().baseLayerSource(iD.BackgroundSource.Custom(template));
31787 context.storage('background-custom-template', template);
31790 function clickSetOverlay(d) {
31791 d3.event.preventDefault();
31792 context.background().toggleOverlayLayer(d);
31796 function drawList(layerList, type, change, filter) {
31797 var sources = context.background()
31798 .sources(context.map().extent())
31801 var layerLinks = layerList.selectAll('li.layer')
31802 .data(sources, function(d) { return d.name(); })
31803 .sort(sortSources);
31805 var enter = layerLinks.enter()
31806 .insert('li', '.custom_layer')
31807 .attr('class', 'layer');
31809 // only set tooltips for layers with tooltips
31810 enter.filter(function(d) { return d.description; })
31811 .call(bootstrap.tooltip()
31812 .title(function(d) { return d.description; })
31813 .placement('top'));
31815 var label = enter.append('label');
31817 label.append('input')
31818 .attr('type', type)
31819 .attr('name', 'layers')
31820 .on('change', change);
31822 label.append('span')
31823 .text(function(d) { return d.name(); });
31828 layerList.style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
31831 function update() {
31832 backgroundList.call(drawList, 'radio', clickSetSource, function(d) { return !d.overlay; });
31833 overlayList.call(drawList, 'checkbox', clickSetOverlay, function(d) { return d.overlay; });
31837 var source = context.background().baseLayerSource();
31838 if (source.id === 'custom') {
31839 customTemplate = source.template;
31843 function clickNudge(d) {
31844 var timeout = window.setTimeout(function() {
31845 interval = window.setInterval(nudge, 100);
31849 d3.select(this).on('mouseup', function() {
31850 window.clearInterval(interval);
31851 window.clearTimeout(timeout);
31856 var offset = context.background()
31857 .nudge(d[1], context.map().zoom())
31859 resetButton.classed('disabled', offset[0] === 0 && offset[1] === 0);
31863 function hide() { setVisible(false); }
31865 function toggle() {
31866 if (d3.event) d3.event.preventDefault();
31867 tooltip.hide(button);
31868 setVisible(!button.classed('active'));
31871 function setVisible(show) {
31872 if (show !== shown) {
31873 button.classed('active', show);
31877 selection.on('mousedown.background-inside', function() {
31878 return d3.event.stopPropagation();
31880 content.style('display', 'block')
31881 .style('right', '-300px')
31884 .style('right', '0px');
31886 content.style('display', 'block')
31887 .style('right', '0px')
31890 .style('right', '-300px')
31891 .each('end', function() {
31892 d3.select(this).style('display', 'none');
31894 selection.on('mousedown.background-inside', null);
31900 var content = selection.append('div')
31901 .attr('class', 'fillL map-overlay col3 content hide'),
31902 tooltip = bootstrap.tooltip()
31905 .title(iD.ui.tooltipHtml(t('background.description'), key)),
31906 button = selection.append('button')
31907 .attr('tabindex', -1)
31908 .on('click', toggle)
31909 .call(iD.svg.Icon('#icon-layers', 'light'))
31913 var opa = content.append('div')
31914 .attr('class', 'opacity-options-wrapper');
31917 .text(t('background.title'));
31919 var opacityList = opa.append('ul')
31920 .attr('class', 'opacity-options');
31922 opacityList.selectAll('div.opacity')
31926 .attr('data-original-title', function(d) {
31927 return t('background.percent_brightness', { opacity: (d * 100) });
31929 .on('click.set-opacity', setOpacity)
31930 .html('<div class="select-box"></div>')
31931 .call(bootstrap.tooltip()
31932 .placement('left'))
31934 .attr('class', 'opacity')
31935 .style('opacity', function(d) { return 1.25 - d; });
31937 var backgroundList = content.append('ul')
31938 .attr('class', 'layer-list');
31940 var custom = backgroundList.append('li')
31941 .attr('class', 'custom_layer')
31942 .datum(iD.BackgroundSource.Custom());
31944 custom.append('button')
31945 .attr('class', 'layer-browse')
31946 .call(bootstrap.tooltip()
31947 .title(t('background.custom_button'))
31948 .placement('left'))
31949 .on('click', editCustom)
31950 .call(iD.svg.Icon('#icon-search'));
31952 var label = custom.append('label');
31954 label.append('input')
31955 .attr('type', 'radio')
31956 .attr('name', 'layers')
31957 .on('change', function () {
31958 if (customTemplate) {
31959 setCustom(customTemplate);
31965 label.append('span')
31966 .text(t('background.custom'));
31968 var overlayList = content.append('ul')
31969 .attr('class', 'layer-list');
31971 var controls = content.append('div')
31972 .attr('class', 'controls-list');
31974 var minimapLabel = controls
31976 .call(bootstrap.tooltip()
31978 .title(iD.ui.tooltipHtml(t('background.minimap.tooltip'), '/'))
31982 minimapLabel.classed('minimap-toggle', true)
31984 .attr('type', 'checkbox')
31985 .on('change', function() {
31986 iD.ui.MapInMap.toggle();
31987 d3.event.preventDefault();
31990 minimapLabel.append('span')
31991 .text(t('background.minimap.description'));
31993 var adjustments = content.append('div')
31994 .attr('class', 'adjustments');
31996 adjustments.append('a')
31997 .text(t('background.fix_misalignment'))
31999 .classed('hide-toggle', true)
32000 .classed('expanded', false)
32001 .on('click', function() {
32002 var exp = d3.select(this).classed('expanded');
32003 nudgeContainer.style('display', exp ? 'none' : 'block');
32004 d3.select(this).classed('expanded', !exp);
32005 d3.event.preventDefault();
32008 var nudgeContainer = adjustments.append('div')
32009 .attr('class', 'nudge-container cf')
32010 .style('display', 'none');
32012 nudgeContainer.selectAll('button')
32013 .data(directions).enter()
32015 .attr('class', function(d) { return d[0] + ' nudge'; })
32016 .on('mousedown', clickNudge);
32018 var resetButton = nudgeContainer
32020 .attr('class', 'reset disabled')
32021 .on('click', function () {
32022 context.background().offset([0, 0]);
32023 resetButton.classed('disabled', true);
32025 .call(iD.svg.Icon('#icon-undo'));
32028 .on('move.background-update', _.debounce(update, 1000));
32030 context.background()
32031 .on('change.background-update', update);
32034 setOpacity(opacityDefault);
32036 var keybinding = d3.keybinding('background')
32041 d3.select(document)
32044 context.surface().on('mousedown.background-outside', hide);
32045 context.container().on('mousedown.background-outside', hide);
32050 // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
32051 // For example, ⌘Z -> Ctrl+Z
32052 iD.ui.cmd = function(code) {
32053 if (iD.detect().os === 'mac') {
32057 if (iD.detect().os === 'win') {
32058 if (code === '⌘⇧Z') return 'Ctrl+Y';
32070 for (var i = 0; i < code.length; i++) {
32071 if (code[i] in replacements) {
32072 result += replacements[code[i]] + '+';
32080 iD.ui.Commit = function(context) {
32081 var dispatch = d3.dispatch('cancel', 'save');
32083 function commit(selection) {
32084 var changes = context.history().changes(),
32085 summary = context.history().difference().summary();
32087 function zoomToEntity(change) {
32088 var entity = change.entity;
32089 if (change.changeType !== 'deleted' &&
32090 context.graph().entity(entity.id).geometry(context.graph()) !== 'vertex') {
32091 context.map().zoomTo(entity);
32092 context.surface().selectAll(
32093 iD.util.entityOrMemberSelector([entity.id], context.graph()))
32094 .classed('hover', true);
32098 var header = selection.append('div')
32099 .attr('class', 'header fillL');
32101 header.append('h3')
32102 .text(t('commit.title'));
32104 var body = selection.append('div')
32105 .attr('class', 'body');
32109 var commentSection = body.append('div')
32110 .attr('class', 'modal-section form-field commit-form');
32112 commentSection.append('label')
32113 .attr('class', 'form-label')
32114 .text(t('commit.message_label'));
32116 var commentField = commentSection.append('textarea')
32117 .attr('placeholder', t('commit.description_placeholder'))
32118 .attr('maxlength', 255)
32119 .property('value', context.storage('comment') || '')
32120 .on('input.save', function() {
32121 d3.selectAll('.save-section .save-button')
32122 .attr('disabled', (this.value.length ? null : true));
32124 .on('blur.save', function() {
32125 context.storage('comment', this.value);
32128 commentField.node().select();
32132 var warnings = body.selectAll('div.warning-section')
32133 .data([context.history().validate(changes)])
32136 .attr('class', 'modal-section warning-section fillL2')
32137 .style('display', function(d) { return _.isEmpty(d) ? 'none' : null; })
32138 .style('background', '#ffb');
32140 warnings.append('h3')
32141 .text(t('commit.warnings'));
32143 var warningLi = warnings.append('ul')
32144 .attr('class', 'changeset-list')
32146 .data(function(d) { return d; })
32150 .on('mouseover', mouseover)
32151 .on('mouseout', mouseout)
32152 .on('click', warningClick);
32155 .call(iD.svg.Icon('#icon-alert', 'pre-text'));
32158 .append('strong').text(function(d) {
32162 warningLi.filter(function(d) { return d.tooltip; })
32163 .call(bootstrap.tooltip()
32164 .title(function(d) { return d.tooltip; })
32169 // Upload Explanation
32170 var saveSection = body.append('div')
32171 .attr('class','modal-section save-section fillL cf');
32173 var prose = saveSection.append('p')
32174 .attr('class', 'commit-info')
32175 .html(t('commit.upload_explanation'));
32177 context.connection().userDetails(function(err, user) {
32180 var userLink = d3.select(document.createElement('div'));
32182 if (user.image_url) {
32183 userLink.append('img')
32184 .attr('src', user.image_url)
32185 .attr('class', 'icon pre-text user-icon');
32188 userLink.append('a')
32189 .attr('class','user-info')
32190 .text(user.display_name)
32191 .attr('href', context.connection().userURL(user.display_name))
32192 .attr('tabindex', -1)
32193 .attr('target', '_blank');
32195 prose.html(t('commit.upload_explanation_with_user', {user: userLink.html()}));
32200 var buttonSection = saveSection.append('div')
32201 .attr('class','buttons fillL cf');
32203 var saveButton = buttonSection.append('button')
32204 .attr('class', 'action col5 button save-button')
32205 .attr('disabled', function() {
32206 var n = d3.select('.commit-form textarea').node();
32207 return (n && n.value.length) ? null : true;
32209 .on('click.save', function() {
32211 comment: commentField.node().value
32215 saveButton.append('span')
32216 .attr('class', 'label')
32217 .text(t('commit.save'));
32219 var cancelButton = buttonSection.append('button')
32220 .attr('class', 'action col5 button cancel-button')
32221 .on('click.cancel', function() { dispatch.cancel(); });
32223 cancelButton.append('span')
32224 .attr('class', 'label')
32225 .text(t('commit.cancel'));
32229 var changeSection = body.selectAll('div.commit-section')
32233 .attr('class', 'commit-section modal-section fillL2');
32235 changeSection.append('h3')
32236 .text(t('commit.changes', {count: summary.length}));
32238 var li = changeSection.append('ul')
32239 .attr('class', 'changeset-list')
32244 .on('mouseover', mouseover)
32245 .on('mouseout', mouseout)
32246 .on('click', zoomToEntity);
32248 li.each(function(d) {
32250 .call(iD.svg.Icon('#icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
32254 .attr('class', 'change-type')
32255 .text(function(d) {
32256 return t('commit.' + d.changeType) + ' ';
32259 li.append('strong')
32260 .attr('class', 'entity-type')
32261 .text(function(d) {
32262 return context.presets().match(d.entity, d.graph).name();
32266 .attr('class', 'entity-name')
32267 .text(function(d) {
32268 var name = iD.util.displayName(d.entity) || '',
32270 if (name !== '') string += ':';
32271 return string += ' ' + name;
32274 li.style('opacity', 0)
32276 .style('opacity', 1);
32278 li.style('opacity', 0)
32280 .style('opacity', 1);
32282 function mouseover(d) {
32284 context.surface().selectAll(
32285 iD.util.entityOrMemberSelector([d.entity.id], context.graph())
32286 ).classed('hover', true);
32290 function mouseout() {
32291 context.surface().selectAll('.hover')
32292 .classed('hover', false);
32295 function warningClick(d) {
32297 context.map().zoomTo(d.entity);
32299 iD.modes.Select(context, [d.entity.id])
32300 .suppressMenu(true));
32305 return d3.rebind(commit, dispatch, 'on');
32307 iD.ui.confirm = function(selection) {
32308 var modal = iD.ui.modal(selection);
32310 modal.select('.modal')
32311 .classed('modal-alert', true);
32313 var section = modal.select('.content');
32315 section.append('div')
32316 .attr('class', 'modal-section header');
32318 section.append('div')
32319 .attr('class', 'modal-section message-text');
32321 var buttons = section.append('div')
32322 .attr('class', 'modal-section buttons cf');
32324 modal.okButton = function() {
32327 .attr('class', 'action col4')
32328 .on('click.confirm', function() {
32331 .text(t('confirm.okay'));
32338 iD.ui.Conflicts = function(context) {
32339 var dispatch = d3.dispatch('download', 'cancel', 'save'),
32342 function conflicts(selection) {
32343 var header = selection
32345 .attr('class', 'header fillL');
32349 .attr('class', 'fr')
32350 .on('click', function() { dispatch.cancel(); })
32351 .call(iD.svg.Icon('#icon-close'));
32355 .text(t('save.conflict.header'));
32357 var body = selection
32359 .attr('class', 'body fillL');
32363 .attr('class', 'conflicts-help')
32364 .text(t('save.conflict.help'))
32366 .attr('class', 'conflicts-download')
32367 .text(t('save.conflict.download_changes'))
32368 .on('click.download', function() { dispatch.download(); });
32372 .attr('class', 'conflict-container fillL3')
32373 .call(showConflict, 0);
32377 .attr('class', 'conflicts-done')
32378 .attr('opacity', 0)
32379 .style('display', 'none')
32380 .text(t('save.conflict.done'));
32384 .attr('class','buttons col12 joined conflicts-buttons');
32388 .attr('disabled', list.length > 1)
32389 .attr('class', 'action conflicts-button col6')
32390 .text(t('save.title'))
32391 .on('click.try_again', function() { dispatch.save(); });
32395 .attr('class', 'secondary-action conflicts-button col6')
32396 .text(t('confirm.cancel'))
32397 .on('click.cancel', function() { dispatch.cancel(); });
32401 function showConflict(selection, index) {
32402 if (index < 0 || index >= list.length) return;
32404 var parent = d3.select(selection.node().parentElement);
32406 // enable save button if this is the last conflict being reviewed..
32407 if (index === list.length - 1) {
32408 window.setTimeout(function() {
32409 parent.select('.conflicts-button')
32410 .attr('disabled', null);
32412 parent.select('.conflicts-done')
32414 .attr('opacity', 1)
32415 .style('display', 'block');
32419 var item = selection
32420 .selectAll('.conflict')
32421 .data([list[index]]);
32423 var enter = item.enter()
32425 .attr('class', 'conflict');
32429 .attr('class', 'conflict-count')
32430 .text(t('save.conflict.count', { num: index + 1, total: list.length }));
32434 .attr('class', 'conflict-description')
32436 .text(function(d) { return d.name; })
32437 .on('click', function(d) {
32438 zoomToEntity(d.id);
32439 d3.event.preventDefault();
32442 var details = enter
32444 .attr('class', 'conflict-detail-container');
32448 .attr('class', 'conflict-detail-list')
32450 .data(function(d) { return d.details || []; })
32453 .attr('class', 'conflict-detail-item')
32454 .html(function(d) { return d; });
32458 .attr('class', 'conflict-choices')
32463 .attr('class', 'conflict-nav-buttons joined cf')
32464 .selectAll('button')
32465 .data(['previous', 'next'])
32468 .text(function(d) { return t('save.conflict.' + d); })
32469 .attr('class', 'conflict-nav-button action col6')
32470 .attr('disabled', function(d, i) {
32471 return (i === 0 && index === 0) ||
32472 (i === 1 && index === list.length - 1) || null;
32474 .on('click', function(d, i) {
32475 var container = parent.select('.conflict-container'),
32476 sign = (i === 0 ? -1 : 1);
32479 .selectAll('.conflict')
32483 .call(showConflict, index + sign);
32485 d3.event.preventDefault();
32493 function addChoices(selection) {
32494 var choices = selection
32496 .attr('class', 'layer-list')
32498 .data(function(d) { return d.choices || []; });
32500 var enter = choices.enter()
32502 .attr('class', 'layer');
32509 .attr('type', 'radio')
32510 .attr('name', function(d) { return d.id; })
32511 .on('change', function(d, i) {
32512 var ul = this.parentElement.parentElement.parentElement;
32513 ul.__data__.chosen = i;
32519 .text(function(d) { return d.text; });
32522 .each(function(d, i) {
32523 var ul = this.parentElement;
32524 if (ul.__data__.chosen === i) choose(ul, d);
32528 function choose(ul, datum) {
32529 if (d3.event) d3.event.preventDefault();
32533 .classed('active', function(d) { return d === datum; })
32534 .selectAll('input')
32535 .property('checked', function(d) { return d === datum; });
32537 var extent = iD.geo.Extent(),
32540 entity = context.graph().hasEntity(datum.id);
32541 if (entity) extent._extend(entity.extent(context.graph()));
32545 entity = context.graph().hasEntity(datum.id);
32546 if (entity) extent._extend(entity.extent(context.graph()));
32548 zoomToEntity(datum.id, extent);
32551 function zoomToEntity(id, extent) {
32552 context.surface().selectAll('.hover')
32553 .classed('hover', false);
32555 var entity = context.graph().hasEntity(id);
32558 context.map().trimmedExtent(extent);
32560 context.map().zoomTo(entity);
32562 context.surface().selectAll(
32563 iD.util.entityOrMemberSelector([entity.id], context.graph()))
32564 .classed('hover', true);
32569 // The conflict list should be an array of objects like:
32572 // name: entityName(local),
32573 // details: merge.conflicts(),
32576 // choice(id, keepMine, forceLocal),
32577 // choice(id, keepTheirs, forceRemote)
32580 conflicts.list = function(_) {
32581 if (!arguments.length) return list;
32586 return d3.rebind(conflicts, dispatch, 'on');
32588 iD.ui.Contributors = function(context) {
32589 function update(selection) {
32592 entities = context.intersects(context.map().extent());
32594 entities.forEach(function(entity) {
32595 if (entity && entity.user) users[entity.user] = true;
32598 var u = Object.keys(users),
32599 subset = u.slice(0, u.length > limit ? limit - 1 : limit);
32602 .call(iD.svg.Icon('#icon-nearby', 'pre-text light'));
32604 var userList = d3.select(document.createElement('span'));
32606 userList.selectAll()
32610 .attr('class', 'user-link')
32611 .attr('href', function(d) { return context.connection().userURL(d); })
32612 .attr('target', '_blank')
32613 .attr('tabindex', -1)
32616 if (u.length > limit) {
32617 var count = d3.select(document.createElement('span'));
32620 .attr('target', '_blank')
32621 .attr('tabindex', -1)
32622 .attr('href', function() {
32623 return context.connection().changesetsURL(context.map().center(), context.map().zoom());
32625 .text(u.length - limit + 1);
32627 selection.append('span')
32628 .html(t('contributors.truncated_list', {users: userList.html(), count: count.html()}));
32630 selection.append('span')
32631 .html(t('contributors.list', {users: userList.html()}));
32635 selection.transition().style('opacity', 0);
32636 } else if (selection.style('opacity') === '0') {
32637 selection.transition().style('opacity', 1);
32641 return function(selection) {
32644 context.connection().on('loaded.contributors', function() {
32648 context.map().on('move.contributors', _.debounce(function() {
32653 iD.ui.Disclosure = function() {
32654 var dispatch = d3.dispatch('toggled'),
32657 content = function () {};
32659 var disclosure = function(selection) {
32660 var $link = selection.selectAll('.hide-toggle')
32663 $link.enter().append('a')
32665 .attr('class', 'hide-toggle');
32668 .on('click', toggle)
32669 .classed('expanded', expanded);
32671 var $body = selection.selectAll('div')
32674 $body.enter().append('div');
32676 $body.classed('hide', !expanded)
32679 function toggle() {
32680 expanded = !expanded;
32681 $link.classed('expanded', expanded);
32682 $body.call(iD.ui.Toggle(expanded));
32683 dispatch.toggled(expanded);
32687 disclosure.title = function(_) {
32688 if (!arguments.length) return title;
32693 disclosure.expanded = function(_) {
32694 if (!arguments.length) return expanded;
32699 disclosure.content = function(_) {
32700 if (!arguments.length) return content;
32705 return d3.rebind(disclosure, dispatch, 'on');
32707 iD.ui.EntityEditor = function(context) {
32708 var dispatch = d3.dispatch('choose'),
32710 coalesceChanges = false,
32717 var presetEditor = iD.ui.preset(context)
32718 .on('change', changeTags);
32719 var rawTagEditor = iD.ui.RawTagEditor(context)
32720 .on('change', changeTags);
32722 function entityEditor(selection) {
32723 var entity = context.entity(id),
32724 tags = _.clone(entity.tags);
32726 var $header = selection.selectAll('.header')
32730 var $enter = $header.enter().append('div')
32731 .attr('class', 'header fillL cf');
32733 $enter.append('button')
32734 .attr('class', 'fl preset-reset preset-choose')
32738 $enter.append('button')
32739 .attr('class', 'fr preset-close')
32740 .call(iD.svg.Icon(modified ? '#icon-apply' : '#icon-close'));
32742 $enter.append('h3');
32745 $header.select('h3')
32746 .text(t('inspector.edit'));
32748 $header.select('.preset-close')
32749 .on('click', function() {
32750 context.enter(iD.modes.Browse(context));
32753 var $body = selection.selectAll('.inspector-body')
32757 $enter = $body.enter().append('div')
32758 .attr('class', 'inspector-body');
32760 $enter.append('div')
32761 .attr('class', 'preset-list-item inspector-inner')
32763 .attr('class', 'preset-list-button-wrap')
32765 .attr('class', 'preset-list-button preset-reset')
32766 .call(bootstrap.tooltip()
32767 .title(t('inspector.back_tooltip'))
32768 .placement('bottom'))
32770 .attr('class', 'label');
32772 $body.select('.preset-list-button-wrap')
32773 .call(reference.button);
32775 $body.select('.preset-list-item')
32776 .call(reference.body);
32778 $enter.append('div')
32779 .attr('class', 'inspector-border inspector-preset');
32781 $enter.append('div')
32782 .attr('class', 'inspector-border raw-tag-editor inspector-inner');
32784 $enter.append('div')
32785 .attr('class', 'inspector-border raw-member-editor inspector-inner');
32787 $enter.append('div')
32788 .attr('class', 'raw-membership-editor inspector-inner');
32790 selection.selectAll('.preset-reset')
32791 .on('click', function() {
32792 dispatch.choose(preset);
32796 $body.select('.preset-list-item button')
32797 .call(iD.ui.PresetIcon()
32798 .geometry(context.geometry(id))
32801 $body.select('.preset-list-item .label')
32802 .text(preset.name());
32804 $body.select('.inspector-preset')
32811 $body.select('.raw-tag-editor')
32818 if (entity.type === 'relation') {
32819 $body.select('.raw-member-editor')
32820 .style('display', 'block')
32821 .call(iD.ui.RawMemberEditor(context)
32824 $body.select('.raw-member-editor')
32825 .style('display', 'none');
32828 $body.select('.raw-membership-editor')
32829 .call(iD.ui.RawMembershipEditor(context)
32832 function historyChanged() {
32833 if (state === 'hide') return;
32835 var entity = context.hasEntity(id);
32836 if (!entity) return;
32838 entityEditor.preset(context.presets().match(entity, context.graph()));
32840 var head = context.history().difference();
32841 entityEditor.modified(base && !_.isEqual(base.changes(), head.changes()));
32843 entityEditor(selection);
32847 .on('change.entity-editor', historyChanged);
32850 function clean(o) {
32852 function cleanVal(k, v) {
32853 function keepSpaces(k) {
32854 var whitelist = ['opening_hours', 'service_times', 'collection_times',
32855 'operating_times', 'smoking_hours', 'happy_hours'];
32856 return _.any(whitelist, function(s) { return k.indexOf(s) !== -1; });
32859 var blacklist = ['description', 'note', 'fixme'];
32860 if (_.any(blacklist, function(s) { return k.indexOf(s) !== -1; })) return v;
32862 var cleaned = v.split(';')
32863 .map(function(s) { return s.trim(); })
32864 .join(keepSpaces(k) ? '; ' : ';');
32866 // The code below is not intended to validate websites and emails.
32867 // It is only intended to prevent obvious copy-paste errors. (#2323)
32869 // clean website-like tags
32870 if (k.indexOf('website') !== -1 || cleaned.indexOf('http') === 0) {
32872 .replace(/[\u200B-\u200F\uFEFF]/g, '') // strip LRM and other zero width chars
32873 .replace(/[^\w\+\-\.\/\?\[\]\(\)~!@#$%&*',:;=]/g, encodeURIComponent);
32875 // clean email-like tags
32876 } else if (k.indexOf('email') !== -1) {
32878 .replace(/[\u200B-\u200F\uFEFF]/g, '') // strip LRM and other zero width chars
32879 .replace(/[^\w\+\-\.\/\?\|~!@#$%^&*'`{};=]/g, ''); // note: ';' allowed as OSM delimiter
32885 var out = {}, k, v;
32887 if (k && (v = o[k]) !== undefined) {
32888 out[k] = cleanVal(k, v);
32894 // Tag changes that fire on input can all get coalesced into a single
32895 // history operation when the user leaves the field. #2342
32896 function changeTags(changed, onInput) {
32897 var entity = context.entity(id),
32898 annotation = t('operations.change_tags.annotation'),
32899 tags = clean(_.extend({}, entity.tags, changed));
32901 if (!_.isEqual(entity.tags, tags)) {
32902 if (coalesceChanges) {
32903 context.overwrite(iD.actions.ChangeTags(id, tags), annotation);
32905 context.perform(iD.actions.ChangeTags(id, tags), annotation);
32909 coalesceChanges = !!onInput;
32912 entityEditor.modified = function(_) {
32913 if (!arguments.length) return modified;
32915 d3.selectAll('button.preset-close use')
32916 .attr('xlink:href', (modified ? '#icon-apply' : '#icon-close'));
32919 entityEditor.state = function(_) {
32920 if (!arguments.length) return state;
32922 return entityEditor;
32925 entityEditor.entityID = function(_) {
32926 if (!arguments.length) return id;
32928 entityEditor.preset(context.presets().match(context.entity(id), context.graph()));
32929 entityEditor.modified(false);
32930 base = context.history().difference();
32931 coalesceChanges = false;
32932 return entityEditor;
32935 entityEditor.preset = function(_) {
32936 if (!arguments.length) return preset;
32937 if (_ !== preset) {
32939 reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context)
32942 return entityEditor;
32945 return d3.rebind(entityEditor, dispatch, 'on');
32947 iD.ui.FeatureInfo = function(context) {
32948 function update(selection) {
32949 var features = context.features(),
32950 stats = features.stats(),
32952 hiddenList = _.compact(_.map(features.hidden(), function(k) {
32955 return String(stats[k]) + ' ' + t('feature.' + k + '.description');
32959 selection.html('');
32961 if (hiddenList.length) {
32962 var tooltip = bootstrap.tooltip()
32965 .title(function() {
32966 return iD.ui.tooltipHtml(hiddenList.join('<br/>'));
32969 var warning = selection.append('a')
32971 .attr('tabindex', -1)
32972 .html(t('feature_info.hidden_warning', { count: count }))
32974 .on('click', function() {
32975 tooltip.hide(warning);
32976 // open map data panel?
32977 d3.event.preventDefault();
32982 .classed('hide', !hiddenList.length);
32985 return function(selection) {
32988 context.features().on('change.feature_info', function() {
32993 iD.ui.FeatureList = function(context) {
32994 var geocodeResults;
32996 function featureList(selection) {
32997 var header = selection.append('div')
32998 .attr('class', 'header fillL cf');
33000 header.append('h3')
33001 .text(t('inspector.feature_list'));
33003 function keypress() {
33004 var q = search.property('value'),
33005 items = list.selectAll('.feature-list-item');
33006 if (d3.event.keyCode === 13 && q.length && items.size()) {
33007 click(items.datum());
33011 function inputevent() {
33012 geocodeResults = undefined;
33016 var searchWrap = selection.append('div')
33017 .attr('class', 'search-header');
33019 var search = searchWrap.append('input')
33020 .attr('placeholder', t('inspector.search'))
33021 .attr('type', 'search')
33022 .on('keypress', keypress)
33023 .on('input', inputevent);
33026 .call(iD.svg.Icon('#icon-search', 'pre-text'));
33028 var listWrap = selection.append('div')
33029 .attr('class', 'inspector-body');
33031 var list = listWrap.append('div')
33032 .attr('class', 'feature-list cf');
33035 .on('exit.feature-list', clearSearch);
33037 .on('drawn.feature-list', mapDrawn);
33039 function clearSearch() {
33040 search.property('value', '');
33044 function mapDrawn(e) {
33050 function features() {
33053 graph = context.graph(),
33054 q = search.property('value').toLowerCase();
33056 if (!q) return result;
33058 var idMatch = q.match(/^([nwr])([0-9]+)$/);
33063 geometry: idMatch[1] === 'n' ? 'point' : idMatch[1] === 'w' ? 'line' : 'relation',
33064 type: idMatch[1] === 'n' ? t('inspector.node') : idMatch[1] === 'w' ? t('inspector.way') : t('inspector.relation'),
33069 var locationMatch = sexagesimal.pair(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
33071 if (locationMatch) {
33072 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
33076 type: t('inspector.location'),
33077 name: loc[0].toFixed(6) + ', ' + loc[1].toFixed(6),
33082 function addEntity(entity) {
33083 if (entity.id in entities || result.length > 200)
33086 entities[entity.id] = true;
33088 var name = iD.util.displayName(entity) || '';
33089 if (name.toLowerCase().indexOf(q) >= 0) {
33093 geometry: context.geometry(entity.id),
33094 type: context.presets().match(entity, graph).name(),
33099 graph.parentRelations(entity).forEach(function(parent) {
33104 var visible = context.surface().selectAll('.point, .line, .area')[0];
33105 for (var i = 0; i < visible.length && result.length <= 200; i++) {
33106 addEntity(visible[i].__data__);
33109 (geocodeResults || []).forEach(function(d) {
33110 // https://github.com/openstreetmap/iD/issues/1890
33111 if (d.osm_type && d.osm_id) {
33113 id: iD.Entity.id.fromOSM(d.osm_type, d.osm_id),
33114 geometry: d.osm_type === 'relation' ? 'relation' : d.osm_type === 'way' ? 'line' : 'point',
33115 type: d.type !== 'yes' ? (d.type.charAt(0).toUpperCase() + d.type.slice(1)).replace('_', ' ')
33116 : (d.class.charAt(0).toUpperCase() + d.class.slice(1)).replace('_', ' '),
33117 name: d.display_name,
33118 extent: new iD.geo.Extent(
33119 [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
33120 [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
33128 function drawList() {
33129 var value = search.property('value'),
33130 results = features();
33132 list.classed('filtered', value.length);
33134 var noResultsWorldwide = geocodeResults && geocodeResults.length === 0;
33136 var resultsIndicator = list.selectAll('.no-results-item')
33138 .enter().append('button')
33139 .property('disabled', true)
33140 .attr('class', 'no-results-item')
33141 .call(iD.svg.Icon('#icon-alert', 'pre-text'));
33143 resultsIndicator.append('span')
33144 .attr('class', 'entity-name');
33146 list.selectAll('.no-results-item .entity-name')
33147 .text(noResultsWorldwide ? t('geocoder.no_results_worldwide') : t('geocoder.no_results_visible'));
33149 list.selectAll('.geocode-item')
33151 .enter().append('button')
33152 .attr('class', 'geocode-item')
33153 .on('click', geocode)
33155 .attr('class', 'label')
33157 .attr('class', 'entity-name')
33158 .text(t('geocoder.search'));
33160 list.selectAll('.no-results-item')
33161 .style('display', (value.length && !results.length) ? 'block' : 'none');
33163 list.selectAll('.geocode-item')
33164 .style('display', (value && geocodeResults === undefined) ? 'block' : 'none');
33166 list.selectAll('.feature-list-item')
33170 var items = list.selectAll('.feature-list-item')
33171 .data(results, function(d) { return d.id; });
33173 var enter = items.enter()
33174 .insert('button', '.geocode-item')
33175 .attr('class', 'feature-list-item')
33176 .on('mouseover', mouseover)
33177 .on('mouseout', mouseout)
33178 .on('click', click);
33182 .attr('class', 'label');
33184 label.each(function(d) {
33186 .call(iD.svg.Icon('#icon-' + d.geometry, 'pre-text'));
33189 label.append('span')
33190 .attr('class', 'entity-type')
33191 .text(function(d) { return d.type; });
33193 label.append('span')
33194 .attr('class', 'entity-name')
33195 .text(function(d) { return d.name; });
33197 enter.style('opacity', 0)
33199 .style('opacity', 1);
33207 function mouseover(d) {
33208 if (d.id === -1) return;
33210 context.surface().selectAll(iD.util.entityOrMemberSelector([d.id], context.graph()))
33211 .classed('hover', true);
33214 function mouseout() {
33215 context.surface().selectAll('.hover')
33216 .classed('hover', false);
33219 function click(d) {
33220 d3.event.preventDefault();
33222 context.map().centerZoom([d.location[1], d.location[0]], 20);
33224 else if (d.entity) {
33225 if (d.entity.type === 'node') {
33226 context.map().center(d.entity.loc);
33227 } else if (d.entity.type === 'way') {
33228 var center = context.projection(context.map().center()),
33229 edge = iD.geo.chooseEdge(context.childNodes(d.entity), center, context.projection);
33230 context.map().center(edge.loc);
33232 context.enter(iD.modes.Select(context, [d.entity.id]).suppressMenu(true));
33234 context.zoomToEntity(d.id);
33238 function geocode() {
33239 var searchVal = encodeURIComponent(search.property('value'));
33240 d3.json('http://nominatim.openstreetmap.org/search/' + searchVal + '?limit=10&format=json', function(err, resp) {
33241 geocodeResults = resp || [];
33247 return featureList;
33249 iD.ui.flash = function(selection) {
33250 var modal = iD.ui.modal(selection);
33252 modal.select('.modal').classed('modal-flash', true);
33254 modal.select('.content')
33255 .classed('modal-section', true)
33257 .attr('class', 'description');
33259 modal.on('click.flash', function() { modal.remove(); });
33261 setTimeout(function() {
33268 iD.ui.FullScreen = function(context) {
33269 var element = context.container().node(),
33270 keybinding = d3.keybinding('full-screen');
33273 function getFullScreenFn() {
33274 if (element.requestFullscreen) {
33275 return element.requestFullscreen;
33276 } else if (element.msRequestFullscreen) {
33277 return element.msRequestFullscreen;
33278 } else if (element.mozRequestFullScreen) {
33279 return element.mozRequestFullScreen;
33280 } else if (element.webkitRequestFullscreen) {
33281 return element.webkitRequestFullscreen;
33285 function getExitFullScreenFn() {
33286 if (document.exitFullscreen) {
33287 return document.exitFullscreen;
33288 } else if (document.msExitFullscreen) {
33289 return document.msExitFullscreen;
33290 } else if (document.mozCancelFullScreen) {
33291 return document.mozCancelFullScreen;
33292 } else if (document.webkitExitFullscreen) {
33293 return document.webkitExitFullscreen;
33297 function isFullScreen() {
33298 return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement ||
33299 document.msFullscreenElement;
33302 function isSupported() {
33303 return !!getFullScreenFn();
33306 function fullScreen() {
33307 d3.event.preventDefault();
33308 if (!isFullScreen()) {
33309 // button.classed('active', true);
33310 getFullScreenFn().apply(element);
33312 // button.classed('active', false);
33313 getExitFullScreenFn().apply(document);
33317 return function() { // selection) {
33318 if (!isSupported())
33321 // var tooltip = bootstrap.tooltip()
33322 // .placement('left');
33324 // button = selection.append('button')
33325 // .attr('title', t('full_screen'))
33326 // .attr('tabindex', -1)
33327 // .on('click', fullScreen)
33330 // button.append('span')
33331 // .attr('class', 'icon full-screen');
33334 .on('f11', fullScreen)
33335 .on(iD.ui.cmd('⌘⇧F'), fullScreen);
33337 d3.select(document)
33341 iD.ui.Geolocate = function(map) {
33343 navigator.geolocation.getCurrentPosition(
33347 function success(position) {
33348 var extent = iD.geo.Extent([position.coords.longitude, position.coords.latitude])
33349 .padByMeters(position.coords.accuracy);
33351 map.centerZoom(extent.center(), Math.min(20, map.extentZoom(extent)));
33354 function error() { }
33356 return function(selection) {
33357 if (!navigator.geolocation) return;
33359 selection.append('button')
33360 .attr('tabindex', -1)
33361 .attr('title', t('geolocate.title'))
33362 .on('click', click)
33363 .call(iD.svg.Icon('#icon-geolocate', 'light'))
33364 .call(bootstrap.tooltip()
33365 .placement('left'));
33368 iD.ui.Help = function(context) {
33373 'help.editing_saving',
33382 var docs = docKeys.map(function(key) {
33385 title: text.split('\n')[0].replace('#', '').trim(),
33386 html: marked(text.split('\n').slice(1).join('\n'))
33390 function help(selection) {
33396 function toggle() {
33397 if (d3.event) d3.event.preventDefault();
33398 tooltip.hide(button);
33399 setVisible(!button.classed('active'));
33402 function setVisible(show) {
33403 if (show !== shown) {
33404 button.classed('active', show);
33408 selection.on('mousedown.help-inside', function() {
33409 return d3.event.stopPropagation();
33411 pane.style('display', 'block')
33412 .style('right', '-500px')
33415 .style('right', '0px');
33417 pane.style('right', '0px')
33420 .style('right', '-500px')
33421 .each('end', function() {
33422 d3.select(this).style('display', 'none');
33424 selection.on('mousedown.help-inside', null);
33429 function clickHelp(d, i) {
33430 pane.property('scrollTop', 0);
33431 doctitle.html(d.title);
33433 body.selectAll('a')
33434 .attr('target', '_blank');
33435 menuItems.classed('selected', function(m) {
33436 return m.title === d.title;
33442 var prevLink = nav.append('a')
33443 .attr('class', 'previous')
33444 .on('click', function() {
33445 clickHelp(docs[i - 1], i - 1);
33447 prevLink.append('span').html('◄ ' + docs[i - 1].title);
33449 if (i < docs.length - 1) {
33450 var nextLink = nav.append('a')
33451 .attr('class', 'next')
33452 .on('click', function() {
33453 clickHelp(docs[i + 1], i + 1);
33455 nextLink.append('span').html(docs[i + 1].title + ' ►');
33459 function clickWalkthrough() {
33460 d3.select(document.body).call(iD.ui.intro(context));
33465 var pane = selection.append('div')
33466 .attr('class', 'help-wrap map-overlay fillL col5 content hide'),
33467 tooltip = bootstrap.tooltip()
33470 .title(iD.ui.tooltipHtml(t('help.title'), key)),
33471 button = selection.append('button')
33472 .attr('tabindex', -1)
33473 .on('click', toggle)
33474 .call(iD.svg.Icon('#icon-help', 'light'))
33479 var toc = pane.append('ul')
33480 .attr('class', 'toc');
33482 var menuItems = toc.selectAll('li')
33487 .html(function(d) { return d.title; })
33488 .on('click', clickHelp);
33491 .attr('class','walkthrough')
33493 .text(t('splash.walkthrough'))
33494 .on('click', clickWalkthrough);
33496 var content = pane.append('div')
33497 .attr('class', 'left-content');
33499 var doctitle = content.append('h2')
33500 .text(t('help.title'));
33502 var body = content.append('div')
33503 .attr('class', 'body');
33505 var nav = content.append('div')
33506 .attr('class', 'nav');
33508 clickHelp(docs[0], 0);
33510 var keybinding = d3.keybinding('help')
33515 d3.select(document)
33518 context.surface().on('mousedown.help-outside', hide);
33519 context.container().on('mousedown.help-outside', hide);
33524 iD.ui.Info = function(context) {
33525 var key = iD.ui.cmd('⌘I'),
33526 imperial = (iD.detect().locale.toLowerCase() === 'en-us');
33528 function info(selection) {
33529 function radiansToMeters(r) {
33530 // using WGS84 authalic radius (6371007.1809 m)
33531 return r * 6371007.1809;
33534 function steradiansToSqmeters(r) {
33535 // http://gis.stackexchange.com/a/124857/40446
33536 return r / 12.56637 * 510065621724000;
33539 function toLineString(feature) {
33540 if (feature.type === 'LineString') return feature;
33542 var result = { type: 'LineString', coordinates: [] };
33543 if (feature.type === 'Polygon') {
33544 result.coordinates = feature.coordinates[0];
33545 } else if (feature.type === 'MultiPolygon') {
33546 result.coordinates = feature.coordinates[0][0];
33552 function displayLength(m) {
33553 var d = m * (imperial ? 3.28084 : 1),
33572 // drop unnecessary precision
33573 p = d > 1000 ? 0 : d > 100 ? 1 : 2;
33575 return String(d.toFixed(p)) + ' ' + unit;
33578 function displayArea(m2) {
33579 var d = m2 * (imperial ? 10.7639111056 : 1),
33580 d1, d2, p1, p2, unit1, unit2;
33583 if (d >= 6969600) { // > 0.25mi² show mi²
33591 if (d > 4356 && d < 43560000) { // 0.1 - 1000 acres
33597 if (d >= 250000) { // > 0.25km² show km²
33605 if (d > 1000 && d < 10000000) { // 0.1 - 1000 hectares
33611 // drop unnecessary precision
33612 p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2;
33613 p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2;
33615 return String(d1.toFixed(p1)) + ' ' + unit1 +
33616 (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : '');
33620 function redraw() {
33621 if (hidden()) return;
33623 var resolver = context.graph(),
33624 selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }),
33625 singular = selected.length === 1 ? selected[0] : null,
33626 extent = iD.geo.Extent(),
33629 selection.html('');
33630 selection.append('h4')
33631 .attr('class', 'selection-heading fillD')
33632 .text(singular || t('infobox.selected', { n: selected.length }));
33634 if (!selected.length) return;
33637 for (var i = 0; i < selected.length; i++) {
33638 entity = context.entity(selected[i]);
33639 extent._extend(entity.extent(resolver));
33641 center = extent.center();
33644 var list = selection.append('ul');
33646 // multiple selection, just display extent center..
33649 .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
33653 // single selection, display details..
33654 if (!entity) return;
33655 var geometry = entity.geometry(resolver);
33657 if (geometry === 'line' || geometry === 'area') {
33658 var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()),
33659 feature = entity.asGeoJSON(resolver),
33660 length = radiansToMeters(d3.geo.length(toLineString(feature))),
33661 lengthLabel = t('infobox.' + (closed ? 'perimeter' : 'length')),
33662 centroid = d3.geo.centroid(feature);
33665 .text(t('infobox.geometry') + ': ' +
33666 (closed ? t('infobox.closed') + ' ' : '') + t('geometry.' + geometry) );
33669 var area = steradiansToSqmeters(entity.area(resolver));
33671 .text(t('infobox.area') + ': ' + displayArea(area));
33675 .text(lengthLabel + ': ' + displayLength(length));
33678 .text(t('infobox.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5));
33681 var toggle = imperial ? 'imperial' : 'metric';
33682 selection.append('a')
33683 .text(t('infobox.' + toggle))
33685 .attr('class', 'button')
33686 .on('click', function() {
33687 d3.event.preventDefault();
33688 imperial = !imperial;
33693 var centerLabel = t('infobox.' + (entity.type === 'node' ? 'location' : 'center'));
33696 .text(t('infobox.geometry') + ': ' + t('geometry.' + geometry));
33699 .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
33704 function hidden() {
33705 return selection.style('display') === 'none';
33709 function toggle() {
33710 if (d3.event) d3.event.preventDefault();
33714 .style('display', 'block')
33715 .style('opacity', 0)
33718 .style('opacity', 1);
33724 .style('display', 'block')
33725 .style('opacity', 1)
33728 .style('opacity', 0)
33729 .each('end', function() {
33730 d3.select(this).style('display', 'none');
33736 .on('drawn.info', redraw);
33740 var keybinding = d3.keybinding('info')
33743 d3.select(document)
33749 iD.ui.Inspector = function(context) {
33750 var presetList = iD.ui.PresetList(context),
33751 entityEditor = iD.ui.EntityEditor(context),
33754 newFeature = false;
33756 function inspector(selection) {
33758 .entityID(entityID)
33759 .autofocus(newFeature)
33760 .on('choose', setPreset);
33764 .entityID(entityID)
33765 .on('choose', showList);
33767 var $wrap = selection.selectAll('.panewrap')
33770 var $enter = $wrap.enter().append('div')
33771 .attr('class', 'panewrap');
33773 $enter.append('div')
33774 .attr('class', 'preset-list-pane pane');
33776 $enter.append('div')
33777 .attr('class', 'entity-editor-pane pane');
33779 var $presetPane = $wrap.select('.preset-list-pane');
33780 var $editorPane = $wrap.select('.entity-editor-pane');
33782 var graph = context.graph(),
33783 entity = context.entity(entityID),
33784 showEditor = state === 'hover' ||
33785 entity.isUsed(graph) ||
33786 entity.isHighwayIntersection(graph);
33789 $wrap.style('right', '0%');
33790 $editorPane.call(entityEditor);
33792 $wrap.style('right', '-100%');
33793 $presetPane.call(presetList);
33796 var $footer = selection.selectAll('.footer')
33799 $footer.enter().append('div')
33800 .attr('class', 'footer');
33802 selection.select('.footer')
33803 .call(iD.ui.ViewOnOSM(context)
33804 .entityID(entityID));
33806 function showList(preset) {
33808 .styleTween('right', function() { return d3.interpolate('0%', '-100%'); });
33810 $presetPane.call(presetList
33815 function setPreset(preset) {
33817 .styleTween('right', function() { return d3.interpolate('-100%', '0%'); });
33819 $editorPane.call(entityEditor
33824 inspector.state = function(_) {
33825 if (!arguments.length) return state;
33827 entityEditor.state(state);
33831 inspector.entityID = function(_) {
33832 if (!arguments.length) return entityID;
33837 inspector.newFeature = function(_) {
33838 if (!arguments.length) return newFeature;
33845 iD.ui.intro = function(context) {
33848 function intro(selection) {
33850 function localizedName(id) {
33852 n2140018997: 'city_hall',
33853 n367813436: 'fire_department',
33854 w203988286: 'memory_isle_park',
33855 w203972937: 'riverwalk_trail',
33856 w203972938: 'riverwalk_trail',
33857 w203972940: 'riverwalk_trail',
33858 w41785752: 'w_michigan_ave',
33859 w134150789: 'w_michigan_ave',
33860 w134150795: 'w_michigan_ave',
33861 w134150800: 'w_michigan_ave',
33862 w134150811: 'w_michigan_ave',
33863 w134150802: 'e_michigan_ave',
33864 w134150836: 'e_michigan_ave',
33865 w41074896: 'e_michigan_ave',
33866 w17965834: 'spring_st',
33867 w203986457: 'scidmore_park',
33868 w203049587: 'petting_zoo',
33869 w17967397: 'n_andrews_st',
33870 w17967315: 's_andrews_st',
33871 w17967326: 'n_constantine_st',
33872 w17966400: 's_constantine_st',
33873 w170848823: 'rocky_river',
33874 w170848824: 'rocky_river',
33875 w170848331: 'rocky_river',
33876 w17967752: 'railroad_dr',
33877 w17965998: 'conrail_rr',
33878 w134150845: 'conrail_rr',
33879 w170989131: 'st_joseph_river',
33880 w143497377: 'n_main_st',
33881 w134150801: 's_main_st',
33882 w134150830: 's_main_st',
33883 w17966462: 's_main_st',
33884 w17967734: 'water_st',
33885 w17964996: 'foster_st',
33886 w170848330: 'portage_river',
33887 w17965351: 'flower_st',
33888 w17965502: 'elm_st',
33889 w17965402: 'walnut_st',
33890 w17964793: 'morris_ave',
33891 w17967444: 'east_st',
33892 w17966984: 'portage_ave'
33894 return features[id] && t('intro.graph.' + features[id]);
33897 context.enter(iD.modes.Browse(context));
33899 // Save current map state
33900 var history = context.history().toJSON(),
33901 hash = window.location.hash,
33902 center = context.map().center(),
33903 zoom = context.map().zoom(),
33904 background = context.background().baseLayerSource(),
33905 opacity = d3.select('.background-layer').style('opacity'),
33906 loadedTiles = context.connection().loadedTiles(),
33907 baseEntities = context.history().graph().base().entities,
33911 context.inIntro(true);
33913 // Load semi-real data used in intro
33914 context.connection().toggle(false).flush();
33915 context.history().reset();
33917 introGraph = JSON.parse(iD.introGraph);
33918 for (var key in introGraph) {
33919 introGraph[key] = iD.Entity(introGraph[key]);
33920 name = localizedName(key);
33922 introGraph[key].tags.name = name;
33925 context.history().merge(d3.values(iD.Graph().load(introGraph).entities));
33926 context.background().bing();
33928 d3.select('.background-layer').style('opacity', 1);
33930 var curtain = d3.curtain();
33931 selection.call(curtain);
33933 function reveal(box, text, options) {
33934 options = options || {};
33935 if (text) curtain.reveal(box, text, options.tooltipClass, options.duration);
33936 else curtain.reveal(box, '', '', options.duration);
33939 var steps = ['navigation', 'point', 'area', 'line', 'startEditing'].map(function(step, i) {
33940 var s = iD.ui.intro[step](context, reveal)
33941 .on('done', function() {
33942 entered.filter(function(d) {
33943 return d.title === s.title;
33944 }).classed('finished', true);
33945 enter(steps[i + 1]);
33950 steps[steps.length - 1].on('startEditing', function() {
33953 d3.select('.background-layer').style('opacity', opacity);
33954 context.connection().toggle(true).flush().loadedTiles(loadedTiles);
33955 context.history().reset().merge(d3.values(baseEntities));
33956 context.background().baseLayerSource(background);
33957 if (history) context.history().fromJSON(history, false);
33958 context.map().centerZoom(center, zoom);
33959 window.location.replace(hash);
33960 context.inIntro(false);
33963 var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
33965 var buttonwrap = navwrap.append('div')
33966 .attr('class', 'joined')
33967 .selectAll('button.step');
33969 var entered = buttonwrap
33973 .attr('class', 'step')
33974 .on('click', enter);
33977 .call(iD.svg.Icon('#icon-apply', 'pre-text'));
33981 .text(function(d) { return t(d.title); });
33985 function enter (newStep) {
33986 if (step) { step.exit(); }
33988 context.enter(iD.modes.Browse(context));
33993 entered.classed('active', function(d) {
33994 return d.title === step.title;
34002 iD.ui.intro.pointBox = function(point, context) {
34003 var rect = context.surfaceRect();
34004 point = context.projection(point);
34006 left: point[0] + rect.left - 30,
34007 top: point[1] + rect.top - 50,
34013 iD.ui.intro.pad = function(box, padding, context) {
34014 if (box instanceof Array) {
34015 var rect = context.surfaceRect();
34016 box = context.projection(box);
34018 left: box[0] + rect.left,
34019 top: box[1] + rect.top
34023 left: box.left - padding,
34024 top: box.top - padding,
34025 width: (box.width || 0) + 2 * padding,
34026 height: (box.width || 0) + 2 * padding
34030 iD.ui.intro.icon = function(name, svgklass) {
34031 return '<svg class="icon ' + (svgklass || '') + '">' +
34032 '<use xlink:href="' + name + '"></use></svg>';
34034 iD.ui.Lasso = function(context) {
34040 function lasso(selection) {
34042 context.container().classed('lasso', true);
34044 group = selection.append('g')
34045 .attr('class', 'lasso hide');
34047 box = group.append('rect')
34048 .attr('class', 'lasso-box');
34050 group.call(iD.ui.Toggle(true));
34055 function topLeft(d) {
34056 return 'translate(' + Math.min(d[0][0], d[1][0]) + ',' + Math.min(d[0][1], d[1][1]) + ')';
34059 function width(d) { return Math.abs(d[0][0] - d[1][0]); }
34060 function height(d) { return Math.abs(d[0][1] - d[1][1]); }
34065 .attr('transform', topLeft)
34066 .attr('width', width)
34067 .attr('height', height);
34071 lasso.a = function(_) {
34072 if (!arguments.length) return a;
34078 lasso.b = function(_) {
34079 if (!arguments.length) return b;
34085 lasso.close = function() {
34087 group.call(iD.ui.Toggle(false, function() {
34088 d3.select(this).remove();
34091 context.container().classed('lasso', false);
34096 iD.ui.Loading = function(context) {
34101 var loading = function(selection) {
34102 modal = iD.ui.modal(selection, blocking);
34104 var loadertext = modal.select('.content')
34105 .classed('loading-modal', true)
34107 .attr('class', 'modal-section fillL');
34109 loadertext.append('img')
34110 .attr('class', 'loader')
34111 .attr('src', context.imagePath('loader-white.gif'));
34113 loadertext.append('h3')
34116 modal.select('button.close')
34117 .attr('class', 'hide');
34122 loading.message = function(_) {
34123 if (!arguments.length) return message;
34128 loading.blocking = function(_) {
34129 if (!arguments.length) return blocking;
34134 loading.close = function() {
34140 iD.ui.MapData = function(context) {
34142 features = context.features().keys(),
34143 fills = ['wireframe', 'partial', 'full'],
34144 fillDefault = context.storage('area-fill') || 'partial',
34145 fillSelected = fillDefault;
34147 function map_data(selection) {
34149 function showsFeature(d) {
34150 return context.features().enabled(d);
34153 function autoHiddenFeature(d) {
34154 return context.features().autoHidden(d);
34157 function clickFeature(d) {
34158 context.features().toggle(d);
34162 function showsFill(d) {
34163 return fillSelected === d;
34166 function setFill(d) {
34167 _.each(fills, function(opt) {
34168 context.surface().classed('fill-' + opt, Boolean(opt === d));
34172 if (d !== 'wireframe') {
34174 context.storage('area-fill', d);
34179 function clickGpx() {
34180 context.background().toggleGpxLayer();
34184 function clickMapillary() {
34185 context.background().toggleMapillaryLayer();
34189 function drawList(selection, data, type, name, change, active) {
34190 var items = selection.selectAll('li')
34194 var enter = items.enter()
34196 .attr('class', 'layer')
34197 .call(bootstrap.tooltip()
34199 .title(function(d) {
34200 var tip = t(name + '.' + d + '.tooltip'),
34201 key = (d === 'wireframe' ? 'W' : null);
34203 if (name === 'feature' && autoHiddenFeature(d)) {
34204 tip += '<div>' + t('map_data.autohidden') + '</div>';
34206 return iD.ui.tooltipHtml(tip, key);
34211 var label = enter.append('label');
34213 label.append('input')
34214 .attr('type', type)
34215 .attr('name', name)
34216 .on('change', change);
34218 label.append('span')
34219 .text(function(d) { return t(name + '.' + d + '.description'); });
34223 .classed('active', active)
34224 .selectAll('input')
34225 .property('checked', active)
34226 .property('indeterminate', function(d) {
34227 return (name === 'feature' && autoHiddenFeature(d));
34235 function update() {
34236 featureList.call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature);
34237 fillList.call(drawList, fills, 'radio', 'area_fill', setFill, showsFill);
34239 var hasGpx = context.background().hasGpxLayer(),
34240 showsGpx = context.background().showsGpxLayer(),
34241 showsMapillary = context.background().showsMapillaryLayer();
34244 .classed('active', showsGpx)
34245 .selectAll('input')
34246 .property('disabled', !hasGpx)
34247 .property('checked', showsGpx);
34250 .classed('active', showsMapillary)
34251 .selectAll('input')
34252 .property('checked', showsMapillary);
34255 function hidePanel() { setVisible(false); }
34257 function togglePanel() {
34258 if (d3.event) d3.event.preventDefault();
34259 tooltip.hide(button);
34260 setVisible(!button.classed('active'));
34263 function toggleWireframe() {
34265 d3.event.preventDefault();
34266 d3.event.stopPropagation();
34268 setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe'));
34269 context.map().pan([0,0]); // trigger a redraw
34272 function setVisible(show) {
34273 if (show !== shown) {
34274 button.classed('active', show);
34278 selection.on('mousedown.map_data-inside', function() {
34279 return d3.event.stopPropagation();
34281 content.style('display', 'block')
34282 .style('right', '-300px')
34285 .style('right', '0px');
34287 content.style('display', 'block')
34288 .style('right', '0px')
34291 .style('right', '-300px')
34292 .each('end', function() {
34293 d3.select(this).style('display', 'none');
34295 selection.on('mousedown.map_data-inside', null);
34301 var content = selection.append('div')
34302 .attr('class', 'fillL map-overlay col3 content hide'),
34303 tooltip = bootstrap.tooltip()
34306 .title(iD.ui.tooltipHtml(t('map_data.description'), key)),
34307 button = selection.append('button')
34308 .attr('tabindex', -1)
34309 .on('click', togglePanel)
34310 .call(iD.svg.Icon('#icon-data', 'light'))
34314 content.append('h4')
34315 .text(t('map_data.title'));
34319 content.append('a')
34320 .text(t('map_data.data_layers'))
34322 .classed('hide-toggle', true)
34323 .classed('expanded', true)
34324 .on('click', function() {
34325 var exp = d3.select(this).classed('expanded');
34326 layerContainer.style('display', exp ? 'none' : 'block');
34327 d3.select(this).classed('expanded', !exp);
34328 d3.event.preventDefault();
34331 var layerContainer = content.append('div')
34332 .attr('class', 'filters')
34333 .style('display', 'block');
34336 var mapillaryLayerItem = layerContainer.append('ul')
34337 .attr('class', 'layer-list')
34340 var label = mapillaryLayerItem.append('label')
34341 .call(bootstrap.tooltip()
34342 .title(t('mapillary.tooltip'))
34343 .placement('top'));
34345 label.append('input')
34346 .attr('type', 'checkbox')
34347 .on('change', clickMapillary);
34349 label.append('span')
34350 .text(t('mapillary.title'));
34353 var gpxLayerItem = layerContainer.append('ul')
34354 .style('display', iD.detect().filedrop ? 'block' : 'none')
34355 .attr('class', 'layer-list')
34357 .classed('layer-toggle-gpx', true);
34359 gpxLayerItem.append('button')
34360 .attr('class', 'layer-extent')
34361 .call(bootstrap.tooltip()
34362 .title(t('gpx.zoom'))
34363 .placement('left'))
34364 .on('click', function() {
34365 d3.event.preventDefault();
34366 d3.event.stopPropagation();
34367 context.background().zoomToGpxLayer();
34369 .call(iD.svg.Icon('#icon-search'));
34371 gpxLayerItem.append('button')
34372 .attr('class', 'layer-browse')
34373 .call(bootstrap.tooltip()
34374 .title(t('gpx.browse'))
34375 .placement('left'))
34376 .on('click', function() {
34377 d3.select(document.createElement('input'))
34378 .attr('type', 'file')
34379 .on('change', function() {
34380 context.background().gpxLayerFiles(d3.event.target.files);
34384 .call(iD.svg.Icon('#icon-geolocate'));
34386 label = gpxLayerItem.append('label')
34387 .call(bootstrap.tooltip()
34388 .title(t('gpx.drag_drop'))
34389 .placement('top'));
34391 label.append('input')
34392 .attr('type', 'checkbox')
34393 .property('disabled', true)
34394 .on('change', clickGpx);
34396 label.append('span')
34397 .text(t('gpx.local_layer'));
34401 content.append('a')
34402 .text(t('map_data.fill_area'))
34404 .classed('hide-toggle', true)
34405 .classed('expanded', false)
34406 .on('click', function() {
34407 var exp = d3.select(this).classed('expanded');
34408 fillContainer.style('display', exp ? 'none' : 'block');
34409 d3.select(this).classed('expanded', !exp);
34410 d3.event.preventDefault();
34413 var fillContainer = content.append('div')
34414 .attr('class', 'filters')
34415 .style('display', 'none');
34417 var fillList = fillContainer.append('ul')
34418 .attr('class', 'layer-list');
34422 content.append('a')
34423 .text(t('map_data.map_features'))
34425 .classed('hide-toggle', true)
34426 .classed('expanded', false)
34427 .on('click', function() {
34428 var exp = d3.select(this).classed('expanded');
34429 featureContainer.style('display', exp ? 'none' : 'block');
34430 d3.select(this).classed('expanded', !exp);
34431 d3.event.preventDefault();
34434 var featureContainer = content.append('div')
34435 .attr('class', 'filters')
34436 .style('display', 'none');
34438 var featureList = featureContainer.append('ul')
34439 .attr('class', 'layer-list');
34443 .on('change.map_data-update', update);
34445 setFill(fillDefault);
34447 var keybinding = d3.keybinding('features')
34448 .on(key, togglePanel)
34449 .on('W', toggleWireframe)
34450 .on('B', hidePanel)
34451 .on('H', hidePanel);
34453 d3.select(document)
34456 context.surface().on('mousedown.map_data-outside', hidePanel);
34457 context.container().on('mousedown.map_data-outside', hidePanel);
34462 iD.ui.MapInMap = function(context) {
34465 function map_in_map(selection) {
34467 var backgroundLayer = iD.TileLayer(),
34468 overlayLayers = {},
34469 dispatch = d3.dispatch('change'),
34470 gpxLayer = iD.GpxLayer(context, dispatch),
34471 projection = iD.geo.RawMercator(),
34472 zoom = d3.behavior.zoom()
34473 .scaleExtent([ztok(0.5), ztok(24)])
34474 .on('zoom', zoomPan),
34475 transformed = false,
34477 zDiff = 6, // by default, minimap renders at (main zoom - 6)
34478 tStart, tLast, tCurr, kLast, kCurr, tiles, svg, gpx, timeoutId;
34480 iD.ui.MapInMap.gpxLayer = gpxLayer;
34482 function ztok(z) { return 256 * Math.pow(2, z); }
34483 function ktoz(k) { return Math.log(k) / Math.LN2 - 8; }
34486 function startMouse() {
34487 context.surface().on('mouseup.map-in-map-outside', endMouse);
34488 context.container().on('mouseup.map-in-map-outside', endMouse);
34490 tStart = tLast = tCurr = projection.translate();
34495 function zoomPan() {
34496 var e = d3.event.sourceEvent,
34497 t = d3.event.translate,
34498 k = d3.event.scale,
34499 zMain = ktoz(context.projection.scale() * 2 * Math.PI),
34502 // restrict minimap zoom to < (main zoom - 3)
34503 if (zMini > zMain - 3) {
34505 zoom.scale(kCurr).translate(tCurr); // restore last good values
34511 zDiff = zMain - zMini;
34513 var scale = kCurr / kLast,
34514 tX = (tCurr[0] / scale - tLast[0]) * scale,
34515 tY = (tCurr[1] / scale - tLast[1]) * scale;
34517 iD.util.setTransform(tiles, tX, tY, scale);
34518 iD.util.setTransform(svg, 0, 0, scale);
34519 transformed = true;
34523 e.preventDefault();
34524 e.stopPropagation();
34528 function endMouse() {
34529 context.surface().on('mouseup.map-in-map-outside', null);
34530 context.container().on('mouseup.map-in-map-outside', null);
34532 updateProjection();
34535 if (tCurr[0] !== tStart[0] && tCurr[1] !== tStart[1]) {
34536 var dMini = selection.dimensions(),
34537 cMini = [ dMini[0] / 2, dMini[1] / 2 ];
34539 context.map().center(projection.invert(cMini));
34544 function updateProjection() {
34545 var loc = context.map().center(),
34546 dMini = selection.dimensions(),
34547 cMini = [ dMini[0] / 2, dMini[1] / 2 ],
34548 tMain = context.projection.translate(),
34549 kMain = context.projection.scale(),
34550 zMain = ktoz(kMain * 2 * Math.PI),
34551 zMini = Math.max(zMain - zDiff, 0.5),
34552 kMini = ztok(zMini);
34556 .scale(kMini / (2 * Math.PI));
34558 var s = projection(loc),
34559 mouse = panning ? [ tCurr[0] - tStart[0], tCurr[1] - tStart[1] ] : [0, 0],
34561 cMini[0] - s[0] + tMain[0] + mouse[0],
34562 cMini[1] - s[1] + tMain[1] + mouse[1]
34567 .clipExtent([[0, 0], dMini]);
34574 tLast = tCurr = tMini;
34575 kLast = kCurr = kMini;
34578 iD.util.setTransform(tiles, 0, 0);
34579 iD.util.setTransform(svg, 0, 0);
34580 transformed = false;
34585 function redraw() {
34586 if (hidden()) return;
34588 updateProjection();
34590 var dMini = selection.dimensions(),
34591 zMini = ktoz(projection.scale() * 2 * Math.PI);
34593 // setup tile container
34595 .selectAll('.map-in-map-tiles')
34601 .attr('class', 'map-in-map-tiles');
34603 // redraw background
34605 .source(context.background().baseLayerSource())
34606 .projection(projection)
34607 .dimensions(dMini);
34609 var background = tiles
34610 .selectAll('.map-in-map-background')
34615 .attr('class', 'map-in-map-background');
34618 .call(backgroundLayer);
34621 var overlaySources = context.background().overlayLayerSources();
34622 var activeOverlayLayers = [];
34623 for (var i = 0; i < overlaySources.length; i++) {
34624 if (overlaySources[i].validZoom(zMini)) {
34625 if (!overlayLayers[i]) overlayLayers[i] = iD.TileLayer();
34626 activeOverlayLayers.push(overlayLayers[i]
34627 .source(overlaySources[i])
34628 .projection(projection)
34629 .dimensions(dMini));
34633 var overlay = tiles
34634 .selectAll('.map-in-map-overlay')
34639 .attr('class', 'map-in-map-overlay');
34641 var overlays = overlay
34643 .data(activeOverlayLayers, function(d) { return d.source().name(); });
34645 overlays.enter().append('div');
34646 overlays.each(function(layer) {
34647 d3.select(this).call(layer);
34653 // redraw gpx overlay
34655 .projection(projection);
34658 .selectAll('.map-in-map-gpx')
34663 .attr('class', 'map-in-map-gpx');
34665 gpx.call(gpxLayer);
34666 gpx.dimensions(dMini);
34668 // redraw bounding box
34670 var getPath = d3.geo.path().projection(projection),
34671 bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
34673 svg = selection.selectAll('.map-in-map-svg')
34678 .attr('class', 'map-in-map-svg');
34680 var path = svg.selectAll('.map-in-map-bbox')
34685 .attr('class', 'map-in-map-bbox');
34688 .attr('d', getPath)
34689 .classed('thick', function(d) { return getPath.area(d) < 30; });
34694 function queueRedraw() {
34695 clearTimeout(timeoutId);
34696 timeoutId = setTimeout(function() { redraw(); }, 300);
34700 function hidden() {
34701 return selection.style('display') === 'none';
34705 function toggle() {
34706 if (d3.event) d3.event.preventDefault();
34708 var label = d3.select('.minimap-toggle');
34712 .style('display', 'block')
34713 .style('opacity', 0)
34716 .style('opacity', 1);
34718 label.classed('active', true)
34719 .select('input').property('checked', true);
34725 .style('display', 'block')
34726 .style('opacity', 1)
34729 .style('opacity', 0)
34730 .each('end', function() {
34731 d3.select(this).style('display', 'none');
34734 label.classed('active', false)
34735 .select('input').property('checked', false);
34739 iD.ui.MapInMap.toggle = toggle;
34742 .on('mousedown.map-in-map', startMouse)
34743 .on('mouseup.map-in-map', endMouse);
34747 .on('dblclick.zoom', null);
34750 .on('drawn.map-in-map', function(drawn) {
34751 if (drawn.full === true) redraw();
34756 var keybinding = d3.keybinding('map-in-map')
34759 d3.select(document)
34765 iD.ui.modal = function(selection, blocking) {
34767 var previous = selection.select('div.modal');
34768 var animate = previous.empty();
34770 previous.transition()
34772 .style('opacity', 0)
34775 var shaded = selection
34777 .attr('class', 'shaded')
34778 .style('opacity', 0);
34780 shaded.close = function() {
34784 .style('opacity',0)
34789 .style('top','0px');
34793 var keybinding = d3.keybinding('modal')
34794 .on('⌫', shaded.close)
34795 .on('⎋', shaded.close);
34797 d3.select(document).call(keybinding);
34799 var modal = shaded.append('div')
34800 .attr('class', 'modal fillL col6');
34802 shaded.on('click.remove-modal', function() {
34803 if (d3.event.target === this && !blocking) shaded.close();
34806 modal.append('button')
34807 .attr('class', 'close')
34808 .on('click', function() {
34809 if (!blocking) shaded.close();
34812 .attr('class','icon close');
34814 modal.append('div')
34815 .attr('class', 'content');
34818 shaded.transition().style('opacity', 1);
34820 shaded.style('opacity', 1);
34825 iD.ui.Modes = function(context) {
34827 iD.modes.AddPoint(context),
34828 iD.modes.AddLine(context),
34829 iD.modes.AddArea(context)];
34831 function editable() {
34832 return context.editable() && context.mode().id !== 'save';
34835 return function(selection) {
34836 var buttons = selection.selectAll('button.add-button')
34839 buttons.enter().append('button')
34840 .attr('tabindex', -1)
34841 .attr('class', function(mode) { return mode.id + ' add-button col4'; })
34842 .on('click.mode-buttons', function(mode) {
34843 if (mode.id === context.mode().id) {
34844 context.enter(iD.modes.Browse(context));
34846 context.enter(mode);
34849 .call(bootstrap.tooltip()
34850 .placement('bottom')
34852 .title(function(mode) {
34853 return iD.ui.tooltipHtml(mode.description, mode.key);
34857 .on('move.modes', _.debounce(update, 500));
34860 .on('enter.modes', update);
34862 buttons.each(function(d) {
34864 .call(iD.svg.Icon('#icon-' + d.button, 'pre-text'));
34867 buttons.append('span')
34868 .attr('class', 'label')
34869 .text(function(mode) { return mode.title; });
34871 context.on('enter.editor', function(entered) {
34872 buttons.classed('active', function(mode) { return entered.button === mode.button; });
34873 context.container()
34874 .classed('mode-' + entered.id, true);
34877 context.on('exit.editor', function(exited) {
34878 context.container()
34879 .classed('mode-' + exited.id, false);
34882 var keybinding = d3.keybinding('mode-buttons');
34884 modes.forEach(function(m) {
34885 keybinding.on(m.key, function() { if (editable()) context.enter(m); });
34888 d3.select(document)
34891 function update() {
34892 buttons.property('disabled', !editable());
34896 iD.ui.Notice = function(context) {
34897 return function(selection) {
34898 var div = selection.append('div')
34899 .attr('class', 'notice');
34901 var button = div.append('button')
34902 .attr('class', 'zoom-to notice')
34903 .on('click', function() { context.map().zoom(context.minEditableZoom()); });
34906 .call(iD.svg.Icon('#icon-plus', 'pre-text'))
34908 .attr('class', 'label')
34909 .text(t('zoom_in_edit'));
34911 function disableTooHigh() {
34912 div.style('display', context.editable() ? 'none' : 'block');
34916 .on('move.notice', _.debounce(disableTooHigh, 500));
34921 iD.ui.preset = function(context) {
34922 var event = d3.dispatch('change'),
34929 function UIField(field, entity, show) {
34930 field = _.clone(field);
34932 field.input = iD.ui.preset[field.type](field, context)
34933 .on('change', event.change);
34935 if (field.input.entity) field.input.entity(entity);
34937 field.keys = field.keys || [field.key];
34941 field.shown = function() {
34942 return field.id === 'name' || field.show || _.any(field.keys, function(key) { return !!tags[key]; });
34945 field.modified = function() {
34946 var original = context.graph().base().entities[entity.id];
34947 return _.any(field.keys, function(key) {
34948 return original ? tags[key] !== original.tags[key] : tags[key];
34952 field.revert = function() {
34953 var original = context.graph().base().entities[entity.id],
34955 field.keys.forEach(function(key) {
34956 t[key] = original ? original.tags[key] : undefined;
34961 field.present = function() {
34962 return _.any(field.keys, function(key) {
34967 field.remove = function() {
34969 field.keys.forEach(function(key) {
34970 t[key] = undefined;
34978 function fieldKey(field) {
34982 function presets(selection) {
34984 var entity = context.entity(id),
34985 geometry = context.geometry(id);
34987 fields = [UIField(context.presets().field('name'), entity)];
34989 preset.fields.forEach(function(field) {
34990 if (field.matchGeometry(geometry)) {
34991 fields.push(UIField(field, entity, true));
34995 if (entity.isHighwayIntersection(context.graph())) {
34996 fields.push(UIField(context.presets().field('restrictions'), entity, true));
34999 context.presets().universal().forEach(function(field) {
35000 if (preset.fields.indexOf(field) < 0) {
35001 fields.push(UIField(field, entity));
35006 var shown = fields.filter(function(field) { return field.shown(); }),
35007 notShown = fields.filter(function(field) { return !field.shown(); });
35009 var $form = selection.selectAll('.preset-form')
35012 $form.enter().append('div')
35013 .attr('class', 'preset-form inspector-inner fillL3');
35015 var $fields = $form.selectAll('.form-field')
35016 .data(shown, fieldKey);
35020 var $enter = $fields.enter()
35022 .attr('class', function(field) {
35023 return 'form-field form-field-' + field.id;
35026 var $label = $enter.append('label')
35027 .attr('class', 'form-label')
35028 .attr('for', function(field) { return 'preset-input-' + field.id; })
35029 .text(function(field) { return field.label(); });
35031 var wrap = $label.append('div')
35032 .attr('class', 'form-label-button-wrap');
35034 wrap.append('button')
35035 .attr('class', 'remove-icon')
35036 .call(iD.svg.Icon('#operation-delete'));
35038 wrap.append('button')
35039 .attr('class', 'modified-icon')
35040 .attr('tabindex', -1)
35041 .call(iD.svg.Icon('#icon-undo'));
35045 $fields.select('.form-label-button-wrap .remove-icon')
35046 .on('click', remove);
35048 $fields.select('.modified-icon')
35049 .on('click', revert);
35053 .classed('modified', function(field) {
35054 return field.modified();
35056 .classed('present', function(field) {
35057 return field.present();
35059 .each(function(field) {
35060 var reference = iD.ui.TagReference(field.reference || {key: field.key}, context);
35062 if (state === 'hover') {
35063 reference.showing(false);
35068 .call(reference.body)
35069 .select('.form-label-button-wrap')
35070 .call(reference.button);
35072 field.input.tags(tags);
35078 notShown = notShown.map(function(field) {
35080 title: field.label(),
35081 value: field.label(),
35086 var $more = selection.selectAll('.more-fields')
35087 .data((notShown.length > 0) ? [0] : []);
35089 $more.enter().append('div')
35090 .attr('class', 'more-fields')
35092 .text(t('inspector.add_fields'));
35094 var $input = $more.selectAll('.value')
35097 $input.enter().append('input')
35098 .attr('class', 'value')
35099 .attr('type', 'text');
35102 .attr('placeholder', function() {
35103 var placeholder = [];
35104 for (var field in notShown) {
35105 placeholder.push(notShown[field].title);
35107 return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : '');
35109 .call(d3.combobox().data(notShown)
35111 .on('accept', show));
35119 function show(field) {
35120 field = field.field;
35122 presets(selection);
35123 field.input.focus();
35126 function revert(field) {
35127 d3.event.stopPropagation();
35128 d3.event.preventDefault();
35129 event.change(field.revert());
35132 function remove(field) {
35133 d3.event.stopPropagation();
35134 d3.event.preventDefault();
35135 event.change(field.remove());
35139 presets.preset = function(_) {
35140 if (!arguments.length) return preset;
35141 if (preset && preset.id === _.id) return presets;
35147 presets.state = function(_) {
35148 if (!arguments.length) return state;
35153 presets.tags = function(_) {
35154 if (!arguments.length) return tags;
35156 // Don't reset fields here.
35160 presets.entityID = function(_) {
35161 if (!arguments.length) return id;
35162 if (id === _) return presets;
35168 return d3.rebind(presets, event, 'on');
35170 iD.ui.PresetIcon = function() {
35171 var preset, geometry;
35173 function presetIcon(selection) {
35174 selection.each(render);
35177 function render() {
35178 var selection = d3.select(this),
35179 p = preset.apply(this, arguments),
35180 geom = geometry.apply(this, arguments),
35181 icon = p.icon || (geom === 'line' ? 'other-line' : 'marker-stroked'),
35182 maki = iD.data.featureIcons.hasOwnProperty(icon + '-24');
35184 if (icon === 'dentist') maki = true; // workaround for dentist icon missing in `maki-sprite.json`
35186 function tag_classes(p) {
35188 for (var i in p.tags) {
35190 if (p.tags[i] !== '*') {
35191 s += ' tag-' + i + '-' + p.tags[i];
35197 var $fill = selection.selectAll('.preset-icon-fill')
35200 $fill.enter().append('div');
35202 $fill.attr('class', function() {
35203 return 'preset-icon-fill preset-icon-fill-' + geom + tag_classes(p);
35206 var $frame = selection.selectAll('.preset-icon-frame')
35211 .call(iD.svg.Icon('#preset-icon-frame'));
35213 $frame.attr('class', function() {
35214 return 'preset-icon-frame ' + (geom === 'area' ? '' : 'hide');
35218 var $icon = selection.selectAll('.preset-icon')
35223 .attr('class', 'preset-icon')
35224 .call(iD.svg.Icon(''));
35227 .attr('class', 'preset-icon preset-icon-' + (maki ? '32' : '60'));
35229 $icon.selectAll('svg')
35230 .attr('class', function() {
35231 return 'icon ' + icon + tag_classes(p);
35234 $icon.selectAll('use') // workaround: maki parking-24 broken?
35235 .attr('href', '#' + icon + (maki ? ( icon === 'parking' ? '-18' : '-24') : ''));
35238 presetIcon.preset = function(_) {
35239 if (!arguments.length) return preset;
35240 preset = d3.functor(_);
35244 presetIcon.geometry = function(_) {
35245 if (!arguments.length) return geometry;
35246 geometry = d3.functor(_);
35252 iD.ui.PresetList = function(context) {
35253 var event = d3.dispatch('choose'),
35258 function presetList(selection) {
35259 var geometry = context.geometry(id),
35260 presets = context.presets().matchGeometry(geometry);
35262 selection.html('');
35264 var messagewrap = selection.append('div')
35265 .attr('class', 'header fillL cf');
35267 var message = messagewrap.append('h3')
35268 .text(t('inspector.choose'));
35270 if (context.entity(id).isUsed(context.graph())) {
35271 messagewrap.append('button')
35272 .attr('class', 'preset-choose')
35273 .on('click', function() { event.choose(currentPreset); })
35277 messagewrap.append('button')
35278 .attr('class', 'close')
35279 .on('click', function() {
35280 context.enter(iD.modes.Browse(context));
35282 .call(iD.svg.Icon('#icon-close'));
35285 function keydown() {
35286 // hack to let delete shortcut work when search is autofocused
35287 if (search.property('value').length === 0 &&
35288 (d3.event.keyCode === d3.keybinding.keyCodes['⌫'] ||
35289 d3.event.keyCode === d3.keybinding.keyCodes['⌦'])) {
35290 d3.event.preventDefault();
35291 d3.event.stopPropagation();
35292 iD.operations.Delete([id], context)();
35293 } else if (search.property('value').length === 0 &&
35294 (d3.event.ctrlKey || d3.event.metaKey) &&
35295 d3.event.keyCode === d3.keybinding.keyCodes.z) {
35296 d3.event.preventDefault();
35297 d3.event.stopPropagation();
35299 } else if (!d3.event.ctrlKey && !d3.event.metaKey) {
35300 d3.select(this).on('keydown', null);
35304 function keypress() {
35306 var value = search.property('value');
35307 if (d3.event.keyCode === 13 && value.length) {
35308 list.selectAll('.preset-list-item:first-child').datum().choose();
35312 function inputevent() {
35313 var value = search.property('value');
35314 list.classed('filtered', value.length);
35315 if (value.length) {
35316 var results = presets.search(value, geometry);
35317 message.text(t('inspector.results', {
35318 n: results.collection.length,
35321 list.call(drawList, results);
35323 list.call(drawList, context.presets().defaults(geometry, 36));
35324 message.text(t('inspector.choose'));
35328 var searchWrap = selection.append('div')
35329 .attr('class', 'search-header');
35331 var search = searchWrap.append('input')
35332 .attr('class', 'preset-search-input')
35333 .attr('placeholder', t('inspector.search'))
35334 .attr('type', 'search')
35335 .on('keydown', keydown)
35336 .on('keypress', keypress)
35337 .on('input', inputevent);
35340 .call(iD.svg.Icon('#icon-search', 'pre-text'));
35343 search.node().focus();
35346 var listWrap = selection.append('div')
35347 .attr('class', 'inspector-body');
35349 var list = listWrap.append('div')
35350 .attr('class', 'preset-list fillL cf')
35351 .call(drawList, context.presets().defaults(geometry, 36));
35354 function drawList(list, presets) {
35355 var collection = presets.collection.map(function(preset) {
35356 return preset.members ? CategoryItem(preset) : PresetItem(preset);
35359 var items = list.selectAll('.preset-list-item')
35360 .data(collection, function(d) { return d.preset.id; });
35362 items.enter().append('div')
35363 .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
35364 .classed('current', function(item) { return item.preset === currentPreset; })
35365 .each(function(item) {
35366 d3.select(this).call(item);
35368 .style('opacity', 0)
35370 .style('opacity', 1);
35378 function CategoryItem(preset) {
35379 var box, sublist, shown = false;
35381 function item(selection) {
35382 var wrap = selection.append('div')
35383 .attr('class', 'preset-list-button-wrap category col12');
35385 wrap.append('button')
35386 .attr('class', 'preset-list-button')
35387 .call(iD.ui.PresetIcon()
35388 .geometry(context.geometry(id))
35390 .on('click', item.choose)
35392 .attr('class', 'label')
35393 .text(preset.name());
35395 box = selection.append('div')
35396 .attr('class', 'subgrid col12')
35397 .style('max-height', '0px')
35398 .style('opacity', 0);
35401 .attr('class', 'arrow');
35403 sublist = box.append('div')
35404 .attr('class', 'preset-list fillL3 cf fl');
35407 item.choose = function() {
35412 .style('opacity', '0')
35413 .style('max-height', '0px')
35414 .style('padding-bottom', '0px');
35417 sublist.call(drawList, preset.members);
35420 .style('opacity', '1')
35421 .style('max-height', 200 + preset.members.collection.length * 80 + 'px')
35422 .style('padding-bottom', '20px');
35426 item.preset = preset;
35431 function PresetItem(preset) {
35432 function item(selection) {
35433 var wrap = selection.append('div')
35434 .attr('class', 'preset-list-button-wrap col12');
35436 wrap.append('button')
35437 .attr('class', 'preset-list-button')
35438 .call(iD.ui.PresetIcon()
35439 .geometry(context.geometry(id))
35441 .on('click', item.choose)
35443 .attr('class', 'label')
35444 .text(preset.name());
35446 wrap.call(item.reference.button);
35447 selection.call(item.reference.body);
35450 item.choose = function() {
35451 context.presets().choose(preset);
35454 iD.actions.ChangePreset(id, currentPreset, preset),
35455 t('operations.change_tags.annotation'));
35457 event.choose(preset);
35460 item.help = function() {
35461 d3.event.stopPropagation();
35462 item.reference.toggle();
35465 item.preset = preset;
35466 item.reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context);
35471 presetList.autofocus = function(_) {
35472 if (!arguments.length) return autofocus;
35477 presetList.entityID = function(_) {
35478 if (!arguments.length) return id;
35480 presetList.preset(context.presets().match(context.entity(id), context.graph()));
35484 presetList.preset = function(_) {
35485 if (!arguments.length) return currentPreset;
35490 return d3.rebind(presetList, event, 'on');
35492 iD.ui.RadialMenu = function(context, operations) {
35497 var radialMenu = function(selection) {
35498 if (!operations.length)
35501 selection.node().parentNode.focus();
35503 function click(operation) {
35504 d3.event.stopPropagation();
35505 if (operation.disabled())
35508 radialMenu.close();
35511 menu = selection.append('g')
35512 .attr('class', 'radial-menu')
35513 .attr('transform', 'translate(' + center + ')')
35514 .attr('opacity', 0);
35517 .attr('opacity', 1);
35522 a1 = a0 + (operations.length - 1) * a;
35524 menu.append('path')
35525 .attr('class', 'radial-menu-background')
35526 .attr('d', 'M' + r * Math.sin(a0) + ',' +
35528 ' A' + r + ',' + r + ' 0 ' + (operations.length > 5 ? '1' : '0') + ',0 ' +
35529 (r * Math.sin(a1) + 1e-3) + ',' +
35530 (r * Math.cos(a1) + 1e-3)) // Force positive-length path (#1305)
35531 .attr('stroke-width', 50)
35532 .attr('stroke-linecap', 'round');
35534 var button = menu.selectAll()
35538 .attr('class', function(d) { return 'radial-menu-item radial-menu-item-' + d.id; })
35539 .classed('disabled', function(d) { return d.disabled(); })
35540 .attr('transform', function(d, i) {
35541 return 'translate(' + iD.geo.roundCoords([
35542 r * Math.sin(a0 + i * a),
35543 r * Math.cos(a0 + i * a)]).join(',') + ')';
35546 button.append('circle')
35548 .on('click', click)
35549 .on('mousedown', mousedown)
35550 .on('mouseover', mouseover)
35551 .on('mouseout', mouseout);
35553 button.append('use')
35554 .attr('transform', 'translate(-10,-10)')
35555 .attr('width', '20')
35556 .attr('height', '20')
35557 .attr('xlink:href', function(d) { return '#operation-' + d.id; });
35559 tooltip = d3.select(document.body)
35561 .attr('class', 'tooltip-inner radial-menu-tooltip');
35563 function mousedown() {
35564 d3.event.stopPropagation(); // https://github.com/openstreetmap/iD/issues/1869
35567 function mouseover(d, i) {
35568 var rect = context.surfaceRect(),
35569 angle = a0 + i * a,
35570 top = rect.top + (r + 25) * Math.cos(angle) + center[1] + 'px',
35571 left = rect.left + (r + 25) * Math.sin(angle) + center[0] + 'px',
35572 bottom = rect.height - (r + 25) * Math.cos(angle) - center[1] + 'px',
35573 right = rect.width - (r + 25) * Math.sin(angle) - center[0] + 'px';
35576 .style('top', null)
35577 .style('left', null)
35578 .style('bottom', null)
35579 .style('right', null)
35580 .style('display', 'block')
35581 .html(iD.ui.tooltipHtml(d.tooltip(), d.keys[0]));
35585 .style('right', right)
35586 .style('top', top);
35587 } else if (i >= 4) {
35589 .style('left', left)
35590 .style('bottom', bottom);
35593 .style('left', left)
35594 .style('top', top);
35598 function mouseout() {
35599 tooltip.style('display', 'none');
35603 radialMenu.close = function() {
35606 .style('pointer-events', 'none')
35608 .attr('opacity', 0)
35617 radialMenu.center = function(_) {
35618 if (!arguments.length) return center;
35625 iD.ui.RawMemberEditor = function(context) {
35628 function selectMember(d) {
35629 d3.event.preventDefault();
35630 context.enter(iD.modes.Select(context, [d.id]));
35633 function changeRole(d) {
35634 var role = d3.select(this).property('value');
35636 iD.actions.ChangeMember(d.relation.id, _.extend({}, d.id, {role: role}), d.index),
35637 t('operations.change_role.annotation'));
35640 function deleteMember(d) {
35642 iD.actions.DeleteMember(d.relation.id, d.index),
35643 t('operations.delete_member.annotation'));
35645 if (!context.hasEntity(d.relation.id)) {
35646 context.enter(iD.modes.Browse(context));
35650 function rawMemberEditor(selection) {
35651 var entity = context.entity(id),
35654 entity.members.forEach(function(member, index) {
35660 member: context.hasEntity(member.id)
35664 selection.call(iD.ui.Disclosure()
35665 .title(t('inspector.all_members') + ' (' + memberships.length + ')')
35667 .on('toggled', toggled)
35668 .content(content));
35670 function toggled(expanded) {
35672 selection.node().parentNode.scrollTop += 200;
35676 function content($wrap) {
35677 var $list = $wrap.selectAll('.member-list')
35680 $list.enter().append('ul')
35681 .attr('class', 'member-list');
35683 var $items = $list.selectAll('li')
35684 .data(memberships, function(d) {
35685 return iD.Entity.key(d.relation) + ',' + d.index + ',' +
35686 (d.member ? iD.Entity.key(d.member) : 'incomplete');
35689 var $enter = $items.enter().append('li')
35690 .attr('class', 'member-row form-field')
35691 .classed('member-incomplete', function(d) { return !d.member; });
35693 $enter.each(function(d) {
35695 var $label = d3.select(this).append('label')
35696 .attr('class', 'form-label')
35699 .on('click', selectMember);
35701 $label.append('span')
35702 .attr('class', 'member-entity-type')
35703 .text(function(d) { return context.presets().match(d.member, context.graph()).name(); });
35705 $label.append('span')
35706 .attr('class', 'member-entity-name')
35707 .text(function(d) { return iD.util.displayName(d.member); });
35710 d3.select(this).append('label')
35711 .attr('class', 'form-label')
35712 .text(t('inspector.incomplete'));
35716 $enter.append('input')
35717 .attr('class', 'member-role')
35718 .property('type', 'text')
35719 .attr('maxlength', 255)
35720 .attr('placeholder', t('inspector.role'))
35721 .property('value', function(d) { return d.role; })
35722 .on('change', changeRole);
35724 $enter.append('button')
35725 .attr('tabindex', -1)
35726 .attr('class', 'remove button-input-action member-delete minor')
35727 .on('click', deleteMember)
35728 .call(iD.svg.Icon('#operation-delete'));
35735 rawMemberEditor.entityID = function(_) {
35736 if (!arguments.length) return id;
35738 return rawMemberEditor;
35741 return rawMemberEditor;
35743 iD.ui.RawMembershipEditor = function(context) {
35746 function selectRelation(d) {
35747 d3.event.preventDefault();
35748 context.enter(iD.modes.Select(context, [d.relation.id]));
35751 function changeRole(d) {
35752 var role = d3.select(this).property('value');
35754 iD.actions.ChangeMember(d.relation.id, _.extend({}, d.member, {role: role}), d.index),
35755 t('operations.change_role.annotation'));
35758 function addMembership(d, role) {
35763 iD.actions.AddMember(d.relation.id, {id: id, type: context.entity(id).type, role: role}),
35764 t('operations.add_member.annotation'));
35767 var relation = iD.Relation();
35770 iD.actions.AddEntity(relation),
35771 iD.actions.AddMember(relation.id, {id: id, type: context.entity(id).type, role: role}),
35772 t('operations.add.annotation.relation'));
35774 context.enter(iD.modes.Select(context, [relation.id]));
35778 function deleteMembership(d) {
35780 iD.actions.DeleteMember(d.relation.id, d.index),
35781 t('operations.delete_member.annotation'));
35784 function relations(q) {
35785 var newRelation = {
35787 value: t('inspector.new_relation')
35790 graph = context.graph();
35792 context.intersects(context.extent()).forEach(function(entity) {
35793 if (entity.type !== 'relation' || entity.id === id)
35796 var presetName = context.presets().match(entity, graph).name(),
35797 entityName = iD.util.displayName(entity) || '';
35799 var value = presetName + ' ' + entityName;
35800 if (q && value.toLowerCase().indexOf(q.toLowerCase()) === -1)
35809 result.sort(function(a, b) {
35810 return iD.Relation.creationOrder(a.relation, b.relation);
35812 result.unshift(newRelation);
35817 function rawMembershipEditor(selection) {
35818 var entity = context.entity(id),
35821 context.graph().parentRelations(entity).forEach(function(relation) {
35822 relation.members.forEach(function(member, index) {
35823 if (member.id === entity.id) {
35824 memberships.push({relation: relation, member: member, index: index});
35829 selection.call(iD.ui.Disclosure()
35830 .title(t('inspector.all_relations') + ' (' + memberships.length + ')')
35832 .on('toggled', toggled)
35833 .content(content));
35835 function toggled(expanded) {
35837 selection.node().parentNode.scrollTop += 200;
35841 function content($wrap) {
35842 var $list = $wrap.selectAll('.member-list')
35845 $list.enter().append('ul')
35846 .attr('class', 'member-list');
35848 var $items = $list.selectAll('li.member-row-normal')
35849 .data(memberships, function(d) { return iD.Entity.key(d.relation) + ',' + d.index; });
35851 var $enter = $items.enter().append('li')
35852 .attr('class', 'member-row member-row-normal form-field');
35854 var $label = $enter.append('label')
35855 .attr('class', 'form-label')
35858 .on('click', selectRelation);
35860 $label.append('span')
35861 .attr('class', 'member-entity-type')
35862 .text(function(d) { return context.presets().match(d.relation, context.graph()).name(); });
35864 $label.append('span')
35865 .attr('class', 'member-entity-name')
35866 .text(function(d) { return iD.util.displayName(d.relation); });
35868 $enter.append('input')
35869 .attr('class', 'member-role')
35870 .property('type', 'text')
35871 .attr('maxlength', 255)
35872 .attr('placeholder', t('inspector.role'))
35873 .property('value', function(d) { return d.member.role; })
35874 .on('change', changeRole);
35876 $enter.append('button')
35877 .attr('tabindex', -1)
35878 .attr('class', 'remove button-input-action member-delete minor')
35879 .on('click', deleteMembership)
35880 .call(iD.svg.Icon('#operation-delete'));
35886 var $new = $list.selectAll('.member-row-new')
35889 $enter = $new.enter().append('li')
35890 .attr('class', 'member-row member-row-new form-field');
35892 $enter.append('input')
35893 .attr('type', 'text')
35894 .attr('class', 'member-entity-input')
35895 .call(d3.combobox()
35897 .fetcher(function(value, callback) {
35898 callback(relations(value));
35900 .on('accept', function(d) {
35901 addMembership(d, $new.select('.member-role').property('value'));
35904 $enter.append('input')
35905 .attr('class', 'member-role')
35906 .property('type', 'text')
35907 .attr('maxlength', 255)
35908 .attr('placeholder', t('inspector.role'))
35909 .on('change', changeRole);
35911 $enter.append('button')
35912 .attr('tabindex', -1)
35913 .attr('class', 'remove button-input-action member-delete minor')
35914 .on('click', deleteMembership)
35915 .call(iD.svg.Icon('#operation-delete'));
35918 $list.selectAll('.member-row-new')
35922 var $add = $wrap.selectAll('.add-relation')
35927 .attr('class', 'add-relation')
35928 .call(iD.svg.Icon('#icon-plus', 'light'));
35930 $wrap.selectAll('.add-relation')
35931 .on('click', function() {
35934 $list.selectAll('.member-entity-input').node().focus();
35939 rawMembershipEditor.entityID = function(_) {
35940 if (!arguments.length) return id;
35942 return rawMembershipEditor;
35945 return rawMembershipEditor;
35947 iD.ui.RawTagEditor = function(context) {
35948 var event = d3.dispatch('change'),
35955 function rawTagEditor(selection) {
35956 var count = Object.keys(tags).filter(function(d) { return d; }).length;
35958 selection.call(iD.ui.Disclosure()
35959 .title(t('inspector.all_tags') + ' (' + count + ')')
35960 .expanded(context.storage('raw_tag_editor.expanded') === 'true' || preset.isFallback())
35961 .on('toggled', toggled)
35962 .content(content));
35964 function toggled(expanded) {
35965 context.storage('raw_tag_editor.expanded', expanded);
35967 selection.node().parentNode.scrollTop += 200;
35972 function content($wrap) {
35973 var entries = d3.entries(tags);
35975 if (!entries.length || showBlank) {
35977 entries.push({key: '', value: ''});
35980 var $list = $wrap.selectAll('.tag-list')
35983 $list.enter().append('ul')
35984 .attr('class', 'tag-list');
35986 var $newTag = $wrap.selectAll('.add-tag')
35991 .attr('class', 'add-tag')
35992 .call(iD.svg.Icon('#icon-plus', 'light'));
35994 $newTag.on('click', addTag);
35996 var $items = $list.selectAll('li')
35997 .data(entries, function(d) { return d.key; });
36001 var $enter = $items.enter().append('li')
36002 .attr('class', 'tag-row cf');
36004 $enter.append('div')
36005 .attr('class', 'key-wrap')
36007 .property('type', 'text')
36008 .attr('class', 'key')
36009 .attr('maxlength', 255);
36011 $enter.append('div')
36012 .attr('class', 'input-wrap-position')
36014 .property('type', 'text')
36015 .attr('class', 'value')
36016 .attr('maxlength', 255);
36018 $enter.append('button')
36019 .attr('tabindex', -1)
36020 .attr('class', 'remove minor')
36021 .call(iD.svg.Icon('#operation-delete'));
36023 if (context.taginfo()) {
36024 $enter.each(bindTypeahead);
36031 $items.each(function(tag) {
36032 var isRelation = (context.entity(id).type === 'relation'),
36034 if (isRelation && tag.key === 'type')
36035 reference = iD.ui.TagReference({rtype: tag.value}, context);
36037 reference = iD.ui.TagReference({key: tag.key, value: tag.value}, context);
36039 if (state === 'hover') {
36040 reference.showing(false);
36044 .call(reference.button)
36045 .call(reference.body);
36048 $items.select('input.key')
36049 .value(function(d) { return d.key; })
36050 .on('blur', keyChange)
36051 .on('change', keyChange);
36053 $items.select('input.value')
36054 .value(function(d) { return d.value; })
36055 .on('blur', valueChange)
36056 .on('change', valueChange)
36057 .on('keydown.push-more', pushMore);
36059 $items.select('button.remove')
36060 .on('click', removeTag);
36066 function pushMore() {
36067 if (d3.event.keyCode === 9 && !d3.event.shiftKey &&
36068 $list.selectAll('li:last-child input.value').node() === this) {
36073 function bindTypeahead() {
36074 var row = d3.select(this),
36075 key = row.selectAll('input.key'),
36076 value = row.selectAll('input.value');
36078 function sort(value, data) {
36079 var sameletter = [],
36081 for (var i = 0; i < data.length; i++) {
36082 if (data[i].value.substring(0, value.length) === value) {
36083 sameletter.push(data[i]);
36085 other.push(data[i]);
36088 return sameletter.concat(other);
36091 key.call(d3.combobox()
36092 .fetcher(function(value, callback) {
36093 context.taginfo().keys({
36095 geometry: context.geometry(id),
36097 }, function(err, data) {
36098 if (!err) callback(sort(value, data));
36102 value.call(d3.combobox()
36103 .fetcher(function(value, callback) {
36104 context.taginfo().values({
36107 geometry: context.geometry(id),
36109 }, function(err, data) {
36110 if (!err) callback(sort(value, data));
36115 function unbind() {
36116 var row = d3.select(this);
36118 row.selectAll('input.key')
36119 .call(d3.combobox.off);
36121 row.selectAll('input.value')
36122 .call(d3.combobox.off);
36125 function keyChange(d) {
36127 kNew = this.value.trim(),
36130 if (kNew && kNew !== kOld) {
36131 var match = kNew.match(/^(.*?)(?:_(\d+))?$/),
36133 suffix = +(match[2] || 1);
36134 while (tags[kNew]) { // rename key if already in use
36135 kNew = base + '_' + suffix++;
36138 tag[kOld] = undefined;
36139 tag[kNew] = d.value;
36140 d.key = kNew; // Maintain DOM identity through the subsequent update.
36145 function valueChange(d) {
36147 tag[d.key] = this.value;
36151 function removeTag(d) {
36153 tag[d.key] = undefined;
36155 d3.select(this.parentNode).remove();
36158 function addTag() {
36159 // Wrapped in a setTimeout in case it's being called from a blur
36160 // handler. Without the setTimeout, the call to `content` would
36161 // wipe out the pending value change.
36162 setTimeout(function() {
36165 $list.selectAll('li:last-child input.key').node().focus();
36170 rawTagEditor.state = function(_) {
36171 if (!arguments.length) return state;
36173 return rawTagEditor;
36176 rawTagEditor.preset = function(_) {
36177 if (!arguments.length) return preset;
36179 return rawTagEditor;
36182 rawTagEditor.tags = function(_) {
36183 if (!arguments.length) return tags;
36185 return rawTagEditor;
36188 rawTagEditor.entityID = function(_) {
36189 if (!arguments.length) return id;
36191 return rawTagEditor;
36194 return d3.rebind(rawTagEditor, event, 'on');
36196 iD.ui.Restore = function(context) {
36197 return function(selection) {
36198 if (!context.history().lock() || !context.history().restorableChanges())
36201 var modal = iD.ui.modal(selection);
36203 modal.select('.modal')
36204 .attr('class', 'modal fillL col6');
36206 var introModal = modal.select('.content');
36208 introModal.attr('class','cf');
36210 introModal.append('div')
36211 .attr('class', 'modal-section')
36213 .text(t('restore.heading'));
36215 introModal.append('div')
36216 .attr('class','modal-section')
36218 .text(t('restore.description'));
36220 var buttonWrap = introModal.append('div')
36221 .attr('class', 'modal-actions cf');
36223 var restore = buttonWrap.append('button')
36224 .attr('class', 'restore col6')
36225 .text(t('restore.restore'))
36226 .on('click', function() {
36227 context.history().restore();
36231 buttonWrap.append('button')
36232 .attr('class', 'reset col6')
36233 .text(t('restore.reset'))
36234 .on('click', function() {
36235 context.history().clearSaved();
36239 restore.node().focus();
36242 iD.ui.Save = function(context) {
36243 var history = context.history(),
36244 key = iD.ui.cmd('⌘S');
36246 function saving() {
36247 return context.mode().id === 'save';
36251 d3.event.preventDefault();
36252 if (!context.inIntro() && !saving() && history.hasChanges()) {
36253 context.enter(iD.modes.Save(context));
36257 return function(selection) {
36258 var tooltip = bootstrap.tooltip()
36259 .placement('bottom')
36261 .title(iD.ui.tooltipHtml(t('save.no_changes'), key));
36263 var button = selection.append('button')
36264 .attr('class', 'save col12 disabled')
36265 .attr('tabindex', -1)
36269 button.append('span')
36270 .attr('class', 'label')
36271 .text(t('save.title'));
36273 button.append('span')
36274 .attr('class', 'count')
36277 var keybinding = d3.keybinding('undo-redo')
36278 .on(key, save, true);
36280 d3.select(document)
36283 var numChanges = 0;
36285 context.history().on('change.save', function() {
36286 var _ = history.difference().summary().length;
36287 if (_ === numChanges)
36291 tooltip.title(iD.ui.tooltipHtml(t(numChanges > 0 ?
36292 'save.help' : 'save.no_changes'), key));
36295 .classed('disabled', numChanges === 0)
36296 .classed('has-count', numChanges > 0);
36298 button.select('span.count')
36302 context.on('enter.save', function() {
36303 button.property('disabled', saving());
36304 if (saving()) button.call(tooltip.hide);
36308 iD.ui.Scale = function(context) {
36309 var projection = context.projection,
36313 function scaleDefs(loc1, loc2) {
36314 var lat = (loc2[1] + loc1[1]) / 2,
36315 imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
36316 conversion = (imperial ? 3.28084 : 1),
36317 dist = iD.geo.lonToMeters(loc2[0] - loc1[0], lat) * conversion,
36318 scale = { dist: 0, px: 0, text: '' },
36319 buckets, i, val, dLon;
36322 buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
36324 buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
36327 // determine a user-friendly endpoint for the scale
36328 for (i = 0; i < buckets.length; i++) {
36331 scale.dist = Math.floor(dist / val) * val;
36336 dLon = iD.geo.metersToLon(scale.dist / conversion, lat);
36337 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
36340 if (scale.dist >= 5280) {
36341 scale.dist /= 5280;
36342 scale.text = String(scale.dist) + ' mi';
36344 scale.text = String(scale.dist) + ' ft';
36347 if (scale.dist >= 1000) {
36348 scale.dist /= 1000;
36349 scale.text = String(scale.dist) + ' km';
36351 scale.text = String(scale.dist) + ' m';
36358 function update(selection) {
36359 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
36360 var dims = context.map().dimensions(),
36361 loc1 = projection.invert([0, dims[1]]),
36362 loc2 = projection.invert([maxLength, dims[1]]),
36363 scale = scaleDefs(loc1, loc2);
36365 selection.select('#scalepath')
36366 .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
36368 selection.select('#scaletext')
36369 .attr('x', scale.px + 8)
36370 .attr('y', tickHeight)
36374 return function(selection) {
36375 var g = selection.append('svg')
36376 .attr('id', 'scale')
36378 .attr('transform', 'translate(10,11)');
36380 g.append('path').attr('id', 'scalepath');
36381 g.append('text').attr('id', 'scaletext');
36385 context.map().on('move.scale', function() {
36390 iD.ui.SelectionList = function(context, selectedIDs) {
36392 function selectEntity(entity) {
36393 context.enter(iD.modes.Select(context, [entity.id]).suppressMenu(true));
36397 function selectionList(selection) {
36398 selection.classed('selection-list-pane', true);
36400 var header = selection.append('div')
36401 .attr('class', 'header fillL cf');
36403 header.append('h3')
36404 .text(t('inspector.multiselect'));
36406 var listWrap = selection.append('div')
36407 .attr('class', 'inspector-body');
36409 var list = listWrap.append('div')
36410 .attr('class', 'feature-list cf');
36412 context.history().on('change.selection-list', drawList);
36415 function drawList() {
36416 var entities = selectedIDs
36417 .map(function(id) { return context.hasEntity(id); })
36418 .filter(function(entity) { return entity; });
36420 var items = list.selectAll('.feature-list-item')
36421 .data(entities, iD.Entity.key);
36423 var enter = items.enter().append('button')
36424 .attr('class', 'feature-list-item')
36425 .on('click', selectEntity);
36428 var label = enter.append('div')
36429 .attr('class', 'label')
36430 .call(iD.svg.Icon('', 'pre-text'));
36432 label.append('span')
36433 .attr('class', 'entity-type');
36435 label.append('span')
36436 .attr('class', 'entity-name');
36439 items.selectAll('use')
36440 .attr('href', function() {
36441 var entity = this.parentElement.parentElement.__data__;
36442 return '#icon-' + context.geometry(entity.id);
36445 items.selectAll('.entity-type')
36446 .text(function(entity) { return context.presets().match(entity, context.graph()).name(); });
36448 items.selectAll('.entity-name')
36449 .text(function(entity) { return iD.util.displayName(entity); });
36457 return selectionList;
36460 iD.ui.Sidebar = function(context) {
36461 var inspector = iD.ui.Inspector(context),
36464 function sidebar(selection) {
36465 var featureListWrap = selection.append('div')
36466 .attr('class', 'feature-list-pane')
36467 .call(iD.ui.FeatureList(context));
36469 selection.call(iD.ui.Notice(context));
36471 var inspectorWrap = selection.append('div')
36472 .attr('class', 'inspector-hidden inspector-wrap fr');
36474 function hover(id) {
36475 if (!current && context.hasEntity(id)) {
36476 featureListWrap.classed('inspector-hidden', true);
36477 inspectorWrap.classed('inspector-hidden', false)
36478 .classed('inspector-hover', true);
36480 if (inspector.entityID() !== id || inspector.state() !== 'hover') {
36485 inspectorWrap.call(inspector);
36487 } else if (!current) {
36488 featureListWrap.classed('inspector-hidden', false);
36489 inspectorWrap.classed('inspector-hidden', true);
36490 inspector.state('hide');
36494 sidebar.hover = _.throttle(hover, 200);
36496 sidebar.select = function(id, newFeature) {
36497 if (!current && id) {
36498 featureListWrap.classed('inspector-hidden', true);
36499 inspectorWrap.classed('inspector-hidden', false)
36500 .classed('inspector-hover', false);
36502 if (inspector.entityID() !== id || inspector.state() !== 'select') {
36506 .newFeature(newFeature);
36508 inspectorWrap.call(inspector);
36510 } else if (!current) {
36511 featureListWrap.classed('inspector-hidden', false);
36512 inspectorWrap.classed('inspector-hidden', true);
36513 inspector.state('hide');
36517 sidebar.show = function(component) {
36518 featureListWrap.classed('inspector-hidden', true);
36519 inspectorWrap.classed('inspector-hidden', true);
36520 if (current) current.remove();
36521 current = selection.append('div')
36522 .attr('class', 'sidebar-component')
36526 sidebar.hide = function() {
36527 featureListWrap.classed('inspector-hidden', false);
36528 inspectorWrap.classed('inspector-hidden', true);
36529 if (current) current.remove();
36534 sidebar.hover = function() {};
36535 sidebar.hover.cancel = function() {};
36536 sidebar.select = function() {};
36537 sidebar.show = function() {};
36538 sidebar.hide = function() {};
36542 iD.ui.SourceSwitch = function(context) {
36546 d3.event.preventDefault();
36548 if (context.history().hasChanges() &&
36549 !window.confirm(t('source_switch.lose_changes'))) return;
36551 var live = d3.select(this)
36554 context.connection()
36555 .switch(live ? keys[1] : keys[0]);
36557 context.enter(iD.modes.Browse(context));
36561 .text(live ? t('source_switch.dev') : t('source_switch.live'))
36562 .classed('live', !live);
36565 var sourceSwitch = function(selection) {
36566 selection.append('a')
36568 .text(t('source_switch.live'))
36569 .classed('live', true)
36570 .attr('tabindex', -1)
36571 .on('click', click);
36574 sourceSwitch.keys = function(_) {
36575 if (!arguments.length) return keys;
36577 return sourceSwitch;
36580 return sourceSwitch;
36582 iD.ui.Spinner = function(context) {
36583 var connection = context.connection();
36585 return function(selection) {
36586 var img = selection.append('img')
36587 .attr('src', context.imagePath('loader-black.gif'))
36588 .style('opacity', 0);
36590 connection.on('loading.spinner', function() {
36592 .style('opacity', 1);
36595 connection.on('loaded.spinner', function() {
36597 .style('opacity', 0);
36601 iD.ui.Splash = function(context) {
36602 return function(selection) {
36603 if (context.storage('sawSplash'))
36606 context.storage('sawSplash', true);
36608 var modal = iD.ui.modal(selection);
36610 modal.select('.modal')
36611 .attr('class', 'modal-splash modal col6');
36613 var introModal = modal.select('.content')
36615 .attr('class', 'fillL');
36617 introModal.append('div')
36618 .attr('class','modal-section cf')
36619 .append('h3').text(t('splash.welcome'));
36621 introModal.append('div')
36622 .attr('class','modal-section')
36624 .html(t('splash.text', {
36625 version: iD.version,
36626 website: '<a href="http://ideditor.com/">ideditor.com</a>',
36627 github: '<a href="https://github.com/openstreetmap/iD">github.com</a>'
36630 var buttons = introModal.append('div').attr('class', 'modal-actions cf');
36632 buttons.append('button')
36633 .attr('class', 'col6 walkthrough')
36634 .text(t('splash.walkthrough'))
36635 .on('click', function() {
36636 d3.select(document.body).call(iD.ui.intro(context));
36640 buttons.append('button')
36641 .attr('class', 'col6 start')
36642 .text(t('splash.start'))
36643 .on('click', modal.close);
36645 modal.select('button.close').attr('class','hide');
36649 iD.ui.Status = function(context) {
36650 var connection = context.connection(),
36653 return function(selection) {
36655 function update() {
36657 connection.status(function(err, apiStatus) {
36659 selection.html('');
36661 if (err && errCount++ < 2) return;
36664 selection.text(t('status.error'));
36666 } else if (apiStatus === 'readonly') {
36667 selection.text(t('status.readonly'));
36669 } else if (apiStatus === 'offline') {
36670 selection.text(t('status.offline'));
36673 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
36674 if (!err) errCount = 0;
36679 connection.on('auth', function() { update(selection); });
36680 window.setInterval(update, 90000);
36684 iD.ui.Success = function(context) {
36685 var dispatch = d3.dispatch('cancel'),
36688 function success(selection) {
36689 var message = (changeset.comment || t('success.edited_osm')).substring(0, 130) +
36690 ' ' + context.connection().changesetURL(changeset.id);
36692 var header = selection.append('div')
36693 .attr('class', 'header fillL');
36695 header.append('button')
36696 .attr('class', 'fr')
36697 .on('click', function() { dispatch.cancel(); })
36698 .call(iD.svg.Icon('#icon-close'));
36700 header.append('h3')
36701 .text(t('success.just_edited'));
36703 var body = selection.append('div')
36704 .attr('class', 'body save-success fillL');
36707 .html(t('success.help_html'));
36709 var changesetURL = context.connection().changesetURL(changeset.id);
36712 .attr('class', 'button col12 osm')
36713 .attr('target', '_blank')
36714 .attr('href', changesetURL)
36715 .text(t('success.view_on_osm'));
36718 facebook: 'https://facebook.com/sharer/sharer.php?u=' + encodeURIComponent(changesetURL),
36719 twitter: 'https://twitter.com/intent/tweet?source=webclient&text=' + encodeURIComponent(message),
36720 google: 'https://plus.google.com/share?url=' + encodeURIComponent(changesetURL)
36723 body.selectAll('.button.social')
36724 .data(d3.entries(sharing))
36727 .attr('class', 'button social col4')
36728 .attr('target', '_blank')
36729 .attr('href', function(d) { return d.value; })
36730 .call(bootstrap.tooltip()
36731 .title(function(d) { return t('success.' + d.key); })
36732 .placement('bottom'))
36733 .each(function(d) { d3.select(this).call(iD.svg.Icon('#logo-' + d.key, 'social')); });
36736 success.changeset = function(_) {
36737 if (!arguments.length) return changeset;
36742 return d3.rebind(success, dispatch, 'on');
36744 iD.ui.TagReference = function(tag, context) {
36745 var tagReference = {},
36751 function findLocal(data) {
36752 var locale = iD.detect().locale.toLowerCase(),
36755 localized = _.find(data, function(d) {
36756 return d.lang.toLowerCase() === locale;
36758 if (localized) return localized;
36760 // try the non-regional version of a language, like
36761 // 'en' if the language is 'en-US'
36762 if (locale.indexOf('-') !== -1) {
36763 var first = locale.split('-')[0];
36764 localized = _.find(data, function(d) {
36765 return d.lang.toLowerCase() === first;
36767 if (localized) return localized;
36770 // finally fall back to english
36771 return _.find(data, function(d) {
36772 return d.lang.toLowerCase() === 'en';
36776 function load(param) {
36777 button.classed('tag-reference-loading', true);
36779 context.taginfo().docs(param, function show(err, data) {
36781 if (!err && data) {
36782 docs = findLocal(data);
36787 if (!docs || !docs.description) {
36788 if (param.hasOwnProperty('value')) {
36789 load(_.omit(param, 'value')); // retry with key only
36791 body.append('p').text(t('inspector.no_documentation_key'));
36797 if (docs.image && docs.image.thumb_url_prefix) {
36800 .attr('class', 'wiki-image')
36801 .attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix)
36802 .on('load', function() { done(); })
36803 .on('error', function() { d3.select(this).remove(); done(); });
36810 .text(docs.description);
36814 .attr('target', '_blank')
36815 .attr('href', 'http://wiki.openstreetmap.org/wiki/' + docs.title)
36816 .call(iD.svg.Icon('#icon-out-link', 'inline'))
36818 .text(t('inspector.reference'));
36825 button.classed('tag-reference-loading', false);
36829 .style('max-height', '200px')
36830 .style('opacity', '1');
36835 function hide(selection) {
36836 selection = selection || body.transition().duration(200);
36839 .style('max-height', '0px')
36840 .style('opacity', '0');
36845 tagReference.button = function(selection) {
36846 button = selection.selectAll('.tag-reference-button')
36851 .attr('class', 'tag-reference-button')
36852 .attr('tabindex', -1)
36853 .call(iD.svg.Icon('#icon-inspect'));
36855 button.on('click', function () {
36856 d3.event.stopPropagation();
36857 d3.event.preventDefault();
36860 } else if (loaded) {
36863 if (context.taginfo()) {
36870 tagReference.body = function(selection) {
36871 body = selection.selectAll('.tag-reference-body')
36874 body.enter().append('div')
36875 .attr('class', 'tag-reference-body cf')
36876 .style('max-height', '0')
36877 .style('opacity', '0');
36879 if (showing === false) {
36884 tagReference.showing = function(_) {
36885 if (!arguments.length) return showing;
36887 return tagReference;
36890 return tagReference;
36892 // toggles the visibility of ui elements, using a combination of the
36893 // hide class, which sets display=none, and a d3 transition for opacity.
36894 // this will cause blinking when called repeatedly, so check that the
36895 // value actually changes between calls.
36896 iD.ui.Toggle = function(show, callback) {
36897 return function(selection) {
36899 .style('opacity', show ? 0 : 1)
36900 .classed('hide', false)
36902 .style('opacity', show ? 1 : 0)
36903 .each('end', function() {
36904 d3.select(this).classed('hide', !show);
36905 if (callback) callback.apply(this);
36909 iD.ui.UndoRedo = function(context) {
36912 cmd: iD.ui.cmd('⌘Z'),
36913 action: function() { if (!(context.inIntro() || saving())) context.undo(); },
36914 annotation: function() { return context.history().undoAnnotation(); }
36917 cmd: iD.ui.cmd('⌘⇧Z'),
36918 action: function() {if (!(context.inIntro() || saving())) context.redo(); },
36919 annotation: function() { return context.history().redoAnnotation(); }
36922 function saving() {
36923 return context.mode().id === 'save';
36926 return function(selection) {
36927 var tooltip = bootstrap.tooltip()
36928 .placement('bottom')
36930 .title(function (d) {
36931 return iD.ui.tooltipHtml(d.annotation() ?
36932 t(d.id + '.tooltip', {action: d.annotation()}) :
36933 t(d.id + '.nothing'), d.cmd);
36936 var buttons = selection.selectAll('button')
36938 .enter().append('button')
36939 .attr('class', 'col6 disabled')
36940 .on('click', function(d) { return d.action(); })
36943 buttons.each(function(d) {
36945 .call(iD.svg.Icon('#icon-' + d.id));
36948 var keybinding = d3.keybinding('undo')
36949 .on(commands[0].cmd, function() { d3.event.preventDefault(); commands[0].action(); })
36950 .on(commands[1].cmd, function() { d3.event.preventDefault(); commands[1].action(); });
36952 d3.select(document)
36956 .on('change.undo_redo', update);
36959 .on('enter.undo_redo', update);
36961 function update() {
36963 .property('disabled', saving())
36964 .classed('disabled', function(d) { return !d.annotation(); })
36966 var selection = d3.select(this);
36967 if (selection.property('tooltipVisible')) {
36968 selection.call(tooltip.show);
36974 iD.ui.ViewOnOSM = function(context) {
36977 function viewOnOSM(selection) {
36978 var entity = context.entity(id);
36980 selection.style('display', entity.isNew() ? 'none' : null);
36982 var $link = selection.selectAll('.view-on-osm')
36987 .attr('class', 'view-on-osm')
36988 .attr('target', '_blank')
36989 .call(iD.svg.Icon('#icon-out-link', 'inline'))
36991 .text(t('inspector.view_on_osm'));
36994 .attr('href', context.connection().entityURL(entity));
36997 viewOnOSM.entityID = function(_) {
36998 if (!arguments.length) return id;
37005 iD.ui.Zoom = function(context) {
37009 title: t('zoom.in'),
37010 action: context.zoomIn,
37015 title: t('zoom.out'),
37016 action: context.zoomOut,
37020 function zoomIn() {
37021 d3.event.preventDefault();
37022 if (!context.inIntro()) context.zoomIn();
37025 function zoomOut() {
37026 d3.event.preventDefault();
37027 if (!context.inIntro()) context.zoomOut();
37030 function zoomInFurther() {
37031 d3.event.preventDefault();
37032 if (!context.inIntro()) context.zoomInFurther();
37035 function zoomOutFurther() {
37036 d3.event.preventDefault();
37037 if (!context.inIntro()) context.zoomOutFurther();
37041 return function(selection) {
37042 var button = selection.selectAll('button')
37044 .enter().append('button')
37045 .attr('tabindex', -1)
37046 .attr('class', function(d) { return d.id; })
37047 .on('click.editor', function(d) { d.action(); })
37048 .call(bootstrap.tooltip()
37051 .title(function(d) {
37052 return iD.ui.tooltipHtml(d.title, d.key);
37055 button.each(function(d) {
37057 .call(iD.svg.Icon('#icon-' + d.icon, 'light'));
37060 var keybinding = d3.keybinding('zoom');
37062 _.each(['=','ffequals','plus','ffplus'], function(key) {
37063 keybinding.on(key, zoomIn);
37064 keybinding.on('⇧' + key, zoomIn);
37065 keybinding.on(iD.ui.cmd('⌘' + key), zoomInFurther);
37066 keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomInFurther);
37068 _.each(['-','ffminus','_','dash'], function(key) {
37069 keybinding.on(key, zoomOut);
37070 keybinding.on('⇧' + key, zoomOut);
37071 keybinding.on(iD.ui.cmd('⌘' + key), zoomOutFurther);
37072 keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomOutFurther);
37075 d3.select(document)
37079 iD.ui.preset.access = function(field) {
37080 var dispatch = d3.dispatch('change'),
37083 function access(selection) {
37084 var wrap = selection.selectAll('.preset-input-wrap')
37087 wrap.enter().append('div')
37088 .attr('class', 'cf preset-input-wrap')
37091 items = wrap.select('ul').selectAll('li')
37096 var enter = items.enter().append('li')
37097 .attr('class', function(d) { return 'cf preset-access-' + d; });
37099 enter.append('span')
37100 .attr('class', 'col6 label preset-label-access')
37101 .attr('for', function(d) { return 'preset-input-access-' + d; })
37102 .text(function(d) { return field.t('types.' + d); });
37104 enter.append('div')
37105 .attr('class', 'col6 preset-input-access-wrap')
37107 .attr('type', 'text')
37108 .attr('class', 'preset-input-access')
37109 .attr('id', function(d) { return 'preset-input-access-' + d; })
37110 .each(function(d) {
37112 .call(d3.combobox()
37113 .data(access.options(d)));
37118 wrap.selectAll('.preset-input-access')
37119 .on('change', change)
37120 .on('blur', change);
37123 function change(d) {
37125 tag[d] = d3.select(this).value() || undefined;
37126 dispatch.change(tag);
37129 access.options = function(type) {
37130 var options = ['no', 'permissive', 'private', 'destination'];
37132 if (type !== 'access') {
37133 options.unshift('yes');
37134 options.push('designated');
37136 if (type === 'bicycle') {
37137 options.push('dismount');
37141 return options.map(function(option) {
37143 title: field.t('options.' + option + '.description'),
37149 var placeholders = {
37151 foot: 'designated',
37152 motor_vehicle: 'no'
37156 motor_vehicle: 'no',
37162 motor_vehicle: 'no'
37165 motor_vehicle: 'no',
37166 bicycle: 'designated'
37169 motor_vehicle: 'no',
37170 horse: 'designated'
37174 motor_vehicle: 'no',
37180 motor_vehicle: 'yes',
37185 motor_vehicle: 'yes'
37189 motor_vehicle: 'yes',
37195 motor_vehicle: 'yes',
37201 motor_vehicle: 'yes',
37207 motor_vehicle: 'yes',
37213 motor_vehicle: 'yes',
37219 motor_vehicle: 'yes',
37225 motor_vehicle: 'yes',
37230 motor_vehicle: 'yes'
37234 motor_vehicle: 'yes',
37240 motor_vehicle: 'yes',
37246 motor_vehicle: 'yes',
37252 access.tags = function(tags) {
37253 items.selectAll('.preset-input-access')
37254 .value(function(d) { return tags[d] || ''; })
37255 .attr('placeholder', function() {
37256 return tags.access ? tags.access : field.placeholder();
37259 // items.selectAll('#preset-input-access-access')
37260 // .attr('placeholder', 'yes');
37262 _.forEach(placeholders[tags.highway], function(v, k) {
37263 items.selectAll('#preset-input-access-' + k)
37264 .attr('placeholder', function() { return (tags.access || v); });
37268 access.focus = function() {
37269 items.selectAll('.preset-input-access')
37273 return d3.rebind(access, dispatch, 'on');
37275 iD.ui.preset.address = function(field, context) {
37276 var dispatch = d3.dispatch('init', 'change'),
37289 function getStreets() {
37290 var extent = entity.extent(context.graph()),
37291 l = extent.center(),
37292 box = iD.geo.Extent(l).padByMeters(200);
37294 return context.intersects(box)
37295 .filter(isAddressable)
37297 var loc = context.projection([
37298 (extent[0][0] + extent[1][0]) / 2,
37299 (extent[0][1] + extent[1][1]) / 2]),
37300 choice = iD.geo.chooseEdge(context.childNodes(d), loc, context.projection);
37302 title: d.tags.name,
37303 value: d.tags.name,
37304 dist: choice.distance
37306 }).sort(function(a, b) {
37307 return a.dist - b.dist;
37310 function isAddressable(d) {
37311 return d.tags.highway && d.tags.name && d.type === 'way';
37315 function getCities() {
37316 var extent = entity.extent(context.graph()),
37317 l = extent.center(),
37318 box = iD.geo.Extent(l).padByMeters(200);
37320 return context.intersects(box)
37321 .filter(isAddressable)
37324 title: d.tags['addr:city'] || d.tags.name,
37325 value: d.tags['addr:city'] || d.tags.name,
37326 dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
37328 }).sort(function(a, b) {
37329 return a.dist - b.dist;
37332 function isAddressable(d) {
37334 (d.tags.admin_level === '8' || d.tags.border_type === 'city'))
37337 if (d.tags.place && d.tags.name && (
37338 d.tags.place === 'city' ||
37339 d.tags.place === 'town' ||
37340 d.tags.place === 'village'))
37343 if (d.tags['addr:city']) return true;
37349 function getPostCodes() {
37350 var extent = entity.extent(context.graph()),
37351 l = extent.center(),
37352 box = iD.geo.Extent(l).padByMeters(200);
37354 return context.intersects(box)
37355 .filter(isAddressable)
37358 title: d.tags['addr:postcode'],
37359 value: d.tags['addr:postcode'],
37360 dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
37362 }).sort(function(a, b) {
37363 return a.dist - b.dist;
37366 function isAddressable(d) {
37367 return d.tags['addr:postcode'];
37371 function address(selection) {
37372 isInitialized = false;
37374 selection.selectAll('.preset-input-wrap')
37377 var center = entity.extent(context.graph()).center(),
37382 wrap = selection.append('div')
37383 .attr('class', 'preset-input-wrap');
37385 iD.countryCode().search(center, function (err, countryCode) {
37386 addressFormat = _.find(iD.data.addressFormats, function (a) {
37387 return a && a.countryCodes && _.contains(a.countryCodes, countryCode);
37388 }) || _.first(iD.data.addressFormats);
37391 // Normalize widths.
37392 var total = _.reduce(r, function(sum, field) {
37393 return sum + (widths[field] || 0.5);
37396 return r.map(function (field) {
37399 width: (widths[field] || 0.5) / total
37404 wrap.selectAll('div')
37405 .data(addressFormat.format)
37408 .attr('class', 'addr-row')
37409 .selectAll('input')
37413 .property('type', 'text')
37414 .attr('placeholder', function (d) { return field.t('placeholders.' + d.id); })
37415 .attr('class', function (d) { return 'addr-' + d.id; })
37416 .style('width', function (d) { return d.width * 100 + '%'; });
37420 wrap.selectAll('.addr-street')
37421 .call(d3.combobox()
37422 .fetcher(function(value, callback) {
37423 callback(getStreets());
37426 wrap.selectAll('.addr-city')
37427 .call(d3.combobox()
37428 .fetcher(function(value, callback) {
37429 callback(getCities());
37432 wrap.selectAll('.addr-postcode')
37433 .call(d3.combobox()
37434 .fetcher(function(value, callback) {
37435 callback(getPostCodes());
37438 wrap.selectAll('input')
37439 .on('blur', change())
37440 .on('change', change());
37442 wrap.selectAll('input:not(.combobox-input)')
37443 .on('input', change(true));
37446 isInitialized = true;
37450 function change(onInput) {
37451 return function() {
37454 wrap.selectAll('input')
37455 .each(function (field) {
37456 tags['addr:' + field.id] = this.value || undefined;
37459 dispatch.change(tags, onInput);
37463 function updateTags(tags) {
37464 wrap.selectAll('input')
37465 .value(function (field) {
37466 return tags['addr:' + field.id] || '';
37470 address.entity = function(_) {
37471 if (!arguments.length) return entity;
37476 address.tags = function(tags) {
37477 if (isInitialized) {
37480 dispatch.on('init', function () {
37486 address.focus = function() {
37487 var node = wrap.selectAll('input').node();
37488 if (node) node.focus();
37491 return d3.rebind(address, dispatch, 'on');
37493 iD.ui.preset.check =
37494 iD.ui.preset.defaultcheck = function(field) {
37495 var dispatch = d3.dispatch('change'),
37496 options = field.strings && field.strings.options,
37499 entity, value, box, text, label;
37502 for (var k in options) {
37503 values.push(k === 'undefined' ? undefined : k);
37504 texts.push(field.t('options.' + k, { 'default': options[k] }));
37507 values = [undefined, 'yes'];
37508 texts = [t('inspector.unknown'), t('inspector.check.yes')];
37509 if (field.type === 'check') {
37511 texts.push(t('inspector.check.no'));
37515 var check = function(selection) {
37516 // hack: pretend oneway field is a oneway_yes field
37517 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
37518 if (field.id === 'oneway') {
37519 for (var key in entity.tags) {
37520 if (key in iD.oneWayTags && (entity.tags[key] in iD.oneWayTags[key])) {
37521 texts[0] = t('presets.fields.oneway_yes.options.undefined');
37527 selection.classed('checkselect', 'true');
37529 label = selection.selectAll('.preset-input-wrap')
37532 var enter = label.enter().append('label')
37533 .attr('class', 'preset-input-wrap');
37535 enter.append('input')
37536 .property('indeterminate', field.type === 'check')
37537 .attr('type', 'checkbox')
37538 .attr('id', 'preset-input-' + field.id);
37540 enter.append('span')
37542 .attr('class', 'value');
37544 box = label.select('input')
37545 .on('click', function() {
37547 t[field.key] = values[(values.indexOf(value) + 1) % values.length];
37548 dispatch.change(t);
37549 d3.event.stopPropagation();
37552 text = label.select('span.value');
37555 check.entity = function(_) {
37556 if (!arguments.length) return entity;
37561 check.tags = function(tags) {
37562 value = tags[field.key];
37563 box.property('indeterminate', field.type === 'check' && !value);
37564 box.property('checked', value === 'yes');
37565 text.text(texts[values.indexOf(value)]);
37566 label.classed('set', !!value);
37569 check.focus = function() {
37570 box.node().focus();
37573 return d3.rebind(check, dispatch, 'on');
37575 iD.ui.preset.combo =
37576 iD.ui.preset.typeCombo = function(field, context) {
37577 var dispatch = d3.dispatch('change'),
37578 optstrings = field.strings && field.strings.options,
37579 optarray = field.options,
37580 snake_case = (field.snake_case || (field.snake_case === undefined)),
37584 function snake(s) {
37585 return s.replace(/\s+/g, '_');
37588 function unsnake(s) {
37589 return s.replace(/_+/g, ' ');
37592 function clean(s) {
37593 return s.split(';')
37594 .map(function(s) { return s.trim(); })
37598 function optString() {
37599 return _.find(_.keys(strings), function(k) {
37600 return strings[k] === input.value();
37604 function combo(selection) {
37605 var combobox = d3.combobox();
37607 input = selection.selectAll('input')
37610 var enter = input.enter()
37612 .attr('type', 'text')
37613 .attr('id', 'preset-input-' + field.id);
37615 if (optstrings) { enter.attr('readonly', 'readonly'); }
37619 .on('change', change)
37620 .on('blur', change)
37623 _.each(optstrings, function(v, k) {
37624 strings[k] = field.t('options.' + k, { 'default': v });
37627 } else if (optarray) {
37628 _.each(optarray, function(k) {
37629 strings[k] = (snake_case ? unsnake(k) : k);
37632 } else if (context.taginfo()) {
37633 context.taginfo().values({key: field.key}, function(err, data) {
37635 _.each(_.pluck(data, 'value'), function(k) {
37636 strings[k] = (snake_case ? unsnake(k) : k);
37644 function stringsLoaded() {
37645 var keys = _.keys(strings),
37649 combobox.data(keys.map(function(k) {
37650 var s = strings[k],
37652 o.title = o.value = s;
37653 if (s.length < 20) { strs.push(s); }
37657 placeholders = strs.length > 1 ? strs : keys;
37658 input.attr('placeholder', field.placeholder() ||
37659 (placeholders.slice(0, 3).join(', ') + '...'));
37663 function change() {
37664 var value = optString() || clean(input.value());
37667 value = snake(value);
37669 if (field.type === 'typeCombo' && !value) {
37674 t[field.key] = value || undefined;
37675 dispatch.change(t);
37678 combo.tags = function(tags) {
37679 var key = tags[field.key],
37680 optstring = optString(),
37681 value = strings[key] || key || '';
37683 if (field.type === 'typeCombo' && value.toLowerCase() === 'yes') {
37686 if (!optstring && snake_case) {
37687 value = unsnake(value);
37689 input.value(value);
37692 combo.focus = function() {
37693 input.node().focus();
37696 return d3.rebind(combo, dispatch, 'on');
37698 iD.ui.preset.cycleway = function(field) {
37699 var dispatch = d3.dispatch('change'),
37702 function cycleway(selection) {
37703 var wrap = selection.selectAll('.preset-input-wrap')
37706 wrap.enter().append('div')
37707 .attr('class', 'cf preset-input-wrap')
37710 items = wrap.select('ul').selectAll('li')
37715 var enter = items.enter().append('li')
37716 .attr('class', function(d) { return 'cf preset-cycleway-' + d; });
37718 enter.append('span')
37719 .attr('class', 'col6 label preset-label-cycleway')
37720 .attr('for', function(d) { return 'preset-input-cycleway-' + d; })
37721 .text(function(d) { return field.t('types.' + d); });
37723 enter.append('div')
37724 .attr('class', 'col6 preset-input-cycleway-wrap')
37726 .attr('type', 'text')
37727 .attr('class', 'preset-input-cycleway')
37728 .attr('id', function(d) { return 'preset-input-cycleway-' + d; })
37729 .each(function(d) {
37731 .call(d3.combobox()
37732 .data(cycleway.options(d)));
37737 wrap.selectAll('.preset-input-cycleway')
37738 .on('change', change)
37739 .on('blur', change);
37742 function change() {
37743 var inputs = d3.selectAll('.preset-input-cycleway')[0],
37744 left = d3.select(inputs[0]).value(),
37745 right = d3.select(inputs[1]).value(),
37747 if (left === 'none' || left === '') { left = undefined; }
37748 if (right === 'none' || right === '') { right = undefined; }
37750 // Always set both left and right as changing one can affect the other
37752 cycleway: undefined,
37753 'cycleway:left': left,
37754 'cycleway:right': right
37757 // If the left and right tags match, use the cycleway tag to tag both
37758 // sides the same way
37759 if (left === right) {
37762 'cycleway:left': undefined,
37763 'cycleway:right': undefined
37767 dispatch.change(tag);
37770 cycleway.options = function() {
37771 return d3.keys(field.strings.options).map(function(option) {
37773 title: field.t('options.' + option + '.description'),
37779 cycleway.tags = function(tags) {
37780 items.selectAll('.preset-input-cycleway')
37781 .value(function(d) {
37782 // If cycleway is set, always return that
37783 if (tags.cycleway) {
37784 return tags.cycleway;
37786 return tags[d] || '';
37788 .attr('placeholder', field.placeholder());
37791 cycleway.focus = function() {
37792 items.selectAll('.preset-input-cycleway')
37796 return d3.rebind(cycleway, dispatch, 'on');
37798 iD.ui.preset.text =
37799 iD.ui.preset.number =
37801 iD.ui.preset.email =
37802 iD.ui.preset.url = function(field) {
37804 var dispatch = d3.dispatch('change'),
37807 function i(selection) {
37808 input = selection.selectAll('input')
37811 input.enter().append('input')
37812 .attr('type', field.type)
37813 .attr('id', 'preset-input-' + field.id)
37814 .attr('placeholder', field.placeholder() || t('inspector.unknown'));
37817 .on('input', change(true))
37818 .on('blur', change())
37819 .on('change', change());
37821 if (field.type === 'number') {
37822 input.attr('type', 'text');
37824 var spinControl = selection.selectAll('.spin-control')
37827 var enter = spinControl.enter().append('div')
37828 .attr('class', 'spin-control');
37830 enter.append('button')
37832 .attr('class', 'increment');
37834 enter.append('button')
37836 .attr('class', 'decrement');
37838 spinControl.selectAll('button')
37839 .on('click', function(d) {
37840 d3.event.preventDefault();
37841 var num = parseInt(input.node().value || 0, 10);
37842 if (!isNaN(num)) input.node().value = num + d;
37848 function change(onInput) {
37849 return function() {
37851 t[field.key] = input.value() || undefined;
37852 dispatch.change(t, onInput);
37856 i.tags = function(tags) {
37857 input.value(tags[field.key] || '');
37860 i.focus = function() {
37861 input.node().focus();
37864 return d3.rebind(i, dispatch, 'on');
37866 iD.ui.preset.localized = function(field, context) {
37867 var dispatch = d3.dispatch('change', 'input'),
37868 wikipedia = iD.wikipedia(),
37869 input, localizedInputs, wikiTitles,
37872 function i(selection) {
37873 input = selection.selectAll('.localized-main')
37876 input.enter().append('input')
37877 .attr('type', 'text')
37878 .attr('id', 'preset-input-' + field.id)
37879 .attr('class', 'localized-main')
37880 .attr('placeholder', field.placeholder());
37882 if (field.id === 'name') {
37883 var preset = context.presets().match(entity, context.graph());
37884 input.call(d3.combobox().fetcher(
37885 iD.util.SuggestNames(preset, iD.data.suggestions)
37890 .on('input', change(true))
37891 .on('blur', change())
37892 .on('change', change());
37894 var translateButton = selection.selectAll('.localized-add')
37897 translateButton.enter()
37899 .attr('class', 'button-input-action localized-add minor')
37900 .call(iD.svg.Icon('#icon-plus'))
37901 .call(bootstrap.tooltip()
37902 .title(t('translate.translate'))
37903 .placement('left'));
37906 .on('click', addBlank);
37908 localizedInputs = selection.selectAll('.localized-wrap')
37911 localizedInputs.enter().append('div')
37912 .attr('class', 'localized-wrap');
37915 function addBlank() {
37916 d3.event.preventDefault();
37917 var data = localizedInputs.selectAll('div.entry').data();
37918 data.push({ lang: '', value: '' });
37919 localizedInputs.call(render, data);
37922 function change(onInput) {
37923 return function() {
37925 t[field.key] = d3.select(this).value() || undefined;
37926 dispatch.change(t, onInput);
37930 function key(lang) { return field.key + ':' + lang; }
37932 function changeLang(d) {
37933 var lang = d3.select(this).value(),
37935 language = _.find(iD.data.wikipedia, function(d) {
37936 return d[0].toLowerCase() === lang.toLowerCase() ||
37937 d[1].toLowerCase() === lang.toLowerCase();
37940 if (language) lang = language[2];
37942 if (d.lang && d.lang !== lang) {
37943 t[key(d.lang)] = undefined;
37946 var value = d3.select(this.parentNode)
37947 .selectAll('.localized-value')
37950 if (lang && value) {
37951 t[key(lang)] = value;
37952 } else if (lang && wikiTitles && wikiTitles[d.lang]) {
37953 t[key(lang)] = wikiTitles[d.lang];
37957 dispatch.change(t);
37960 function changeValue(d) {
37961 if (!d.lang) return;
37963 t[key(d.lang)] = d3.select(this).value() || undefined;
37964 dispatch.change(t);
37967 function fetcher(value, cb) {
37968 var v = value.toLowerCase();
37970 cb(iD.data.wikipedia.filter(function(d) {
37971 return d[0].toLowerCase().indexOf(v) >= 0 ||
37972 d[1].toLowerCase().indexOf(v) >= 0 ||
37973 d[2].toLowerCase().indexOf(v) >= 0;
37974 }).map(function(d) {
37975 return { value: d[1] };
37979 function render(selection, data) {
37980 var wraps = selection.selectAll('div.entry').
37981 data(data, function(d) { return d.lang; });
37983 var innerWrap = wraps.enter()
37984 .insert('div', ':first-child');
37986 innerWrap.attr('class', 'entry')
37988 var wrap = d3.select(this);
37989 var langcombo = d3.combobox().fetcher(fetcher);
37991 var label = wrap.append('label')
37992 .attr('class','form-label')
37993 .text(t('translate.localized_translation_label'))
37994 .attr('for','localized-lang');
37996 label.append('button')
37997 .attr('class', 'minor remove')
37998 .on('click', function(d){
37999 d3.event.preventDefault();
38001 t[key(d.lang)] = undefined;
38002 dispatch.change(t);
38003 d3.select(this.parentNode.parentNode)
38005 .style('max-height','240px')
38007 .style('opacity', '0')
38008 .style('max-height','0px')
38011 .call(iD.svg.Icon('#operation-delete'));
38013 wrap.append('input')
38014 .attr('class', 'localized-lang')
38015 .attr('type', 'text')
38016 .attr('placeholder',t('translate.localized_translation_language'))
38017 .on('blur', changeLang)
38018 .on('change', changeLang)
38021 wrap.append('input')
38022 .on('blur', changeValue)
38023 .on('change', changeValue)
38024 .attr('type', 'text')
38025 .attr('placeholder', t('translate.localized_translation_name'))
38026 .attr('class', 'localized-value');
38030 .style('margin-top', '0px')
38031 .style('max-height', '0px')
38032 .style('opacity', '0')
38035 .style('margin-top', '10px')
38036 .style('max-height', '240px')
38037 .style('opacity', '1')
38038 .each('end', function() {
38040 .style('max-height', '')
38041 .style('overflow', 'visible');
38047 .style('max-height','0px')
38048 .style('opacity', '0')
38049 .style('top','-10px')
38052 var entry = selection.selectAll('.entry');
38054 entry.select('.localized-lang')
38055 .value(function(d) {
38056 var lang = _.find(iD.data.wikipedia, function(lang) { return lang[2] === d.lang; });
38057 return lang ? lang[1] : d.lang;
38060 entry.select('.localized-value')
38061 .value(function(d) { return d.value; });
38064 i.tags = function(tags) {
38065 // Fetch translations from wikipedia
38066 if (tags.wikipedia && !wikiTitles) {
38068 var wm = tags.wikipedia.match(/([^:]+):(.+)/);
38069 if (wm && wm[0] && wm[1]) {
38070 wikipedia.translations(wm[1], wm[2], function(d) {
38076 input.value(tags[field.key] || '');
38078 var postfixed = [], k, m;
38080 m = k.match(/^(.*):([a-zA-Z_-]+)$/);
38081 if (m && m[1] === field.key && m[2]) {
38082 postfixed.push({ lang: m[2], value: tags[k] });
38086 localizedInputs.call(render, postfixed.reverse());
38089 i.focus = function() {
38090 input.node().focus();
38093 i.entity = function(_) {
38097 return d3.rebind(i, dispatch, 'on');
38099 iD.ui.preset.maxspeed = function(field, context) {
38100 var dispatch = d3.dispatch('change'),
38107 var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
38108 imperialValues = [20, 25, 30, 35, 40, 45, 50, 55, 65, 70];
38110 function maxspeed(selection) {
38111 combobox = d3.combobox();
38112 var unitCombobox = d3.combobox().data(['km/h', 'mph'].map(comboValues));
38114 input = selection.selectAll('#preset-input-' + field.id)
38117 input.enter().append('input')
38118 .attr('type', 'text')
38119 .attr('id', 'preset-input-' + field.id)
38120 .attr('placeholder', field.placeholder());
38124 .on('change', change)
38125 .on('blur', change);
38127 var childNodes = context.graph().childNodes(context.entity(entity.id)),
38128 loc = childNodes[~~(childNodes.length/2)].loc;
38130 imperial = _.any(iD.data.imperial.features, function(f) {
38131 return _.any(f.geometry.coordinates, function(d) {
38132 return iD.geo.pointInPolygon(loc, d[0]);
38136 unitInput = selection.selectAll('input.maxspeed-unit')
38139 unitInput.enter().append('input')
38140 .attr('type', 'text')
38141 .attr('class', 'maxspeed-unit');
38144 .on('blur', changeUnits)
38145 .on('change', changeUnits)
38146 .call(unitCombobox);
38148 function changeUnits() {
38149 imperial = unitInput.value() === 'mph';
38150 unitInput.value(imperial ? 'mph' : 'km/h');
38157 function setSuggestions() {
38158 combobox.data((imperial ? imperialValues : metricValues).map(comboValues));
38159 unitInput.value(imperial ? 'mph' : 'km/h');
38162 function comboValues(d) {
38164 value: d.toString(),
38165 title: d.toString()
38169 function change() {
38171 value = input.value();
38174 tag[field.key] = undefined;
38175 } else if (isNaN(value) || !imperial) {
38176 tag[field.key] = value;
38178 tag[field.key] = value + ' mph';
38181 dispatch.change(tag);
38184 maxspeed.tags = function(tags) {
38185 var value = tags[field.key];
38187 if (value && value.indexOf('mph') >= 0) {
38188 value = parseInt(value, 10);
38190 } else if (value) {
38196 input.value(value || '');
38199 maxspeed.focus = function() {
38200 input.node().focus();
38203 maxspeed.entity = function(_) {
38207 return d3.rebind(maxspeed, dispatch, 'on');
38209 iD.ui.preset.radio = function(field) {
38210 var dispatch = d3.dispatch('change'),
38211 labels, radios, placeholder;
38213 function radio(selection) {
38214 selection.classed('preset-radio', true);
38216 var wrap = selection.selectAll('.preset-input-wrap')
38219 var buttonWrap = wrap.enter().append('div')
38220 .attr('class', 'preset-input-wrap toggle-list');
38222 buttonWrap.append('span')
38223 .attr('class', 'placeholder');
38225 placeholder = selection.selectAll('.placeholder');
38227 labels = wrap.selectAll('label')
38228 .data(field.options || field.keys);
38230 var enter = labels.enter().append('label');
38232 enter.append('input')
38233 .attr('type', 'radio')
38234 .attr('name', field.id)
38235 .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
38236 .attr('checked', false);
38238 enter.append('span')
38239 .text(function(d) { return field.t('options.' + d, { 'default': d }); });
38241 radios = labels.selectAll('input')
38242 .on('change', change);
38245 function change() {
38247 if (field.key) t[field.key] = undefined;
38248 radios.each(function(d) {
38249 var active = d3.select(this).property('checked');
38251 if (active) t[field.key] = d;
38253 t[d] = active ? 'yes' : undefined;
38256 dispatch.change(t);
38259 radio.tags = function(tags) {
38260 function checked(d) {
38262 return tags[field.key] === d;
38264 return !!(tags[d] && tags[d] !== 'no');
38268 labels.classed('active', checked);
38269 radios.property('checked', checked);
38270 var selection = radios.filter(function() { return this.checked; });
38271 if (selection.empty()) {
38272 placeholder.text(t('inspector.none'));
38274 placeholder.text(selection.attr('value'));
38278 radio.focus = function() {
38279 radios.node().focus();
38282 return d3.rebind(radio, dispatch, 'on');
38284 iD.ui.preset.restrictions = function(field, context) {
38285 var dispatch = d3.dispatch('change'),
38289 function restrictions(selection) {
38290 var wrap = selection.selectAll('.preset-input-wrap')
38293 var enter = wrap.enter().append('div')
38294 .attr('class', 'preset-input-wrap');
38296 enter.append('div')
38297 .attr('class', 'restriction-help');
38299 enter.append('svg')
38300 .call(iD.svg.Surface(context))
38301 .call(iD.behavior.Hover(context));
38303 var intersection = iD.geo.Intersection(context.graph(), vertexID),
38304 graph = intersection.graph,
38305 vertex = graph.entity(vertexID),
38306 surface = wrap.selectAll('svg'),
38307 filter = function () { return true; },
38308 extent = iD.geo.Extent(),
38309 projection = iD.geo.RawMercator(),
38310 lines = iD.svg.Lines(projection, context),
38311 vertices = iD.svg.Vertices(projection, context),
38312 turns = iD.svg.Turns(projection, context);
38314 var d = wrap.dimensions(),
38315 c = [d[0] / 2, d[1] / 2],
38319 .scale(256 * Math.pow(2, z) / (2 * Math.PI));
38321 var s = projection(vertex.loc);
38324 .translate([c[0] - s[0], c[1] - s[1]])
38325 .clipExtent([[0, 0], d]);
38328 .call(vertices, graph, [vertex], filter, extent, z)
38329 .call(lines, graph, intersection.ways, filter)
38330 .call(turns, graph, intersection.turns(fromNodeID));
38333 .on('click.restrictions', click)
38334 .on('mouseover.restrictions', mouseover)
38335 .on('mouseout.restrictions', mouseout);
38338 .selectAll('.selected')
38339 .classed('selected', false);
38343 .selectAll('.' + intersection.highways[fromNodeID].id)
38344 .classed('selected', true);
38350 .on('change.restrictions', render);
38353 .on('resize.restrictions', render);
38356 var datum = d3.event.target.__data__;
38357 if (datum instanceof iD.Entity) {
38358 fromNodeID = intersection.adjacentNodeId(datum.id);
38360 } else if (datum instanceof iD.geo.Turn) {
38361 if (datum.restriction) {
38363 iD.actions.UnrestrictTurn(datum, projection),
38364 t('operations.restriction.annotation.delete'));
38367 iD.actions.RestrictTurn(datum, projection),
38368 t('operations.restriction.annotation.create'));
38373 function mouseover() {
38374 var datum = d3.event.target.__data__;
38375 if (datum instanceof iD.geo.Turn) {
38376 var graph = context.graph(),
38377 presets = context.presets(),
38380 if (datum.restriction) {
38381 preset = presets.match(graph.entity(datum.restriction), graph);
38383 preset = presets.item('type/restriction/' +
38384 iD.geo.inferRestriction(
38392 wrap.selectAll('.restriction-help')
38393 .text(t('operations.restriction.help.' +
38394 (datum.restriction ? 'toggle_off' : 'toggle_on'),
38395 {restriction: preset.name()}));
38399 function mouseout() {
38400 wrap.selectAll('.restriction-help')
38401 .text(t('operations.restriction.help.' +
38402 (fromNodeID ? 'toggle' : 'select')));
38405 function render() {
38406 if (context.hasEntity(vertexID)) {
38407 restrictions(selection);
38412 restrictions.entity = function(_) {
38413 if (!vertexID || vertexID !== _.id) {
38419 restrictions.tags = function() {};
38420 restrictions.focus = function() {};
38422 return d3.rebind(restrictions, dispatch, 'on');
38424 iD.ui.preset.textarea = function(field) {
38425 var dispatch = d3.dispatch('change'),
38428 function i(selection) {
38429 input = selection.selectAll('textarea')
38432 input.enter().append('textarea')
38433 .attr('id', 'preset-input-' + field.id)
38434 .attr('placeholder', field.placeholder() || t('inspector.unknown'))
38435 .attr('maxlength', 255);
38438 .on('input', change(true))
38439 .on('blur', change())
38440 .on('change', change());
38443 function change(onInput) {
38444 return function() {
38446 t[field.key] = input.value() || undefined;
38447 dispatch.change(t, onInput);
38451 i.tags = function(tags) {
38452 input.value(tags[field.key] || '');
38455 i.focus = function() {
38456 input.node().focus();
38459 return d3.rebind(i, dispatch, 'on');
38461 iD.ui.preset.wikipedia = function(field, context) {
38462 var dispatch = d3.dispatch('change'),
38463 wikipedia = iD.wikipedia(),
38464 link, entity, lang, title;
38466 function i(selection) {
38467 var langcombo = d3.combobox()
38468 .fetcher(function(value, cb) {
38469 var v = value.toLowerCase();
38471 cb(iD.data.wikipedia.filter(function(d) {
38472 return d[0].toLowerCase().indexOf(v) >= 0 ||
38473 d[1].toLowerCase().indexOf(v) >= 0 ||
38474 d[2].toLowerCase().indexOf(v) >= 0;
38475 }).map(function(d) {
38476 return { value: d[1] };
38480 var titlecombo = d3.combobox()
38481 .fetcher(function(value, cb) {
38483 if (!value) value = context.entity(entity.id).tags.name || '';
38484 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
38486 searchfn(language()[2], value, function(query, data) {
38487 cb(data.map(function(d) {
38488 return { value: d };
38493 lang = selection.selectAll('input.wiki-lang')
38496 lang.enter().append('input')
38497 .attr('type', 'text')
38498 .attr('class', 'wiki-lang')
38503 .on('blur', changeLang)
38504 .on('change', changeLang);
38506 title = selection.selectAll('input.wiki-title')
38509 title.enter().append('input')
38510 .attr('type', 'text')
38511 .attr('class', 'wiki-title')
38512 .attr('id', 'preset-input-' + field.id);
38516 .on('blur', change)
38517 .on('change', change);
38519 link = selection.selectAll('a.wiki-link')
38522 link.enter().append('a')
38523 .attr('class', 'wiki-link button-input-action minor')
38524 .attr('target', '_blank')
38525 .call(iD.svg.Icon('#icon-out-link', 'inline'));
38528 function language() {
38529 var value = lang.value().toLowerCase();
38530 var locale = iD.detect().locale.toLowerCase();
38531 var localeLanguage;
38532 return _.find(iD.data.wikipedia, function(d) {
38533 if (d[2] === locale) localeLanguage = d;
38534 return d[0].toLowerCase() === value ||
38535 d[1].toLowerCase() === value ||
38537 }) || localeLanguage || ['English', 'English', 'en'];
38540 function changeLang() {
38541 lang.value(language()[1]);
38545 function change() {
38546 var value = title.value(),
38547 m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/),
38548 l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
38552 // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
38553 value = decodeURIComponent(m[2]).replace(/_/g, ' ');
38556 // Best-effort `anchordecode:` implementation
38557 anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
38559 anchor = decodeURIComponent(m[3]);
38561 value += '#' + anchor.replace(/_/g, ' ');
38563 value = value.slice(0, 1).toUpperCase() + value.slice(1);
38565 title.value(value);
38569 t[field.key] = value ? language()[2] + ':' + value : undefined;
38570 dispatch.change(t);
38573 i.tags = function(tags) {
38574 var value = tags[field.key] || '',
38575 m = value.match(/([^:]+):([^#]+)(?:#(.+))?/),
38576 l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
38577 anchor = m && m[3];
38579 // value in correct format
38582 title.value(m[2] + (anchor ? ('#' + anchor) : ''));
38585 // Best-effort `anchorencode:` implementation
38586 anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
38588 anchor = anchor.replace(/ /g, '_');
38591 link.attr('href', 'http://' + m[1] + '.wikipedia.org/wiki/' +
38592 m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : ''));
38594 // unrecognized value format
38596 title.value(value);
38597 link.attr('href', 'http://en.wikipedia.org/wiki/Special:Search?search=' + value);
38601 i.entity = function(_) {
38605 i.focus = function() {
38606 title.node().focus();
38609 return d3.rebind(i, dispatch, 'on');
38611 iD.ui.intro.area = function(context, reveal) {
38612 var event = d3.dispatch('done'),
38616 title: 'intro.areas.title'
38619 step.enter = function() {
38620 var playground = [-85.63552, 41.94159],
38621 corner = [-85.63565411045074, 41.9417715536927];
38622 context.map().centerZoom(playground, 19);
38623 reveal('button.add-area',
38624 t('intro.areas.add', { button: iD.ui.intro.icon('#icon-area', 'pre-text') }),
38625 { tooltipClass: 'intro-areas-add' });
38627 context.on('enter.intro', addArea);
38629 function addArea(mode) {
38630 if (mode.id !== 'add-area') return;
38631 context.on('enter.intro', drawArea);
38633 var padding = 120 * Math.pow(2, context.map().zoom() - 19);
38634 var pointBox = iD.ui.intro.pad(corner, padding, context);
38635 reveal(pointBox, t('intro.areas.corner'));
38637 context.map().on('move.intro', function() {
38638 padding = 120 * Math.pow(2, context.map().zoom() - 19);
38639 pointBox = iD.ui.intro.pad(corner, padding, context);
38640 reveal(pointBox, t('intro.areas.corner'), {duration: 0});
38644 function drawArea(mode) {
38645 if (mode.id !== 'draw-area') return;
38646 context.on('enter.intro', enterSelect);
38648 var padding = 150 * Math.pow(2, context.map().zoom() - 19);
38649 var pointBox = iD.ui.intro.pad(playground, padding, context);
38650 reveal(pointBox, t('intro.areas.place'));
38652 context.map().on('move.intro', function() {
38653 padding = 150 * Math.pow(2, context.map().zoom() - 19);
38654 pointBox = iD.ui.intro.pad(playground, padding, context);
38655 reveal(pointBox, t('intro.areas.place'), {duration: 0});
38659 function enterSelect(mode) {
38660 if (mode.id !== 'select') return;
38661 context.map().on('move.intro', null);
38662 context.on('enter.intro', null);
38664 timeout = setTimeout(function() {
38665 reveal('.preset-search-input',
38666 t('intro.areas.search',
38667 { name: context.presets().item('leisure/playground').name() }));
38668 d3.select('.preset-search-input').on('keyup.intro', keySearch);
38672 function keySearch() {
38673 var first = d3.select('.preset-list-item:first-child');
38674 if (first.classed('preset-leisure-playground')) {
38675 reveal(first.select('.preset-list-button').node(), t('intro.areas.choose'));
38676 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
38677 d3.select('.preset-search-input').on('keyup.intro', null);
38681 function selectedPreset() {
38683 t('intro.areas.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
38684 context.on('exit.intro', event.done);
38688 step.exit = function() {
38689 window.clearTimeout(timeout);
38690 context.on('enter.intro', null);
38691 context.on('exit.intro', null);
38692 context.history().on('change.intro', null);
38693 context.map().on('move.intro', null);
38694 d3.select('.preset-search-input').on('keyup.intro', null);
38697 return d3.rebind(step, event, 'on');
38699 iD.ui.intro.line = function(context, reveal) {
38700 var event = d3.dispatch('done'),
38704 title: 'intro.lines.title'
38707 function timeout(f, t) {
38708 timeouts.push(window.setTimeout(f, t));
38711 function eventCancel() {
38712 d3.event.stopPropagation();
38713 d3.event.preventDefault();
38716 step.enter = function() {
38717 var centroid = [-85.62830, 41.95699];
38718 var midpoint = [-85.62975395449628, 41.95787501510204];
38719 var start = [-85.6297754121684, 41.95805253325314];
38720 var intersection = [-85.62974496187628, 41.95742515554585];
38722 context.map().centerZoom(start, 18);
38723 reveal('button.add-line',
38724 t('intro.lines.add', { button: iD.ui.intro.icon('#icon-line', 'pre-text') }),
38725 { tooltipClass: 'intro-lines-add' });
38727 context.on('enter.intro', addLine);
38729 function addLine(mode) {
38730 if (mode.id !== 'add-line') return;
38731 context.on('enter.intro', drawLine);
38733 var padding = 150 * Math.pow(2, context.map().zoom() - 18);
38734 var pointBox = iD.ui.intro.pad(start, padding, context);
38735 reveal(pointBox, t('intro.lines.start'));
38737 context.map().on('move.intro', function() {
38738 padding = 150 * Math.pow(2, context.map().zoom() - 18);
38739 pointBox = iD.ui.intro.pad(start, padding, context);
38740 reveal(pointBox, t('intro.lines.start'), {duration: 0});
38744 function drawLine(mode) {
38745 if (mode.id !== 'draw-line') return;
38746 context.history().on('change.intro', addIntersection);
38747 context.on('enter.intro', retry);
38749 var padding = 300 * Math.pow(2, context.map().zoom() - 19);
38750 var pointBox = iD.ui.intro.pad(midpoint, padding, context);
38751 reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}));
38753 context.map().on('move.intro', function() {
38754 padding = 300 * Math.pow(2, context.map().zoom() - 19);
38755 pointBox = iD.ui.intro.pad(midpoint, padding, context);
38756 reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}), {duration: 0});
38760 // ended line before creating intersection
38761 function retry(mode) {
38762 if (mode.id !== 'select') return;
38763 var pointBox = iD.ui.intro.pad(intersection, 30, context),
38764 ids = mode.selectedIDs();
38765 reveal(pointBox, t('intro.lines.restart', {name: t('intro.graph.flower_st')}));
38766 d3.select(window).on('mousedown.intro', eventCancel, true);
38768 timeout(function() {
38769 context.replace(iD.actions.DeleteMultiple(ids));
38775 function addIntersection(changes) {
38776 if ( _.any(changes.created(), function(d) {
38777 return d.type === 'node' && context.graph().parentWays(d).length > 1;
38779 context.history().on('change.intro', null);
38780 context.on('enter.intro', enterSelect);
38782 var padding = 900 * Math.pow(2, context.map().zoom() - 19);
38783 var pointBox = iD.ui.intro.pad(centroid, padding, context);
38784 reveal(pointBox, t('intro.lines.finish'));
38786 context.map().on('move.intro', function() {
38787 padding = 900 * Math.pow(2, context.map().zoom() - 19);
38788 pointBox = iD.ui.intro.pad(centroid, padding, context);
38789 reveal(pointBox, t('intro.lines.finish'), {duration: 0});
38794 function enterSelect(mode) {
38795 if (mode.id !== 'select') return;
38796 context.map().on('move.intro', null);
38797 context.on('enter.intro', null);
38798 d3.select('#curtain').style('pointer-events', 'all');
38803 function presetCategory() {
38804 timeout(function() {
38805 d3.select('#curtain').style('pointer-events', 'none');
38806 var road = d3.select('.preset-category-road .preset-list-button');
38807 reveal(road.node(), t('intro.lines.road'));
38808 road.one('click.intro', roadCategory);
38812 function roadCategory() {
38813 timeout(function() {
38814 var grid = d3.select('.subgrid');
38815 reveal(grid.node(), t('intro.lines.residential'));
38816 grid.selectAll(':not(.preset-highway-residential) .preset-list-button')
38817 .one('click.intro', retryPreset);
38818 grid.selectAll('.preset-highway-residential .preset-list-button')
38819 .one('click.intro', roadDetails);
38823 // selected wrong road type
38824 function retryPreset() {
38825 timeout(function() {
38826 var preset = d3.select('.entity-editor-pane .preset-list-button');
38827 reveal(preset.node(), t('intro.lines.wrong_preset'));
38828 preset.one('click.intro', presetCategory);
38832 function roadDetails() {
38834 t('intro.lines.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
38835 context.on('exit.intro', event.done);
38840 step.exit = function() {
38841 d3.select(window).on('mousedown.intro', null, true);
38842 d3.select('#curtain').style('pointer-events', 'none');
38843 timeouts.forEach(window.clearTimeout);
38844 context.on('enter.intro', null);
38845 context.on('exit.intro', null);
38846 context.map().on('move.intro', null);
38847 context.history().on('change.intro', null);
38850 return d3.rebind(step, event, 'on');
38852 iD.ui.intro.navigation = function(context, reveal) {
38853 var event = d3.dispatch('done'),
38857 title: 'intro.navigation.title'
38860 function set(f, t) {
38861 timeouts.push(window.setTimeout(f, t));
38864 function eventCancel() {
38865 d3.event.stopPropagation();
38866 d3.event.preventDefault();
38869 step.enter = function() {
38870 var rect = context.surfaceRect(),
38872 left: rect.left + 10,
38873 top: rect.top + 70,
38874 width: rect.width - 70,
38875 height: rect.height - 170
38878 context.map().centerZoom([-85.63591, 41.94285], 19);
38880 reveal(map, t('intro.navigation.drag'));
38882 context.map().on('move.intro', _.debounce(function() {
38883 context.map().on('move.intro', null);
38885 context.on('enter.intro', inspectTownHall);
38888 function townhall() {
38889 var hall = [-85.63645945147184, 41.942986488012565];
38891 var point = context.projection(hall);
38892 if (point[0] < 0 || point[0] > rect.width ||
38893 point[1] < 0 || point[1] > rect.height) {
38894 context.map().center(hall);
38897 var box = iD.ui.intro.pointBox(hall, context);
38898 reveal(box, t('intro.navigation.select'));
38900 context.map().on('move.intro', function() {
38901 var box = iD.ui.intro.pointBox(hall, context);
38902 reveal(box, t('intro.navigation.select'), {duration: 0});
38906 function inspectTownHall(mode) {
38907 if (mode.id !== 'select') return;
38908 context.on('enter.intro', null);
38909 context.map().on('move.intro', null);
38911 reveal('.entity-editor-pane',
38912 t('intro.navigation.pane', { button: iD.ui.intro.icon('#icon-close', 'pre-text') }));
38913 context.on('exit.intro', streetSearch);
38917 function streetSearch() {
38918 context.on('exit.intro', null);
38919 reveal('.search-header input',
38920 t('intro.navigation.search', { name: t('intro.graph.spring_st') }));
38921 d3.select('.search-header input').on('keyup.intro', searchResult);
38924 function searchResult() {
38925 var first = d3.select('.feature-list-item:nth-child(0n+2)'), // skip No Results item
38926 firstName = first.select('.entity-name'),
38927 name = t('intro.graph.spring_st');
38929 if (!firstName.empty() && firstName.text() === name) {
38930 reveal(first.node(), t('intro.navigation.choose', { name: name }));
38931 context.on('exit.intro', selectedStreet);
38932 d3.select('.search-header input')
38933 .on('keydown.intro', eventCancel, true)
38934 .on('keyup.intro', null);
38938 function selectedStreet() {
38939 var springSt = [-85.63585099140167, 41.942506848938926];
38940 context.map().center(springSt);
38941 context.on('exit.intro', event.done);
38943 reveal('.entity-editor-pane',
38944 t('intro.navigation.chosen', {
38945 name: t('intro.graph.spring_st'),
38946 button: iD.ui.intro.icon('#icon-close', 'pre-text')
38952 step.exit = function() {
38953 timeouts.forEach(window.clearTimeout);
38954 context.map().on('move.intro', null);
38955 context.on('enter.intro', null);
38956 context.on('exit.intro', null);
38957 d3.select('.search-header input')
38958 .on('keydown.intro', null)
38959 .on('keyup.intro', null);
38962 return d3.rebind(step, event, 'on');
38964 iD.ui.intro.point = function(context, reveal) {
38965 var event = d3.dispatch('done'),
38969 title: 'intro.points.title'
38972 function setTimeout(f, t) {
38973 timeouts.push(window.setTimeout(f, t));
38976 function eventCancel() {
38977 d3.event.stopPropagation();
38978 d3.event.preventDefault();
38981 step.enter = function() {
38982 context.map().centerZoom([-85.63279, 41.94394], 19);
38983 reveal('button.add-point',
38984 t('intro.points.add', { button: iD.ui.intro.icon('#icon-point', 'pre-text') }),
38985 { tooltipClass: 'intro-points-add' });
38987 var corner = [-85.632481,41.944094];
38989 context.on('enter.intro', addPoint);
38991 function addPoint(mode) {
38992 if (mode.id !== 'add-point') return;
38993 context.on('enter.intro', enterSelect);
38995 var pointBox = iD.ui.intro.pad(corner, 150, context);
38996 reveal(pointBox, t('intro.points.place'));
38998 context.map().on('move.intro', function() {
38999 pointBox = iD.ui.intro.pad(corner, 150, context);
39000 reveal(pointBox, t('intro.points.place'), {duration: 0});
39004 function enterSelect(mode) {
39005 if (mode.id !== 'select') return;
39006 context.map().on('move.intro', null);
39007 context.on('enter.intro', null);
39009 setTimeout(function() {
39010 reveal('.preset-search-input',
39011 t('intro.points.search', {name: context.presets().item('amenity/cafe').name()}));
39012 d3.select('.preset-search-input').on('keyup.intro', keySearch);
39016 function keySearch() {
39017 var first = d3.select('.preset-list-item:first-child');
39018 if (first.classed('preset-amenity-cafe')) {
39019 reveal(first.select('.preset-list-button').node(), t('intro.points.choose'));
39020 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
39021 d3.select('.preset-search-input')
39022 .on('keydown.intro', eventCancel, true)
39023 .on('keyup.intro', null);
39027 function selectedPreset() {
39028 setTimeout(function() {
39029 reveal('.entity-editor-pane', t('intro.points.describe'), {tooltipClass: 'intro-points-describe'});
39030 context.history().on('change.intro', closeEditor);
39031 context.on('exit.intro', selectPoint);
39035 function closeEditor() {
39036 d3.select('.preset-search-input').on('keydown.intro', null);
39037 context.history().on('change.intro', null);
39038 reveal('.entity-editor-pane',
39039 t('intro.points.close', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39042 function selectPoint() {
39043 context.on('exit.intro', null);
39044 context.history().on('change.intro', null);
39045 context.on('enter.intro', enterReselect);
39047 var pointBox = iD.ui.intro.pad(corner, 150, context);
39048 reveal(pointBox, t('intro.points.reselect'));
39050 context.map().on('move.intro', function() {
39051 pointBox = iD.ui.intro.pad(corner, 150, context);
39052 reveal(pointBox, t('intro.points.reselect'), {duration: 0});
39056 function enterReselect(mode) {
39057 if (mode.id !== 'select') return;
39058 context.map().on('move.intro', null);
39059 context.on('enter.intro', null);
39061 setTimeout(function() {
39062 reveal('.entity-editor-pane',
39063 t('intro.points.fixname', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39064 context.on('exit.intro', deletePoint);
39068 function deletePoint() {
39069 context.on('exit.intro', null);
39070 context.on('enter.intro', enterDelete);
39072 var pointBox = iD.ui.intro.pad(corner, 150, context);
39073 reveal(pointBox, t('intro.points.reselect_delete'));
39075 context.map().on('move.intro', function() {
39076 pointBox = iD.ui.intro.pad(corner, 150, context);
39077 reveal(pointBox, t('intro.points.reselect_delete'), {duration: 0});
39081 function enterDelete(mode) {
39082 if (mode.id !== 'select') return;
39083 context.map().on('move.intro', null);
39084 context.on('enter.intro', null);
39085 context.on('exit.intro', deletePoint);
39086 context.map().on('move.intro', deletePoint);
39087 context.history().on('change.intro', deleted);
39089 setTimeout(function() {
39090 var node = d3.select('.radial-menu-item-delete').node();
39091 var pointBox = iD.ui.intro.pad(node.getBoundingClientRect(), 50, context);
39093 t('intro.points.delete', { button: iD.ui.intro.icon('#operation-delete', 'pre-text') }));
39097 function deleted(changed) {
39098 if (changed.deleted().length) event.done();
39103 step.exit = function() {
39104 timeouts.forEach(window.clearTimeout);
39105 context.on('exit.intro', null);
39106 context.on('enter.intro', null);
39107 context.map().on('move.intro', null);
39108 context.history().on('change.intro', null);
39109 d3.select('.preset-search-input')
39110 .on('keyup.intro', null)
39111 .on('keydown.intro', null);
39114 return d3.rebind(step, event, 'on');
39116 iD.ui.intro.startEditing = function(context, reveal) {
39117 var event = d3.dispatch('done', 'startEditing'),
39122 title: 'intro.startediting.title'
39125 function timeout(f, t) {
39126 timeouts.push(window.setTimeout(f, t));
39129 step.enter = function() {
39130 reveal('.map-control.help-control',
39131 t('intro.startediting.help', { button: iD.ui.intro.icon('#icon-help', 'pre-text') }));
39133 timeout(function() {
39134 reveal('#bar button.save', t('intro.startediting.save'));
39137 timeout(function() {
39138 reveal('#surface');
39141 timeout(function() {
39142 modal = iD.ui.modal(context.container());
39144 modal.select('.modal')
39145 .attr('class', 'modal-splash modal col6');
39147 modal.selectAll('.close').remove();
39149 var startbutton = modal.select('.content')
39150 .attr('class', 'fillL')
39152 .attr('class', 'modal-section huge-modal-button')
39153 .on('click', function() {
39157 startbutton.append('div')
39158 .attr('class','illustration');
39159 startbutton.append('h2')
39160 .text(t('intro.startediting.start'));
39162 event.startEditing();
39166 step.exit = function() {
39167 if (modal) modal.remove();
39168 timeouts.forEach(window.clearTimeout);
39171 return d3.rebind(step, event, 'on');
39173 iD.presets = function() {
39175 // an iD.presets.Collection with methods for
39176 // loading new data and returning defaults
39178 var all = iD.presets.Collection([]),
39179 defaults = { area: all, line: all, point: all, vertex: all, relation: all },
39182 recent = iD.presets.Collection([]);
39184 // Index of presets by (geometry, tag key).
39193 all.match = function(entity, resolver) {
39194 var geometry = entity.geometry(resolver),
39195 geometryMatches = index[geometry],
39199 for (var k in entity.tags) {
39200 var keyMatches = geometryMatches[k];
39201 if (!keyMatches) continue;
39203 for (var i = 0; i < keyMatches.length; i++) {
39204 var score = keyMatches[i].matchScore(entity);
39205 if (score > best) {
39207 match = keyMatches[i];
39212 return match || all.item(geometry);
39215 // Because of the open nature of tagging, iD will never have a complete
39216 // list of tags used in OSM, so we want it to have logic like "assume
39217 // that a closed way with an amenity tag is an area, unless the amenity
39218 // is one of these specific types". This function computes a structure
39219 // that allows testing of such conditions, based on the presets designated
39220 // as as supporting (or not supporting) the area geometry.
39222 // The returned object L is a whitelist/blacklist of tags. A closed way
39223 // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
39224 // (see `iD.Way#isArea()`). In other words, the keys of L form the whitelist,
39225 // and the subkeys form the blacklist.
39226 all.areaKeys = function() {
39228 ignore = ['barrier', 'highway', 'footway', 'railway', 'type'],
39229 presets = _.reject(all.collection, 'suggestion');
39232 presets.forEach(function(d) {
39233 for (var key in d.tags) break;
39235 if (ignore.indexOf(key) !== -1) return;
39237 if (d.geometry.indexOf('area') !== -1) {
39238 areaKeys[key] = areaKeys[key] || {};
39243 presets.forEach(function(d) {
39244 for (var key in d.tags) break;
39246 if (ignore.indexOf(key) !== -1) return;
39248 var value = d.tags[key];
39249 if (d.geometry.indexOf('area') === -1 && key in areaKeys && value !== '*') {
39250 areaKeys[key][value] = true;
39257 all.load = function(d) {
39260 _.forEach(d.fields, function(d, id) {
39261 fields[id] = iD.presets.Field(id, d);
39262 if (d.universal) universal.push(fields[id]);
39267 _.forEach(d.presets, function(d, id) {
39268 all.collection.push(iD.presets.Preset(id, d, fields));
39272 if (d.categories) {
39273 _.forEach(d.categories, function(d, id) {
39274 all.collection.push(iD.presets.Category(id, d, all));
39279 var getItem = _.bind(all.item, all);
39281 area: iD.presets.Collection(d.defaults.area.map(getItem)),
39282 line: iD.presets.Collection(d.defaults.line.map(getItem)),
39283 point: iD.presets.Collection(d.defaults.point.map(getItem)),
39284 vertex: iD.presets.Collection(d.defaults.vertex.map(getItem)),
39285 relation: iD.presets.Collection(d.defaults.relation.map(getItem))
39289 for (var i = 0; i < all.collection.length; i++) {
39290 var preset = all.collection[i],
39291 geometry = preset.geometry;
39293 for (var j = 0; j < geometry.length; j++) {
39294 var g = index[geometry[j]];
39295 for (var k in preset.tags) {
39296 (g[k] = g[k] || []).push(preset);
39304 all.field = function(id) {
39308 all.universal = function() {
39312 all.defaults = function(geometry, n) {
39313 var rec = recent.matchGeometry(geometry).collection.slice(0, 4),
39314 def = _.uniq(rec.concat(defaults[geometry].collection)).slice(0, n - 1);
39315 return iD.presets.Collection(_.unique(rec.concat(def).concat(all.item(geometry))));
39318 all.choose = function(preset) {
39319 if (!preset.isFallback()) {
39320 recent = iD.presets.Collection(_.unique([preset].concat(recent.collection)));
39327 iD.presets.Category = function(id, category, all) {
39328 category = _.clone(category);
39332 category.members = iD.presets.Collection(category.members.map(function(id) {
39333 return all.item(id);
39336 category.matchGeometry = function(geometry) {
39337 return category.geometry.indexOf(geometry) >= 0;
39340 category.matchScore = function() { return -1; };
39342 category.name = function() {
39343 return t('presets.categories.' + id + '.name', {'default': id});
39346 category.terms = function() {
39352 iD.presets.Collection = function(collection) {
39354 var maxSearchResults = 50,
39355 maxSuggestionResults = 10;
39359 collection: collection,
39361 item: function(id) {
39362 return _.find(collection, function(d) {
39363 return d.id === id;
39367 matchGeometry: function(geometry) {
39368 return iD.presets.Collection(collection.filter(function(d) {
39369 return d.matchGeometry(geometry);
39373 search: function(value, geometry) {
39374 if (!value) return this;
39376 value = value.toLowerCase();
39378 var searchable = _.filter(collection, function(a) {
39379 return a.searchable !== false && a.suggestion !== true;
39381 suggestions = _.filter(collection, function(a) {
39382 return a.suggestion === true;
39385 function leading(a) {
39386 var index = a.indexOf(value);
39387 return index === 0 || a[index - 1] === ' ';
39390 // matches value to preset.name
39391 var leading_name = _.filter(searchable, function(a) {
39392 return leading(a.name().toLowerCase());
39393 }).sort(function(a, b) {
39394 var i = a.name().toLowerCase().indexOf(value) - b.name().toLowerCase().indexOf(value);
39395 if (i === 0) return a.name().length - b.name().length;
39399 // matches value to preset.terms values
39400 var leading_terms = _.filter(searchable, function(a) {
39401 return _.any(a.terms() || [], leading);
39404 // matches value to preset.tags values
39405 var leading_tag_values = _.filter(searchable, function(a) {
39406 return _.any(_.without(_.values(a.tags || {}), '*'), leading);
39410 // finds close matches to value in preset.name
39411 var levenstein_name = searchable.map(function(a) {
39414 dist: iD.util.editDistance(value, a.name().toLowerCase())
39416 }).filter(function(a) {
39417 return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
39418 }).sort(function(a, b) {
39419 return a.dist - b.dist;
39420 }).map(function(a) {
39424 // finds close matches to value in preset.terms
39425 var leventstein_terms = _.filter(searchable, function(a) {
39426 return _.any(a.terms() || [], function(b) {
39427 return iD.util.editDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
39431 function suggestionName(name) {
39432 var nameArray = name.split(' - ');
39433 if (nameArray.length > 1) {
39434 name = nameArray.slice(0, nameArray.length-1).join(' - ');
39436 return name.toLowerCase();
39439 var leading_suggestions = _.filter(suggestions, function(a) {
39440 return leading(suggestionName(a.name()));
39441 }).sort(function(a, b) {
39442 a = suggestionName(a.name());
39443 b = suggestionName(b.name());
39444 var i = a.indexOf(value) - b.indexOf(value);
39445 if (i === 0) return a.length - b.length;
39449 var leven_suggestions = suggestions.map(function(a) {
39452 dist: iD.util.editDistance(value, suggestionName(a.name()))
39454 }).filter(function(a) {
39455 return a.dist + Math.min(value.length - suggestionName(a.preset.name()).length, 0) < 1;
39456 }).sort(function(a, b) {
39457 return a.dist - b.dist;
39458 }).map(function(a) {
39462 var other = presets.item(geometry);
39464 var results = leading_name.concat(
39466 leading_tag_values,
39467 leading_suggestions.slice(0, maxSuggestionResults+5),
39470 leven_suggestions.slice(0, maxSuggestionResults)
39471 ).slice(0, maxSearchResults-1);
39473 return iD.presets.Collection(_.unique(
39474 results.concat(other)
39481 iD.presets.Field = function(id, field) {
39482 field = _.clone(field);
39486 field.matchGeometry = function(geometry) {
39487 return !field.geometry || field.geometry === geometry;
39490 field.t = function(scope, options) {
39491 return t('presets.fields.' + id + '.' + scope, options);
39494 field.label = function() {
39495 return field.t('label', {'default': id});
39498 var placeholder = field.placeholder;
39499 field.placeholder = function() {
39500 return field.t('placeholder', {'default': placeholder});
39505 iD.presets.Preset = function(id, preset, fields) {
39506 preset = _.clone(preset);
39509 preset.fields = (preset.fields || []).map(getFields);
39510 preset.geometry = (preset.geometry || []);
39512 function getFields(f) {
39516 preset.matchGeometry = function(geometry) {
39517 return preset.geometry.indexOf(geometry) >= 0;
39520 var matchScore = preset.matchScore || 1;
39521 preset.matchScore = function(entity) {
39522 var tags = preset.tags,
39525 for (var t in tags) {
39526 if (entity.tags[t] === tags[t]) {
39527 score += matchScore;
39528 } else if (tags[t] === '*' && t in entity.tags) {
39529 score += matchScore / 2;
39538 preset.t = function(scope, options) {
39539 return t('presets.presets.' + id + '.' + scope, options);
39542 var name = preset.name;
39543 preset.name = function() {
39544 if (preset.suggestion) {
39545 id = id.split('/');
39546 id = id[0] + '/' + id[1];
39547 return name + ' - ' + t('presets.presets.' + id + '.name');
39549 return preset.t('name', {'default': name});
39552 preset.terms = function() {
39553 return preset.t('terms', {'default': ''}).toLowerCase().trim().split(/\s*,+\s*/);
39556 preset.isFallback = function() {
39557 return Object.keys(preset.tags).length === 0;
39560 preset.reference = function(geometry) {
39561 var key = Object.keys(preset.tags)[0],
39562 value = preset.tags[key];
39564 if (geometry === 'relation' && key === 'type') {
39565 return { rtype: value };
39566 } else if (value === '*') {
39567 return { key: key };
39569 return { key: key, value: value };
39573 var removeTags = preset.removeTags || preset.tags;
39574 preset.removeTags = function(tags, geometry) {
39575 tags = _.omit(tags, _.keys(removeTags));
39577 for (var f in preset.fields) {
39578 var field = preset.fields[f];
39579 if (field.matchGeometry(geometry) && field.default === tags[field.key]) {
39580 delete tags[field.key];
39588 var applyTags = preset.addTags || preset.tags;
39589 preset.applyTags = function(tags, geometry) {
39592 tags = _.clone(tags);
39594 for (k in applyTags) {
39595 if (applyTags[k] === '*') {
39598 tags[k] = applyTags[k];
39602 // Add area=yes if necessary.
39603 // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
39604 // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
39605 // 2. chosen preset doesn't have a key in areaKeys (`railway=station`)
39606 if (geometry === 'area') {
39607 var needsAreaTag = true;
39608 if (preset.geometry.indexOf('line') === -1) {
39609 for (k in applyTags) {
39610 if (k in iD.areaKeys) {
39611 needsAreaTag = false;
39616 if (needsAreaTag) {
39621 for (var f in preset.fields) {
39622 var field = preset.fields[f];
39623 if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
39624 tags[field.key] = field.default;
39633 iD.validations = {};
39634 iD.validations.DeprecatedTag = function() {
39636 var validation = function(changes) {
39638 for (var i = 0; i < changes.created.length; i++) {
39639 var change = changes.created[i],
39640 deprecatedTags = change.deprecatedTags();
39642 if (!_.isEmpty(deprecatedTags)) {
39643 var tags = iD.util.tagText({ tags: deprecatedTags });
39645 id: 'deprecated_tags',
39646 message: t('validations.deprecated_tags', { tags: tags }),
39656 iD.validations.ManyDeletions = function() {
39657 var threshold = 100;
39659 var validation = function(changes) {
39661 if (changes.deleted.length > threshold) {
39663 id: 'many_deletions',
39664 message: t('validations.many_deletions', { n: changes.deleted.length })
39672 iD.validations.MissingTag = function() {
39674 var validation = function(changes, graph) {
39676 for (var i = 0; i < changes.created.length; i++) {
39677 var change = changes.created[i],
39678 geometry = change.geometry(graph);
39680 if ((geometry === 'point' || geometry === 'line' || geometry === 'area') && !change.isUsed(graph)) {
39683 message: t('validations.untagged_' + geometry),
39684 tooltip: t('validations.untagged_' + geometry + '_tooltip'),
39694 iD.validations.TagSuggestsArea = function() {
39696 // https://github.com/openstreetmap/josm/blob/mirror/src/org/
39697 // openstreetmap/josm/data/validation/tests/UnclosedWays.java#L80
39698 function tagSuggestsArea(tags) {
39699 if (_.isEmpty(tags)) return false;
39701 var presence = ['landuse', 'amenities', 'tourism', 'shop'];
39702 for (var i = 0; i < presence.length; i++) {
39703 if (tags[presence[i]] !== undefined) {
39704 return presence[i] + '=' + tags[presence[i]];
39708 if (tags.building && tags.building === 'yes') return 'building=yes';
39711 var validation = function(changes, graph) {
39713 for (var i = 0; i < changes.created.length; i++) {
39714 var change = changes.created[i],
39715 geometry = change.geometry(graph),
39716 suggestion = (geometry === 'line' ? tagSuggestsArea(change.tags) : undefined);
39720 id: 'tag_suggests_area',
39721 message: t('validations.tag_suggests_area', { tag: suggestion }),
39732 window.locale = { _current: 'en' };
39734 locale.current = function(_) {
39735 if (!arguments.length) return locale._current;
39736 if (locale[_] !== undefined) locale._current = _;
39737 else if (locale[_.split('-')[0]]) locale._current = _.split('-')[0];
39741 function t(s, o, loc) {
39742 loc = loc || locale._current;
39744 var path = s.split(".").reverse(),
39747 while (rep !== undefined && path.length) rep = rep[path.pop()];
39749 if (rep !== undefined) {
39750 if (o) for (var k in o) rep = rep.replace('{' + k + '}', o[k]);
39754 if (loc !== 'en') {
39755 return t(s, o, 'en');
39758 if (o && 'default' in o) {
39759 return o['default'];
39762 var missing = 'Missing ' + loc + ' translation: ' + s;
39763 if (typeof console !== "undefined") console.error(missing);
39767 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 = {
39771 "amenity": "firepit"
39774 "leisure": "firepit"
39779 "barrier": "wire_fence"
39782 "barrier": "fence",
39783 "fence_type": "chain"
39788 "barrier": "wood_fence"
39791 "barrier": "fence",
39792 "fence_type": "wood"
39813 "highway": "incline"
39822 "highway": "incline_steep"
39831 "highway": "unsurfaced"
39835 "incline": "unpaved"
39843 "landuse": "forest",
39852 "natural": "wetland",
39858 "power_source": "*"
39861 "generator:source": "$1"
39866 "power_rating": "*"
39869 "generator:output": "$1"
39877 "shop": "supermarket",
39886 "tiger:upload_uuid",
39890 "geobase:datasetName",
39903 "KSJ2:PubFacAdmin",
40008 "South Azerbaijani",
40038 "беларуская (тарашкевіца)",
40039 "беларуская (тарашкевіца)",
40079 "বিষ্ণুপ্রিয়া মণিপুরী",
40108 "Chavacano de Zamboanga",
40109 "Chavacano de Zamboanga",
40113 "Min Dong Chinese",
40154 "Nēhiyawēwin / ᓀᐦᐃᔭᐍᐏᐣ",
40174 "словѣньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ",
40228 "Emiliano-Romagnolo",
40229 "emiliàn e rumagnòl",
40284 "Na Vosa Vakaviti",
40303 "Northern Frisian",
40354 "गोवा कोंकणी / Gova Konknni",
40434 "Bahasa Indonesia",
40474 "ᐃᓄᒃᑎᑐᑦ/inuktitut",
40554 "къарачай-малкъар",
40694 "Baso Minangkabau",
40773 "Low Saxon (Netherlands)",
40793 "Norwegian Nynorsk",
40814 "Sesotho sa Leboa",
40873 "Pennsylvania German",
40888 "Norfuk / Pitkern",
40889 "Norfuk / Pitkern",
41014 "srpskohrvatski / српскохрватски",
41078 "Saterland Frisian",
41199 "ئۇيغۇرچە / Uyghurche",
41214 "oʻzbekcha/ўзбекча",
41308 "Classical Chinese",
41313 "Chinese (Min Nan)",
41329 "type": "FeatureCollection",
41337 "type": "MultiPolygon",
42792 "type": "MultiPolygon",
51200 "circle-stroked-24": {
51206 "circle-stroked-18": {
51212 "circle-stroked-12": {
51236 "square-stroked-24": {
51242 "square-stroked-18": {
51248 "square-stroked-12": {
51272 "triangle-stroked-24": {
51278 "triangle-stroked-18": {
51284 "triangle-stroked-12": {
51308 "star-stroked-24": {
51314 "star-stroked-18": {
51320 "star-stroked-12": {
51362 "marker-stroked-24": {
51368 "marker-stroked-18": {
51374 "marker-stroked-12": {
51398 "religious-jewish-24": {
51404 "religious-jewish-18": {
51410 "religious-jewish-12": {
51416 "religious-christian-24": {
51422 "religious-christian-18": {
51428 "religious-christian-12": {
51434 "religious-muslim-24": {
51440 "religious-muslim-18": {
51446 "religious-muslim-12": {
51632 "parking-garage-24": {
51638 "parking-garage-18": {
51644 "parking-garage-12": {
51902 "art-gallery-24": {
51908 "art-gallery-18": {
51914 "art-gallery-12": {
51956 "america-football-24": {
51962 "america-football-18": {
51968 "america-football-12": {
52172 "fire-station-24": {
52178 "fire-station-18": {
52184 "fire-station-12": {
52550 "place-of-worship-24": {
52556 "place-of-worship-18": {
52562 "place-of-worship-12": {
52568 "alcohol-shop-24": {
52574 "alcohol-shop-18": {
52580 "alcohol-shop-12": {
52622 "slaughterhouse-24": {
52628 "slaughterhouse-18": {
52634 "slaughterhouse-12": {
52730 "emergency-telephone-24": {
52736 "emergency-telephone-18": {
52742 "emergency-telephone-12": {
52766 "waste-basket-24": {
52772 "waste-basket-18": {
52778 "waste-basket-12": {
52946 "clothing-store-24": {
52952 "clothing-store-18": {
52958 "clothing-store-12": {
52964 "polling-place-24": {
52970 "polling-place-18": {
52976 "polling-place-12": {
53036 "london-underground-24": {
53042 "london-underground-18": {
53048 "london-underground-12": {
53072 "rail-underground-24": {
53078 "rail-underground-18": {
53084 "rail-underground-12": {
53180 "hairdresser-24": {
53186 "hairdresser-18": {
53192 "hairdresser-12": {
53216 "mobilephone-24": {
53222 "mobilephone-18": {
53228 "mobilephone-12": {
53353 "description": "Add parks, buildings, lakes or other areas to the map.",
53354 "tail": "Click on the map to start drawing an area, like a park, lake, or building."
53358 "description": "Add highways, streets, pedestrian paths, canals or other lines to the map.",
53359 "tail": "Click on the map to start drawing a road, path, or route."
53363 "description": "Add restaurants, monuments, postal boxes or other points to the map.",
53364 "tail": "Click on the map to add a point."
53368 "description": "Pan and zoom the map."
53371 "tail": "Click to add nodes to your area. Click the first node to finish the area."
53374 "tail": "Click to add more nodes to the line. Click on other lines to connect to them, and double-click to end the line."
53380 "point": "Added a point.",
53381 "vertex": "Added a node to a way.",
53382 "relation": "Added a relation."
53387 "line": "Started a line.",
53388 "area": "Started an area."
53393 "title": "Continue",
53394 "description": "Continue this line.",
53395 "not_eligible": "No line can be continued here.",
53396 "multiple": "Several lines can be continued here. To choose a line, press the Shift key and click on it to select it.",
53398 "line": "Continued a line.",
53399 "area": "Continued an area."
53403 "annotation": "Canceled drawing."
53406 "annotation": "Changed the role of a relation member."
53409 "annotation": "Changed tags."
53412 "title": "Circularize",
53414 "line": "Make this line circular.",
53415 "area": "Make this area circular."
53419 "line": "Made a line circular.",
53420 "area": "Made an area circular."
53422 "not_closed": "This can't be made circular because it's not a loop.",
53423 "too_large": "This can't be made circular because not enough of it is currently visible.",
53424 "connected_to_hidden": "This can't be made circular because it is connected to a hidden feature."
53429 "line": "Square the corners of this line.",
53430 "area": "Square the corners of this area."
53434 "line": "Squared the corners of a line.",
53435 "area": "Squared the corners of an area."
53437 "not_squarish": "This can't be made square because it is not squarish.",
53438 "too_large": "This can't be made square because not enough of it is currently visible.",
53439 "connected_to_hidden": "This can't be made square because it is connected to a hidden feature."
53442 "title": "Straighten",
53443 "description": "Straighten this line.",
53445 "annotation": "Straightened a line.",
53446 "too_bendy": "This can't be straightened because it bends too much.",
53447 "connected_to_hidden": "This line can't be straightened because it is connected to a hidden feature."
53451 "description": "Delete object permanently.",
53453 "point": "Deleted a point.",
53454 "vertex": "Deleted a node from a way.",
53455 "line": "Deleted a line.",
53456 "area": "Deleted an area.",
53457 "relation": "Deleted a relation.",
53458 "multiple": "Deleted {n} objects."
53460 "incomplete_relation": "This feature can't be deleted because it hasn't been fully downloaded.",
53461 "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.",
53462 "connected_to_hidden": "This can't be deleted because it is connected to a hidden feature."
53465 "annotation": "Added a member to a relation."
53468 "annotation": "Removed a member from a relation."
53472 "point": "Connected a way to a point.",
53473 "vertex": "Connected a way to another.",
53474 "line": "Connected a way to a line.",
53475 "area": "Connected a way to an area."
53479 "title": "Disconnect",
53480 "description": "Disconnect these lines/areas from each other.",
53482 "annotation": "Disconnected lines/areas.",
53483 "not_connected": "There aren't enough lines/areas here to disconnect.",
53484 "connected_to_hidden": "This can't be disconnected because it is connected to a hidden feature."
53488 "description": "Merge these features.",
53490 "annotation": "Merged {n} features.",
53491 "not_eligible": "These features can't be merged.",
53492 "not_adjacent": "These features can't be merged because they aren't connected.",
53493 "restriction": "These features can't be merged because at least one is a member of a \"{relation}\" relation.",
53494 "incomplete_relation": "These features can't be merged because at least one hasn't been fully downloaded.",
53495 "conflicting_tags": "These features can't be merged because some of their tags have conflicting values."
53499 "description": "Move this to a different location.",
53502 "point": "Moved a point.",
53503 "vertex": "Moved a node in a way.",
53504 "line": "Moved a line.",
53505 "area": "Moved an area.",
53506 "multiple": "Moved multiple objects."
53508 "incomplete_relation": "This feature can't be moved because it hasn't been fully downloaded.",
53509 "too_large": "This can't be moved because not enough of it is currently visible.",
53510 "connected_to_hidden": "This can't be moved because it is connected to a hidden feature."
53514 "description": "Rotate this object around its center point.",
53517 "line": "Rotated a line.",
53518 "area": "Rotated an area."
53520 "too_large": "This can't be rotated because not enough of it is currently visible.",
53521 "connected_to_hidden": "This can't be rotated because it is connected to a hidden feature."
53524 "title": "Reverse",
53525 "description": "Make this line go in the opposite direction.",
53527 "annotation": "Reversed a line."
53532 "line": "Split this line into two at this node.",
53533 "area": "Split the boundary of this area into two.",
53534 "multiple": "Split the lines/area boundaries at this node into two."
53538 "line": "Split a line.",
53539 "area": "Split an area boundary.",
53540 "multiple": "Split {n} lines/area boundaries."
53542 "not_eligible": "Lines can't be split at their beginning or end.",
53543 "multiple_ways": "There are too many lines here to split.",
53544 "connected_to_hidden": "This can't be split because it is connected to a hidden feature."
53548 "select": "Click to select a road segment.",
53549 "toggle": "Click to toggle turn restrictions.",
53550 "toggle_on": "Click to add a \"{restriction}\" restriction.",
53551 "toggle_off": "Click to remove the \"{restriction}\" restriction."
53554 "create": "Added a turn restriction",
53555 "delete": "Deleted a turn restriction"
53560 "tooltip": "Undo: {action}",
53561 "nothing": "Nothing to undo."
53564 "tooltip": "Redo: {action}",
53565 "nothing": "Nothing to redo."
53567 "tooltip_keyhint": "Shortcut:",
53568 "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.",
53570 "translate": "Translate",
53571 "localized_translation_label": "Multilingual name",
53572 "localized_translation_language": "Choose language",
53573 "localized_translation_name": "Name"
53575 "zoom_in_edit": "Zoom in to Edit",
53576 "logout": "logout",
53577 "loading_auth": "Connecting to OpenStreetMap...",
53578 "report_a_bug": "Report a bug",
53579 "help_translate": "Help translate",
53581 "hidden_warning": "{count} hidden features",
53582 "hidden_details": "These features are currently hidden: {details}"
53585 "error": "Unable to connect to API.",
53586 "offline": "The API is offline. Please try editing later.",
53587 "readonly": "The API is read-only. You will need to wait to save your changes."
53590 "title": "Save Changes",
53591 "description_placeholder": "Brief description of your contributions",
53592 "message_label": "Changeset comment",
53593 "upload_explanation": "The changes you upload will be visible on all maps that use OpenStreetMap data.",
53594 "upload_explanation_with_user": "The changes you upload as {user} will be visible on all maps that use OpenStreetMap data.",
53596 "cancel": "Cancel",
53597 "changes": "{count} Changes",
53598 "warnings": "Warnings",
53599 "modified": "Modified",
53600 "deleted": "Deleted",
53601 "created": "Created"
53604 "list": "Edits by {users}",
53605 "truncated_list": "Edits by {users} and {count} others"
53608 "selected": "{n} selected",
53609 "geometry": "Geometry",
53610 "closed": "closed",
53611 "center": "Center",
53612 "perimeter": "Perimeter",
53613 "length": "Length",
53615 "centroid": "Centroid",
53616 "location": "Location",
53617 "metric": "Metric",
53618 "imperial": "Imperial"
53622 "vertex": "vertex",
53625 "relation": "relation"
53628 "search": "Search worldwide...",
53629 "no_results_visible": "No results in visible map area",
53630 "no_results_worldwide": "No results found"
53633 "title": "Show My Location"
53636 "no_documentation_combination": "There is no documentation available for this tag combination",
53637 "no_documentation_key": "There is no documentation available for this key",
53638 "show_more": "Show More",
53639 "view_on_osm": "View on openstreetmap.org",
53640 "all_tags": "All tags",
53641 "all_members": "All members",
53642 "all_relations": "All relations",
53643 "new_relation": "New relation...",
53645 "choose": "Select feature type",
53646 "results": "{n} results for {search}",
53647 "reference": "View on OpenStreetMap Wiki",
53648 "back_tooltip": "Change feature",
53649 "remove": "Remove",
53650 "search": "Search",
53651 "multiselect": "Selected items",
53652 "unknown": "Unknown",
53653 "incomplete": "<not downloaded>",
53654 "feature_list": "Search features",
53655 "edit": "Edit feature",
53663 "relation": "Relation",
53664 "location": "Location",
53665 "add_fields": "Add field:"
53668 "title": "Background",
53669 "description": "Background settings",
53670 "percent_brightness": "{opacity}% brightness",
53672 "custom": "Custom",
53673 "custom_button": "Edit custom background",
53674 "custom_prompt": "Enter a tile URL template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme.",
53675 "fix_misalignment": "Fix alignment",
53678 "description": "Minimap",
53679 "tooltip": "Show a zoomed out map to help locate the area currently displayed."
53683 "title": "Map Data",
53684 "description": "Map Data",
53685 "data_layers": "Data Layers",
53686 "fill_area": "Fill Areas",
53687 "map_features": "Map Features",
53688 "autohidden": "These features have been automatically hidden because too many would be shown on the screen. You can zoom in to edit them."
53692 "description": "Points",
53693 "tooltip": "Points of Interest"
53696 "description": "Major Roads",
53697 "tooltip": "Highways, Streets, etc."
53700 "description": "Minor Roads",
53701 "tooltip": "Service Roads, Parking Aisles, Tracks, etc."
53704 "description": "Paths",
53705 "tooltip": "Sidewalks, Foot Paths, Cycle Paths, etc."
53708 "description": "Buildings",
53709 "tooltip": "Buildings, Shelters, Garages, etc."
53712 "description": "Landuse Features",
53713 "tooltip": "Forests, Farmland, Parks, Residential, Commercial, etc."
53716 "description": "Boundaries",
53717 "tooltip": "Administrative Boundaries"
53720 "description": "Water Features",
53721 "tooltip": "Rivers, Lakes, Ponds, Basins, etc."
53724 "description": "Rail Features",
53725 "tooltip": "Railways"
53728 "description": "Power Features",
53729 "tooltip": "Power Lines, Power Plants, Substations, etc."
53732 "description": "Past/Future",
53733 "tooltip": "Proposed, Construction, Abandoned, Demolished, etc."
53736 "description": "Others",
53737 "tooltip": "Everything Else"
53742 "description": "No Fill (Wireframe)",
53743 "tooltip": "Enabling wireframe mode makes it easy to see the background imagery."
53746 "description": "Partial Fill",
53747 "tooltip": "Areas are drawn with fill only around their inner edges. (Recommended for beginner mappers)"
53750 "description": "Full Fill",
53751 "tooltip": "Areas are drawn fully filled."
53755 "heading": "You have unsaved changes",
53756 "description": "Do you wish to restore unsaved changes from a previous editing session?",
53757 "restore": "Restore",
53762 "help": "Save changes to OpenStreetMap, making them visible to other users.",
53763 "no_changes": "No changes to save.",
53764 "error": "Errors occurred while trying to save",
53765 "status_code": "Server returned status code {code}",
53766 "unknown_error_details": "Please ensure you are connected to the internet.",
53767 "uploading": "Uploading changes to OpenStreetMap.",
53768 "unsaved_changes": "You have unsaved changes",
53770 "header": "Resolve conflicting edits",
53771 "count": "Conflict {num} of {total}",
53772 "previous": "< Previous",
53774 "keep_local": "Keep mine",
53775 "keep_remote": "Use theirs",
53776 "restore": "Restore",
53777 "delete": "Leave Deleted",
53778 "download_changes": "Or download your changes.",
53779 "done": "All conflicts resolved!",
53780 "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"
53783 "merge_remote_changes": {
53785 "deleted": "This object has been deleted by {user}.",
53786 "location": "This object was moved by both you and {user}.",
53787 "nodelist": "Nodes were changed by both you and {user}.",
53788 "memberlist": "Relation members were changed by both you and {user}.",
53789 "tags": "You changed the <b>{tag}</b> tag to \"{local}\" and {user} changed it to \"{remote}\"."
53793 "edited_osm": "Edited OSM!",
53794 "just_edited": "You just edited OpenStreetMap!",
53795 "view_on_osm": "View on OSM",
53796 "facebook": "Share on Facebook",
53797 "twitter": "Share on Twitter",
53798 "google": "Share on Google+",
53799 "help_html": "Your changes should appear in the \"Standard\" layer in a few minutes. Other layers, and certain features, may take longer\n(<a href='https://help.openstreetmap.org/questions/4705/why-havent-my-changes-appeared-on-the-map' target='_blank'>details</a>).\n"
53806 "welcome": "Welcome to the iD OpenStreetMap editor",
53807 "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}.",
53808 "walkthrough": "Start the Walkthrough",
53809 "start": "Edit Now"
53813 "lose_changes": "You have unsaved changes. Switching the map server will discard them. Are you sure you want to switch servers?",
53817 "description": "Description",
53818 "on_wiki": "{tag} on wiki.osm.org",
53819 "used_with": "used with {type}"
53822 "untagged_point": "Untagged point",
53823 "untagged_line": "Untagged line",
53824 "untagged_area": "Untagged area",
53825 "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.",
53826 "tag_suggests_area": "The tag {tag} suggests line should be area, but it is not an area",
53827 "untagged_point_tooltip": "Select a feature type that describes what this point is.",
53828 "untagged_line_tooltip": "Select a feature type that describes what this line is.",
53829 "untagged_area_tooltip": "Select a feature type that describes what this area is.",
53830 "deprecated_tags": "Deprecated tags: {tags}"
53836 "cannot_zoom": "Cannot zoom out further in current mode.",
53837 "full_screen": "Toggle Full Screen",
53839 "local_layer": "Local GPX file",
53840 "drag_drop": "Drag and drop a .gpx file on the page, or click the button to the right to browse",
53841 "zoom": "Zoom to GPX track",
53842 "browse": "Browse for a .gpx file"
53845 "tooltip": "Street-level photos from Mapillary",
53846 "title": "Photo Overlay (Mapillary)",
53847 "view_on_mapillary": "View this image on Mapillary"
53851 "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",
53852 "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",
53853 "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",
53854 "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",
53855 "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",
53856 "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",
53857 "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",
53858 "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",
53859 "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"
53863 "city_hall": "Three Rivers City Hall",
53864 "fire_department": "Three Rivers Fire Department",
53865 "memory_isle_park": "Memory Isle Park",
53866 "riverwalk_trail": "Riverwalk Trail",
53867 "w_michigan_ave": "West Michigan Avenue",
53868 "e_michigan_ave": "East Michigan Avenue",
53869 "spring_st": "Spring Street",
53870 "scidmore_park": "Scidmore Park",
53871 "petting_zoo": "Scidmore Park Petting Zoo",
53872 "n_andrews_st": "North Andrews Street",
53873 "s_andrews_st": "South Andrews Street",
53874 "n_constantine_st": "North Constantine Street",
53875 "s_constantine_st": "South Constantine Street",
53876 "rocky_river": "Rocky River",
53877 "railroad_dr": "Railroad Drive",
53878 "conrail_rr": "Conrail Railroad",
53879 "st_joseph_river": "Saint Joseph River",
53880 "n_main_st": "North Main Street",
53881 "s_main_st": "South Main Street",
53882 "water_st": "Water Street",
53883 "foster_st": "Foster Street",
53884 "portage_river": "Portage River",
53885 "flower_st": "Flower Street",
53886 "elm_st": "Elm Street",
53887 "walnut_st": "Walnut Street",
53888 "morris_ave": "Morris Avenue",
53889 "east_st": "East Street",
53890 "portage_ave": "Portage Avenue"
53893 "title": "Navigation",
53894 "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!**",
53895 "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.**",
53896 "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.**",
53897 "search": "You can also search for features in the current view, or worldwide. **Search for '{name}'**",
53898 "choose": "**Choose {name} from the list to select it.**",
53899 "chosen": "Great! {name} is now selected. **Close the feature editor by pressing the {button} button.**"
53903 "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.**",
53904 "place": "The point can be placed by clicking on the map. **Click the map to place the new point on top of the building.**",
53905 "search": "There are many different features that can be represented by points. The point you just added is a Cafe. **Search for '{name}'**",
53906 "choose": "**Choose Cafe from the list.**",
53907 "describe": "The point is now marked as a cafe. Using the feature editor, we can add more information about the feature. **Add a name**",
53908 "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**",
53909 "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.**",
53910 "fixname": "**Change the name, then click the {button} button to close the feature editor.**",
53911 "reselect_delete": "All features on the map can be deleted. **Click to select the point you created.**",
53912 "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.**"
53916 "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.**",
53917 "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.**",
53918 "place": "Draw the area by placing more nodes. Finish the area by clicking on the starting node. **Draw an area for the playground.**",
53919 "search": "**Search for '{name}'.**",
53920 "choose": "**Choose Playground from the list.**",
53921 "describe": "**Add a name, then click the {button} button to close the feature editor**"
53925 "add": "Lines are used to represent features such as roads, railroads, and rivers. **Click the {button} Line button to add a new line.**",
53926 "start": "**Start the line by clicking on the end of the road.**",
53927 "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.**",
53928 "finish": "Lines can be finished by clicking on the last node again. **Finish drawing the road.**",
53929 "road": "**Select Road from the list**",
53930 "residential": "There are different types of roads, the most common of which is Residential. **Choose the Residential road type**",
53931 "describe": "**Name the road, then click the {button} button to close the feature editor.**",
53932 "restart": "The road needs to intersect {name}.",
53933 "wrong_preset": "You didn't select the Residential road type. **Click here to choose again**"
53936 "title": "Start Editing",
53937 "help": "You can replay this walkthrough or view more documentation by clicking the {button} Help button.",
53938 "save": "Don't forget to regularly save your changes!",
53939 "start": "Start mapping!"
53944 "category-building": {
53950 "category-landuse": {
53959 "category-restriction": {
53960 "name": "Restriction"
53965 "category-route": {
53968 "category-water-area": {
53971 "category-water-line": {
53977 "label": "Allowed Access",
53978 "placeholder": "Not Specified",
53982 "motor_vehicle": "Motor Vehicles",
53983 "bicycle": "Bicycles",
53988 "title": "Allowed",
53989 "description": "Access permitted by law; a right of way"
53992 "title": "Prohibited",
53993 "description": "Access not permitted to the general public"
53996 "title": "Permissive",
53997 "description": "Access permitted until such time as the owner revokes the permission"
54000 "title": "Private",
54001 "description": "Access permitted only with permission of the owner on an individual basis"
54004 "title": "Designated",
54005 "description": "Access permitted according to signs or specific local laws"
54008 "title": "Destination",
54009 "description": "Access permitted only to reach a destination"
54012 "title": "Dismount",
54013 "description": "Access permitted but rider must dismount"
54018 "label": "Allowed Access",
54019 "placeholder": "yes"
54021 "access_toilets": {
54025 "label": "Address",
54027 "housename": "Housename",
54028 "housenumber": "123",
54029 "conscriptionnumber": "123",
54030 "street": "Street",
54032 "postcode": "Postcode",
54034 "hamlet": "Hamlet",
54035 "suburb": "Suburb",
54036 "subdistrict": "Subdistrict",
54037 "district": "District",
54038 "province": "Province",
54040 "country": "Country"
54044 "label": "Admin Level"
54049 "aerialway/access": {
54057 "aerialway/bubble": {
54060 "aerialway/capacity": {
54061 "label": "Capacity (per hour)",
54062 "placeholder": "500, 2500, 5000..."
54064 "aerialway/duration": {
54065 "label": "Duration (minutes)",
54066 "placeholder": "1, 2, 3..."
54068 "aerialway/heating": {
54071 "aerialway/occupancy": {
54072 "label": "Occupancy",
54073 "placeholder": "2, 4, 8..."
54075 "aerialway/summer/access": {
54076 "label": "Access (summer)",
54102 "label": "Backrest"
54110 "bicycle_parking": {
54120 "label": "Building"
54123 "label": "Building"
54126 "label": "Capacity",
54127 "placeholder": "50, 100, 200..."
54129 "cardinal_direction": {
54130 "label": "Direction",
54140 "NNE": "North-northeast",
54141 "ENE": "East-northeast",
54142 "ESE": "East-southeast",
54143 "SSE": "South-southeast",
54144 "SSW": "South-southwest",
54145 "WSW": "West-southwest",
54146 "WNW": "West-northwest",
54147 "NNW": "North-northwest"
54150 "clock_direction": {
54151 "label": "Direction",
54153 "clockwise": "Clockwise",
54154 "anticlockwise": "Counterclockwise"
54157 "collection_times": {
54158 "label": "Collection Times"
54164 "label": "Contents"
54185 "label": "Bike Lanes",
54186 "placeholder": "none",
54188 "cycleway:left": "Left side",
54189 "cycleway:right": "Right side"
54194 "description": "No bike lane"
54197 "title": "Standard bike lane",
54198 "description": "A bike lane separated from auto traffic by a painted line"
54201 "title": "Shared bike lane",
54202 "description": "A bike lane with no separation from auto traffic"
54205 "title": "Bike track",
54206 "description": "A bike lane separated from traffic by a physical barrier"
54209 "title": "Bike lane shared with bus",
54210 "description": "A bike lane shared with a bus lane"
54213 "title": "Opposite bike lane",
54214 "description": "A bike lane that travels in the opposite direction of traffic"
54217 "title": "Contraflow bike lane",
54218 "description": "A bike lane that travels in both directions on a one-way street"
54223 "label": "Delivery"
54226 "label": "Denomination"
54229 "label": "Denotation"
54232 "label": "Description"
54235 "label": "Diaper Changing Available"
54241 "label": "Drive-Through"
54244 "label": "Electrification",
54245 "placeholder": "Contact Line, Electrified Rail...",
54247 "contact_line": "Contact Line",
54248 "rail": "Electrified Rail",
54249 "yes": "Yes (unspecified)",
54254 "label": "Elevation"
54257 "label": "Emergency"
54263 "label": "Exceptions"
54267 "placeholder": "+31 42 123 4567"
54272 "fire_hydrant/type": {
54275 "pillar": "Pillar/Aboveground",
54276 "underground": "Underground",
54287 "fuel/biodiesel": {
54288 "label": "Sells Biodiesel"
54291 "label": "Sells Diesel"
54294 "label": "Sells E10"
54297 "label": "Sells E85"
54300 "label": "Sells Propane"
54302 "fuel/octane_100": {
54303 "label": "Sells Racing Gasoline"
54305 "fuel/octane_91": {
54306 "label": "Sells Regular Gasoline"
54308 "fuel/octane_95": {
54309 "label": "Sells Midgrade Gasoline"
54311 "fuel/octane_98": {
54312 "label": "Sells Premium Gasoline"
54319 "placeholder": "Unknown",
54322 "female": "Female",
54326 "generator/method": {
54329 "generator/source": {
54332 "generator/type": {
54336 "label": "Reference",
54337 "placeholder": "Hole number (1-18)"
54340 "label": "Handicap",
54341 "placeholder": "1-18"
54344 "label": "Handrail"
54354 "placeholder": "1, 2, 4..."
54366 "label": "Incline",
54375 "internet_access": {
54376 "label": "Internet Access",
54382 "terminal": "Terminal"
54393 "placeholder": "1, 2, 3..."
54399 "label": "Leaf Cycle",
54401 "evergreen": "Evergreen",
54402 "deciduous": "Deciduous",
54403 "semi_evergreen": "Semi-Evergreen",
54404 "semi_deciduous": "Semi-Deciduous",
54408 "leaf_cycle_singular": {
54409 "label": "Leaf Cycle",
54411 "evergreen": "Evergreen",
54412 "deciduous": "Deciduous",
54413 "semi_evergreen": "Semi-Evergreen",
54414 "semi_deciduous": "Semi-Deciduous"
54418 "label": "Leaf Type",
54420 "broadleaved": "Broadleaved",
54421 "needleleaved": "Needleleaved",
54423 "leafless": "Leafless"
54426 "leaf_type_singular": {
54427 "label": "Leaf Type",
54429 "broadleaved": "Broadleaved",
54430 "needleleaved": "Needleleaved",
54431 "leafless": "Leafless"
54438 "label": "Length (Meters)"
54445 "placeholder": "2, 4, 6..."
54451 "label": "Location"
54457 "label": "Speed Limit",
54458 "placeholder": "40, 50, 60..."
54461 "label": "Max Stay"
54464 "label": "Mountain Biking Difficulty",
54465 "placeholder": "0, 1, 2, 3...",
54467 "0": "0: Solid gravel/packed earth, no obstacles, wide curves",
54468 "1": "1: Some loose surface, small obstacles, wide curves",
54469 "2": "2: Much loose surface, large obstacles, easy hairpins",
54470 "3": "3: Slippery surface, large obstacles, tight hairpins",
54471 "4": "4: Loose surface or boulders, dangerous hairpins",
54472 "5": "5: Maximum difficulty, boulder fields, landslides",
54473 "6": "6: Not rideable except by the very best mountain bikers"
54476 "mtb/scale/imba": {
54477 "label": "IMBA Trail Difficulty",
54478 "placeholder": "Easy, Medium, Difficult...",
54480 "0": "Easiest (white circle)",
54481 "1": "Easy (green circle)",
54482 "2": "Medium (blue square)",
54483 "3": "Difficult (black diamond)",
54484 "4": "Extremely Difficult (double black diamond)"
54487 "mtb/scale/uphill": {
54488 "label": "Mountain Biking Uphill Difficulty",
54489 "placeholder": "0, 1, 2, 3...",
54491 "0": "0: Avg. incline <10%, gravel/packed earth, no obstacles",
54492 "1": "1: Avg. incline <15%, gravel/packed earth, few small objects",
54493 "2": "2: Avg. incline <20%, stable surface, fistsize rocks/roots",
54494 "3": "3: Avg. incline <25%, variable surface, fistsize rocks/branches",
54495 "4": "4: Avg. incline <30%, poor condition, big rocks/branches",
54496 "5": "5: Very steep, bike generally needs to be pushed or carried"
54501 "placeholder": "Common name (if any)"
54516 "label": "One Way",
54518 "undefined": "Assumed to be No",
54524 "label": "One Way",
54526 "undefined": "Assumed to be Yes",
54535 "label": "Operator"
54539 "placeholder": "3, 4, 5..."
54542 "label": "Park and Ride"
54547 "surface": "Surface",
54548 "multi-storey": "Multilevel",
54549 "underground": "Underground",
54551 "carports": "Carports",
54552 "garage_boxes": "Garage Boxes",
54553 "lane": "Roadside Lane"
54558 "placeholder": "+31 42 123 4567"
54560 "piste/difficulty": {
54561 "label": "Difficulty",
54562 "placeholder": "Easy, Intermediate, Advanced...",
54564 "novice": "Novice (instructional)",
54565 "easy": "Easy (green circle)",
54566 "intermediate": "Intermediate (blue square)",
54567 "advanced": "Advanced (black diamond)",
54568 "expert": "Expert (double black diamond)",
54569 "freeride": "Freeride (off-piste)",
54570 "extreme": "Extreme (climbing equipment required)"
54573 "piste/grooming": {
54574 "label": "Grooming",
54576 "classic": "Classic",
54578 "backcountry": "Backcountry",
54579 "classic+skating": "Classic and Skating",
54580 "scooter": "Scooter/Snowmobile",
54581 "skating": "Skating"
54587 "downhill": "Downhill",
54588 "nordic": "Nordic",
54589 "skitour": "Skitour",
54592 "sleigh": "Sleigh",
54593 "ice_skate": "Ice Skate",
54594 "snow_park": "Snow Park",
54595 "playground": "Playground"
54602 "label": "Population"
54608 "label": "Power Supply"
54613 "recycling/cans": {
54614 "label": "Accepts Cans"
54616 "recycling/clothes": {
54617 "label": "Accepts Clothes"
54619 "recycling/glass": {
54620 "label": "Accepts Glass"
54622 "recycling/glass_bottles": {
54623 "label": "Accepts Glass Bottles"
54625 "recycling/paper": {
54626 "label": "Accepts Paper"
54628 "recycling/plastic": {
54629 "label": "Accepts Plastic"
54631 "recycling/type": {
54632 "label": "Recycling Type",
54634 "container": "Container",
54635 "centre": "Recycling Center"
54639 "label": "Reference"
54645 "label": "Religion"
54651 "label": "Turn Restrictions"
54660 "label": "Hiking Difficulty",
54661 "placeholder": "Mountain Hiking, Alpine Hiking...",
54663 "hiking": "T1: Hiking",
54664 "mountain_hiking": "T2: Mountain Hiking",
54665 "demanding_mountain_hiking": "T3: Demanding Mountain Hiking",
54666 "alpine_hiking": "T4: Alpine Hiking",
54667 "demanding_alpine_hiking": "T5: Demanding Alpine Hiking",
54668 "difficult_alpine_hiking": "T6: Difficult Alpine Hiking"
54671 "sanitary_dump_station": {
54672 "label": "Toilet Disposal"
54675 "label": "Seasonal"
54680 "service/bicycle/chain_tool": {
54681 "label": "Chain Tool",
54683 "undefined": "Assumed to be No",
54688 "service/bicycle/pump": {
54689 "label": "Air Pump",
54691 "undefined": "Assumed to be No",
54697 "label": "Service Type",
54701 "siding": "Siding",
54702 "crossover": "Crossover"
54715 "label": "Sloped Curb"
54718 "label": "Smoking",
54719 "placeholder": "No, Separated, Yes...",
54721 "no": "No smoking anywhere",
54722 "separated": "In smoking areas, not physically isolated",
54723 "isolated": "In smoking areas, physically isolated",
54724 "outside": "Allowed outside",
54725 "yes": "Allowed everywhere",
54726 "dedicated": "Dedicated to smokers (e.g. smokers' club)"
54730 "label": "Smoothness",
54731 "placeholder": "Thin Rollers, Wheels, Off-Road...",
54733 "excellent": "Thin Rollers: rollerblade, skateboard",
54734 "good": "Thin Wheels: racing bike",
54735 "intermediate": "Wheels: city bike, wheelchair, scooter",
54736 "bad": "Robust Wheels: trekking bike, car, rickshaw",
54737 "very_bad": "High Clearance: light duty off-road vehicle",
54738 "horrible": "Off-Road: heavy duty off-road vehicle",
54739 "very_horrible": "Specialized off-road: tractor, ATV",
54740 "impassable": "Impassable / No wheeled vehicle"
54743 "social_facility_for": {
54744 "label": "People served",
54745 "placeholder": "Homeless, Disabled, Child, etc"
54760 "label": "Structure",
54761 "placeholder": "Unknown",
54763 "bridge": "Bridge",
54764 "tunnel": "Tunnel",
54765 "embankment": "Embankment",
54766 "cutting": "Cutting",
54777 "label": "Supervised"
54782 "tactile_paving": {
54783 "label": "Tactile Paving"
54786 "label": "Takeaway",
54787 "placeholder": "Yes, No, Takeaway Only...",
54791 "only": "Takeaway Only"
54794 "toilets/disposal": {
54795 "label": "Disposal",
54798 "pitlatrine": "Pit/Latrine",
54799 "chemical": "Chemical",
54807 "label": "Tower type"
54810 "label": "Track Type",
54811 "placeholder": "Solid, Mostly Solid, Soft...",
54813 "grade1": "Solid: paved or heavily compacted hardcore surface",
54814 "grade2": "Mostly Solid: gravel/rock with some soft material mixed in",
54815 "grade3": "Even mixture of hard and soft materials",
54816 "grade4": "Mostly Soft: soil/sand/grass with some hard material mixed in",
54817 "grade5": "Soft: soil/sand/grass"
54820 "traffic_signals": {
54823 "trail_visibility": {
54824 "label": "Trail Visibility",
54825 "placeholder": "Excellent, Good, Bad...",
54827 "excellent": "Excellent: unambiguous path or markers everywhere",
54828 "good": "Good: markers visible, sometimes require searching",
54829 "intermediate": "Intermediate: few markers, path mostly visible",
54830 "bad": "Bad: no markers, path sometimes invisible/pathless",
54831 "horrible": "Horrible: often pathless, some orientation skills required",
54832 "no": "No: pathless, excellent orientation skills required"
54842 "label": "Type of Goods"
54848 "label": "Water Point"
54854 "label": "Website",
54855 "placeholder": "http://example.com/"
54861 "label": "Wheelchair Access"
54864 "label": "Width (Meters)"
54867 "label": "Wikipedia"
54876 "name": "Aerialway",
54877 "terms": "ski lift,funifor,funitel"
54879 "aerialway/cable_car": {
54880 "name": "Cable Car",
54881 "terms": "tramway,ropeway"
54883 "aerialway/chair_lift": {
54884 "name": "Chair Lift",
54887 "aerialway/gondola": {
54891 "aerialway/magic_carpet": {
54892 "name": "Magic Carpet Lift",
54895 "aerialway/platter": {
54896 "name": "Platter Lift",
54897 "terms": "button lift,poma lift"
54899 "aerialway/pylon": {
54900 "name": "Aerialway Pylon",
54903 "aerialway/rope_tow": {
54904 "name": "Rope Tow Lift",
54905 "terms": "handle tow,bugel lift"
54907 "aerialway/station": {
54908 "name": "Aerialway Station",
54911 "aerialway/t-bar": {
54912 "name": "T-bar Lift",
54919 "aeroway/aerodrome": {
54921 "terms": "airplane,airport,aerodrome"
54928 "name": "Airport gate",
54931 "aeroway/hangar": {
54935 "aeroway/helipad": {
54937 "terms": "helicopter,helipad,heliport"
54939 "aeroway/runway": {
54941 "terms": "landing strip"
54943 "aeroway/taxiway": {
54947 "aeroway/terminal": {
54948 "name": "Airport terminal",
54949 "terms": "airport,aerodrome"
54955 "amenity/arts_centre": {
54956 "name": "Arts Center",
54961 "terms": "money,cash,machine"
54965 "terms": "credit union,check,deposit,fund,investment,repository,reserve,safe,savings,stock,treasury,trust,vault"
54969 "terms": "dive,beer,bier,booze"
54972 "name": "Barbecue/Grill",
54979 "amenity/bicycle_parking": {
54980 "name": "Bicycle Parking",
54983 "amenity/bicycle_rental": {
54984 "name": "Bicycle Rental",
54987 "amenity/bicycle_repair_station": {
54988 "name": "Bicycle Repair Tool Stand",
54989 "terms": "bike,repair,chain,pump"
54991 "amenity/biergarten": {
54992 "name": "Beer Garden",
54993 "terms": "beer,bier,booze"
54995 "amenity/boat_rental": {
54996 "name": "Boat Rental",
54999 "amenity/bureau_de_change": {
55000 "name": "Currency Exchange",
55001 "terms": "bureau de change,money changer"
55003 "amenity/bus_station": {
55004 "name": "Bus Station",
55009 "terms": "bistro,coffee,tea"
55011 "amenity/car_rental": {
55012 "name": "Car Rental",
55015 "amenity/car_sharing": {
55016 "name": "Car Sharing",
55019 "amenity/car_wash": {
55020 "name": "Car Wash",
55023 "amenity/casino": {
55025 "terms": "gambling,roulette,craps,poker,blackjack"
55027 "amenity/charging_station": {
55028 "name": "Charging Station",
55029 "terms": "EV,Electric Vehicle,Supercharger"
55031 "amenity/childcare": {
55032 "name": "Nursery/Childcare",
55033 "terms": "daycare,orphanage,playgroup"
55035 "amenity/cinema": {
55037 "terms": "drive-in,film,flick,movie,theater,picture,show,screen"
55039 "amenity/clinic": {
55041 "terms": "medical,urgentcare"
55047 "amenity/college": {
55048 "name": "College Grounds",
55049 "terms": "university"
55051 "amenity/community_centre": {
55052 "name": "Community Center",
55053 "terms": "event,hall"
55055 "amenity/compressed_air": {
55056 "name": "Compressed Air",
55059 "amenity/courthouse": {
55060 "name": "Courthouse",
55063 "amenity/dentist": {
55065 "terms": "tooth,teeth"
55067 "amenity/doctors": {
55072 "name": "Dojo / Martial Arts Academy",
55073 "terms": "martial arts,dojang"
55075 "amenity/drinking_water": {
55076 "name": "Drinking Water",
55077 "terms": "fountain,potable"
55079 "amenity/embassy": {
55083 "amenity/fast_food": {
55084 "name": "Fast Food",
55085 "terms": "restaurant"
55087 "amenity/ferry_terminal": {
55088 "name": "Ferry Terminal",
55091 "amenity/fire_station": {
55092 "name": "Fire Station",
55095 "amenity/fountain": {
55096 "name": "Fountain",
55100 "name": "Gas Station",
55101 "terms": "petrol,fuel,gasoline,propane,diesel,lng,cng,biodiesel"
55103 "amenity/grave_yard": {
55104 "name": "Graveyard",
55107 "amenity/grit_bin": {
55108 "name": "Grit Bin",
55109 "terms": "salt,sand"
55111 "amenity/hospital": {
55112 "name": "Hospital Grounds",
55113 "terms": "clinic,doctor,emergency room,health service,hospice,infirmary,institution,nursing home,sanatorium,sanitarium,sick,surgery,ward"
55115 "amenity/hunting_stand": {
55116 "name": "Hunting Stand",
55119 "amenity/kindergarten": {
55120 "name": "Preschool/Kindergarten Grounds",
55121 "terms": "kindergarden,pre-school"
55123 "amenity/library": {
55127 "amenity/marketplace": {
55128 "name": "Marketplace",
55131 "amenity/motorcycle_parking": {
55132 "name": "Motorcycle Parking",
55135 "amenity/nightclub": {
55136 "name": "Nightclub",
55137 "terms": "disco*,night club,dancing,dance club"
55139 "amenity/parking": {
55140 "name": "Car Parking",
55143 "amenity/parking_entrance": {
55144 "name": "Parking Garage Entrance/Exit",
55147 "amenity/parking_space": {
55148 "name": "Parking Space",
55151 "amenity/pharmacy": {
55152 "name": "Pharmacy",
55153 "terms": "drug,medicine"
55155 "amenity/place_of_worship": {
55156 "name": "Place of Worship",
55157 "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"
55159 "amenity/place_of_worship/buddhist": {
55160 "name": "Buddhist Temple",
55161 "terms": "stupa,vihara,monastery,temple,pagoda,zendo,dojo"
55163 "amenity/place_of_worship/christian": {
55165 "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"
55167 "amenity/place_of_worship/jewish": {
55168 "name": "Synagogue",
55171 "amenity/place_of_worship/muslim": {
55175 "amenity/police": {
55177 "terms": "badge,constable,constabulary,cop,detective,fed,law,enforcement,officer,patrol"
55179 "amenity/post_box": {
55181 "terms": "letter,post"
55183 "amenity/post_office": {
55184 "name": "Post Office",
55185 "terms": "letter,mail"
55189 "terms": "dive,beer,bier,booze"
55191 "amenity/public_bookcase": {
55192 "name": "Public Bookcase",
55193 "terms": "library,bookcrossing"
55195 "amenity/ranger_station": {
55196 "name": "Ranger Station",
55197 "terms": "visitor center,visitor centre,permit center,permit centre,backcountry office,warden office,warden center"
55199 "amenity/recycling": {
55200 "name": "Recycling",
55201 "terms": "can,bottle,garbage,scrap,trash"
55203 "amenity/register_office": {
55204 "name": "Register Office",
55207 "amenity/restaurant": {
55208 "name": "Restaurant",
55209 "terms": "bar,breakfast,cafe,café,canteen,coffee,dine,dining,dinner,drive-in,eat,grill,lunch,table"
55211 "amenity/sanitary_dump_station": {
55212 "name": "RV Toilet Disposal",
55213 "terms": "Motor Home,Camper,Sanitary,Dump Station,Elsan,CDP,CTDP,Chemical Toilet"
55215 "amenity/school": {
55216 "name": "School Grounds",
55217 "terms": "academy,elementary school,middle school,high school"
55219 "amenity/shelter": {
55221 "terms": "lean-to,gazebo,picnic"
55223 "amenity/social_facility": {
55224 "name": "Social Facility",
55227 "amenity/social_facility/food_bank": {
55228 "name": "Food Bank",
55231 "amenity/social_facility/group_home": {
55232 "name": "Elderly Group Home",
55233 "terms": "old,senior,living"
55235 "amenity/social_facility/homeless_shelter": {
55236 "name": "Homeless Shelter",
55237 "terms": "houseless,unhoused,displaced"
55239 "amenity/studio": {
55241 "terms": "recording,radio,television"
55243 "amenity/swimming_pool": {
55244 "name": "Swimming Pool",
55248 "name": "Taxi Stand",
55251 "amenity/telephone": {
55252 "name": "Telephone",
55255 "amenity/theatre": {
55257 "terms": "theatre,performance,play,musical"
55259 "amenity/toilets": {
55261 "terms": "bathroom,restroom,outhouse,privy,head,lavatory,latrine,water closet,WC,W.C."
55263 "amenity/townhall": {
55264 "name": "Town Hall",
55265 "terms": "village,city,government,courthouse,municipal"
55267 "amenity/university": {
55268 "name": "University Grounds",
55271 "amenity/vending_machine/cigarettes": {
55272 "name": "Cigarette Vending Machine",
55273 "terms": "cigarette"
55275 "amenity/vending_machine/condoms": {
55276 "name": "Condom Vending Machine",
55279 "amenity/vending_machine/drinks": {
55280 "name": "Drink Vending Machine",
55281 "terms": "drink,soda,beverage,juice,pop"
55283 "amenity/vending_machine/excrement_bags": {
55284 "name": "Excrement Bag Vending Machine",
55285 "terms": "excrement bags,poop,dog,animal"
55287 "amenity/vending_machine/news_papers": {
55288 "name": "Newspaper Vending Machine",
55289 "terms": "newspaper"
55291 "amenity/vending_machine/parcel_pickup_dropoff": {
55292 "name": "Parcel Pickup/Dropoff Vending Machine",
55293 "terms": "parcel,mail,pickup"
55295 "amenity/vending_machine/parking_tickets": {
55296 "name": "Parking Ticket Vending Machine",
55297 "terms": "parking,ticket"
55299 "amenity/vending_machine/public_transport_tickets": {
55300 "name": "Transit Ticket Vending Machine",
55301 "terms": "bus,train,ferry,rail,ticket,transportation"
55303 "amenity/vending_machine/sweets": {
55304 "name": "Snack Vending Machine",
55305 "terms": "candy,gum,chip,pretzel,cookie,cracker"
55307 "amenity/vending_machine/vending_machine": {
55308 "name": "Vending Machine",
55311 "amenity/veterinary": {
55312 "name": "Veterinary",
55313 "terms": "pet clinic,veterinarian,animal hospital,pet doctor"
55315 "amenity/waste_basket": {
55316 "name": "Waste Basket",
55317 "terms": "bin,rubbish,litter,trash,garbage"
55319 "amenity/water_point": {
55320 "name": "RV Drinking Water",
55328 "name": "Road Surface",
55339 "barrier/bollard": {
55343 "barrier/cattle_grid": {
55344 "name": "Cattle Grid",
55347 "barrier/city_wall": {
55348 "name": "City Wall",
55351 "barrier/cycle_barrier": {
55352 "name": "Cycle Barrier",
55359 "barrier/entrance": {
55360 "name": "Entrance",
55375 "barrier/kissing_gate": {
55376 "name": "Kissing Gate",
55379 "barrier/lift_gate": {
55380 "name": "Lift Gate",
55383 "barrier/retaining_wall": {
55384 "name": "Retaining Wall",
55391 "barrier/toll_booth": {
55392 "name": "Toll Booth",
55399 "boundary/administrative": {
55400 "name": "Administrative Boundary",
55404 "name": "Building",
55407 "building/apartments": {
55408 "name": "Apartments",
55415 "building/bunker": {
55419 "building/cabin": {
55423 "building/cathedral": {
55424 "name": "Cathedral Building",
55427 "building/chapel": {
55428 "name": "Chapel Building",
55431 "building/church": {
55432 "name": "Church Building",
55435 "building/college": {
55436 "name": "College Building",
55437 "terms": "university"
55439 "building/commercial": {
55440 "name": "Commercial Building",
55443 "building/construction": {
55444 "name": "Building Under Construction",
55447 "building/detached": {
55448 "name": "Detached House",
55449 "terms": "home,single,family,residence,dwelling"
55451 "building/dormitory": {
55452 "name": "Dormitory",
55455 "building/entrance": {
55456 "name": "Entrance/Exit",
55459 "building/garage": {
55463 "building/garages": {
55467 "building/greenhouse": {
55468 "name": "Greenhouse",
55471 "building/hospital": {
55472 "name": "Hospital Building",
55475 "building/hotel": {
55476 "name": "Hotel Building",
55479 "building/house": {
55481 "terms": "home,family,residence,dwelling"
55487 "building/industrial": {
55488 "name": "Industrial Building",
55491 "building/kindergarten": {
55492 "name": "Preschool/Kindergarten Building",
55493 "terms": "kindergarden,pre-school"
55495 "building/public": {
55496 "name": "Public Building",
55499 "building/residential": {
55500 "name": "Residential Building",
55503 "building/retail": {
55504 "name": "Retail Building",
55511 "building/school": {
55512 "name": "School Building",
55513 "terms": "academy,elementary school,middle school,high school"
55515 "building/semidetached_house": {
55516 "name": "Semi-Detached House",
55517 "terms": "home,double,duplex,twin,family,residence,dwelling"
55523 "building/stable": {
55527 "building/static_caravan": {
55528 "name": "Static Mobile Home",
55531 "building/terrace": {
55532 "name": "Row Houses",
55533 "terms": "home,terrace,brownstone,family,residence,dwelling"
55535 "building/train_station": {
55536 "name": "Train Station",
55539 "building/university": {
55540 "name": "University Building",
55543 "building/warehouse": {
55544 "name": "Warehouse",
55551 "craft/basket_maker": {
55552 "name": "Basket Maker",
55555 "craft/beekeeper": {
55556 "name": "Beekeeper",
55559 "craft/blacksmith": {
55560 "name": "Blacksmith",
55563 "craft/boatbuilder": {
55564 "name": "Boat Builder",
55567 "craft/bookbinder": {
55568 "name": "Bookbinder",
55569 "terms": "book repair"
55573 "terms": "beer,bier"
55575 "craft/carpenter": {
55576 "name": "Carpenter",
55577 "terms": "woodworker"
55579 "craft/carpet_layer": {
55580 "name": "Carpet Layer",
55587 "craft/clockmaker": {
55588 "name": "Clockmaker",
55591 "craft/confectionery": {
55592 "name": "Confectionery",
55593 "terms": "sweets,candy"
55595 "craft/dressmaker": {
55596 "name": "Dressmaker",
55597 "terms": "seamstress"
55599 "craft/electrician": {
55600 "name": "Electrician",
55601 "terms": "power,wire"
55603 "craft/gardener": {
55604 "name": "Gardener",
55605 "terms": "landscaper,grounds keeper"
55607 "craft/glaziery": {
55608 "name": "Glaziery",
55609 "terms": "glass,stained-glass,window"
55611 "craft/handicraft": {
55612 "name": "Handicraft",
55617 "terms": "heat*,vent*,air conditioning"
55619 "craft/insulator": {
55620 "name": "Insulator",
55627 "craft/key_cutter": {
55628 "name": "Key Cutter",
55631 "craft/locksmith": {
55632 "name": "Locksmith",
55635 "craft/metal_construction": {
55636 "name": "Metal Construction",
55639 "craft/optician": {
55640 "name": "Optician",
55647 "craft/photographer": {
55648 "name": "Photographer",
55651 "craft/photographic_laboratory": {
55652 "name": "Photographic Laboratory",
55655 "craft/plasterer": {
55656 "name": "Plasterer",
55679 "craft/sailmaker": {
55680 "name": "Sailmaker",
55687 "craft/scaffolder": {
55688 "name": "Scaffolder",
55691 "craft/sculpter": {
55692 "name": "Sculpter",
55695 "craft/shoemaker": {
55696 "name": "Shoemaker",
55699 "craft/stonemason": {
55700 "name": "Stonemason",
55704 "name": "Chimney Sweep",
55709 "terms": "clothes,suit"
55715 "craft/tinsmith": {
55716 "name": "Tinsmith",
55719 "craft/upholsterer": {
55720 "name": "Upholsterer",
55723 "craft/watchmaker": {
55724 "name": "Watchmaker",
55727 "craft/window_construction": {
55728 "name": "Window Construction",
55736 "name": "Embankment",
55739 "emergency/ambulance_station": {
55740 "name": "Ambulance Station",
55741 "terms": "EMS,EMT,rescue"
55743 "emergency/fire_hydrant": {
55744 "name": "Fire Hydrant",
55747 "emergency/phone": {
55748 "name": "Emergency Phone",
55752 "name": "Entrance/Exit",
55755 "footway/crossing": {
55756 "name": "Street Crossing",
55759 "footway/crosswalk": {
55760 "name": "Pedestrian Crosswalk",
55761 "terms": "zebra crossing"
55763 "footway/sidewalk": {
55764 "name": "Sidewalk",
55772 "name": "Sand Trap",
55773 "terms": "hazard,bunker"
55780 "name": "Putting Green",
55784 "name": "Golf Hole",
55787 "golf/lateral_water_hazard": {
55788 "name": "Lateral Water Hazard",
55797 "terms": "teeing ground"
55799 "golf/water_hazard": {
55800 "name": "Water Hazard",
55807 "highway/bridleway": {
55808 "name": "Bridle Path",
55809 "terms": "bridleway,equestrian,horse"
55811 "highway/bus_stop": {
55812 "name": "Bus Stop",
55815 "highway/corridor": {
55816 "name": "Indoor Corridor",
55817 "terms": "gallery,hall,hallway,indoor,passage,passageway"
55819 "highway/crossing": {
55820 "name": "Street Crossing",
55823 "highway/crosswalk": {
55824 "name": "Pedestrian Crosswalk",
55825 "terms": "zebra crossing"
55827 "highway/cycleway": {
55828 "name": "Cycle Path",
55831 "highway/footway": {
55832 "name": "Foot Path",
55833 "terms": "hike,hiking,trackway,trail,walk"
55835 "highway/living_street": {
55836 "name": "Living Street",
55839 "highway/mini_roundabout": {
55840 "name": "Mini-Roundabout",
55843 "highway/motorway": {
55844 "name": "Motorway",
55847 "highway/motorway_junction": {
55848 "name": "Motorway Junction / Exit",
55851 "highway/motorway_link": {
55852 "name": "Motorway Link",
55853 "terms": "ramp,on ramp,off ramp"
55857 "terms": "hike,hiking,trackway,trail,walk"
55859 "highway/pedestrian": {
55860 "name": "Pedestrian Street",
55863 "highway/primary": {
55864 "name": "Primary Road",
55867 "highway/primary_link": {
55868 "name": "Primary Link",
55869 "terms": "ramp,on ramp,off ramp"
55871 "highway/raceway": {
55872 "name": "Motor Raceway",
55873 "terms": "auto*,race*,nascar"
55875 "highway/residential": {
55876 "name": "Residential Road",
55879 "highway/rest_area": {
55880 "name": "Rest Area",
55881 "terms": "rest stop"
55884 "name": "Unknown Road",
55887 "highway/secondary": {
55888 "name": "Secondary Road",
55891 "highway/secondary_link": {
55892 "name": "Secondary Link",
55893 "terms": "ramp,on ramp,off ramp"
55895 "highway/service": {
55896 "name": "Service Road",
55899 "highway/service/alley": {
55903 "highway/service/drive-through": {
55904 "name": "Drive-Through",
55907 "highway/service/driveway": {
55908 "name": "Driveway",
55911 "highway/service/emergency_access": {
55912 "name": "Emergency Access",
55915 "highway/service/parking_aisle": {
55916 "name": "Parking Aisle",
55919 "highway/services": {
55920 "name": "Service Area",
55921 "terms": "services,travel plaza,service station"
55925 "terms": "stairs,staircase"
55928 "name": "Stop Sign",
55929 "terms": "stop sign"
55931 "highway/street_lamp": {
55932 "name": "Street Lamp",
55933 "terms": "streetlight,street light,lamp,light,gaslight"
55935 "highway/tertiary": {
55936 "name": "Tertiary Road",
55939 "highway/tertiary_link": {
55940 "name": "Tertiary Link",
55941 "terms": "ramp,on ramp,off ramp"
55945 "terms": "woods road,fire road"
55947 "highway/traffic_signals": {
55948 "name": "Traffic Signals",
55949 "terms": "light,stoplight,traffic light"
55952 "name": "Trunk Road",
55955 "highway/trunk_link": {
55956 "name": "Trunk Link",
55957 "terms": "ramp,on ramp,off ramp"
55959 "highway/turning_circle": {
55960 "name": "Turning Circle",
55961 "terms": "cul-de-sac"
55963 "highway/unclassified": {
55964 "name": "Unclassified Road",
55968 "name": "Historic Site",
55971 "historic/archaeological_site": {
55972 "name": "Archaeological Site",
55975 "historic/boundary_stone": {
55976 "name": "Boundary Stone",
55979 "historic/castle": {
55983 "historic/memorial": {
55984 "name": "Memorial",
55987 "historic/monument": {
55988 "name": "Monument",
55991 "historic/ruins": {
55995 "historic/wayside_cross": {
55996 "name": "Wayside Cross",
55999 "historic/wayside_shrine": {
56000 "name": "Wayside Shrine",
56004 "name": "Junction",
56008 "name": "Land Use",
56011 "landuse/allotments": {
56012 "name": "Community Garden",
56013 "terms": "allotment,garden"
56019 "landuse/cemetery": {
56020 "name": "Cemetery",
56023 "landuse/churchyard": {
56024 "name": "Churchyard",
56027 "landuse/commercial": {
56028 "name": "Commercial Area",
56031 "landuse/construction": {
56032 "name": "Construction",
56036 "name": "Farmland",
56039 "landuse/farmland": {
56040 "name": "Farmland",
56043 "landuse/farmyard": {
56044 "name": "Farmyard",
56047 "landuse/forest": {
56051 "landuse/garages": {
56059 "landuse/industrial": {
56060 "name": "Industrial Area",
56063 "landuse/landfill": {
56064 "name": "Landfill",
56067 "landuse/meadow": {
56071 "landuse/military": {
56072 "name": "Military Area",
56075 "landuse/orchard": {
56079 "landuse/plant_nursery": {
56080 "name": "Plant Nursery",
56083 "landuse/quarry": {
56087 "landuse/residential": {
56088 "name": "Residential Area",
56091 "landuse/retail": {
56092 "name": "Retail Area",
56095 "landuse/vineyard": {
56096 "name": "Vineyard",
56103 "leisure/adult_gaming_centre": {
56104 "name": "Adult Gaming Center",
56105 "terms": "gambling,slot machine"
56107 "leisure/bowling_alley": {
56108 "name": "Bowling Alley",
56111 "leisure/common": {
56113 "terms": "open space"
56115 "leisure/dog_park": {
56116 "name": "Dog Park",
56119 "leisure/firepit": {
56121 "terms": "fireplace,campfire"
56123 "leisure/garden": {
56127 "leisure/golf_course": {
56128 "name": "Golf Course",
56131 "leisure/ice_rink": {
56132 "name": "Ice Rink",
56133 "terms": "hockey,skating,curling"
56135 "leisure/marina": {
56139 "leisure/nature_reserve": {
56140 "name": "Nature Reserve",
56141 "terms": "protected,wildlife"
56145 "terms": "esplanade,estate,forest,garden,grass,green,grounds,lawn,lot,meadow,parkland,place,playground,plaza,pleasure garden,recreation area,square,tract,village green,woodland"
56147 "leisure/picnic_table": {
56148 "name": "Picnic Table",
56152 "name": "Sport Pitch",
56155 "leisure/pitch/american_football": {
56156 "name": "American Football Field",
56159 "leisure/pitch/baseball": {
56160 "name": "Baseball Diamond",
56163 "leisure/pitch/basketball": {
56164 "name": "Basketball Court",
56167 "leisure/pitch/rugby_league": {
56168 "name": "Rugby League Field",
56171 "leisure/pitch/rugby_union": {
56172 "name": "Rugby Union Field",
56175 "leisure/pitch/skateboard": {
56176 "name": "Skate Park",
56179 "leisure/pitch/soccer": {
56180 "name": "Soccer Field",
56183 "leisure/pitch/tennis": {
56184 "name": "Tennis Court",
56187 "leisure/pitch/volleyball": {
56188 "name": "Volleyball Court",
56191 "leisure/playground": {
56192 "name": "Playground",
56193 "terms": "jungle gym,play area"
56195 "leisure/running_track": {
56196 "name": "Running Track",
56199 "leisure/slipway": {
56201 "terms": "boat launch,boat ramp"
56203 "leisure/sports_center": {
56204 "name": "Sports Center / Gym",
56207 "leisure/stadium": {
56211 "leisure/swimming_pool": {
56212 "name": "Swimming Pool",
56216 "name": "Racetrack (non-Motorsport)",
56224 "name": "Man Made",
56229 "terms": "entrance,underground,mine,cave"
56231 "man_made/breakwater": {
56232 "name": "Breakwater",
56235 "man_made/cutline": {
56236 "name": "Cut line",
56239 "man_made/embankment": {
56240 "name": "Embankment",
56243 "man_made/flagpole": {
56244 "name": "Flagpole",
56247 "man_made/lighthouse": {
56248 "name": "Lighthouse",
56252 "name": "Radio Mast",
56253 "terms": "broadcast tower,cell phone tower,cell tower,guyed tower,mobile phone tower,radio tower,television tower,transmission mast,transmission tower,tv tower"
56255 "man_made/observation": {
56256 "name": "Observation Tower",
56257 "terms": "lookout tower,fire tower"
56259 "man_made/petroleum_well": {
56260 "name": "Oil Well",
56261 "terms": "drilling rig,oil derrick,oil drill,oil horse,oil rig,oil pump,petroleum well,pumpjack"
56267 "man_made/pipeline": {
56268 "name": "Pipeline",
56273 "terms": "grain,corn,wheat"
56275 "man_made/storage_tank": {
56276 "name": "Storage Tank",
56277 "terms": "water,oil,gas,petrol"
56279 "man_made/survey_point": {
56280 "name": "Survey Point",
56283 "man_made/tower": {
56287 "man_made/wastewater_plant": {
56288 "name": "Wastewater Plant",
56289 "terms": "sewage*,water treatment plant,reclamation plant"
56291 "man_made/water_tower": {
56292 "name": "Water Tower",
56295 "man_made/water_well": {
56296 "name": "Water Well",
56299 "man_made/water_works": {
56300 "name": "Water Works",
56303 "military/airfield": {
56304 "name": "Airfield",
56307 "military/barracks": {
56308 "name": "Barracks",
56311 "military/bunker": {
56315 "military/checkpoint": {
56316 "name": "Checkpoint",
56319 "military/danger_area": {
56320 "name": "Danger Area",
56323 "military/naval_base": {
56324 "name": "Naval Base",
56327 "military/obstacle_course": {
56328 "name": "Obstacle Course",
56331 "military/range": {
56332 "name": "Military Range",
56335 "military/training_area": {
56336 "name": "Training area",
56351 "natural/cave_entrance": {
56352 "name": "Cave Entrance",
56353 "terms": "cavern,hollow,grotto,shelter,cavity"
56359 "natural/coastline": {
56360 "name": "Coastline",
56367 "natural/glacier": {
56371 "natural/grassland": {
56372 "name": "Grassland",
56381 "terms": "acme,aiguille,alp,climax,crest,crown,hill,mount,mountain,pinnacle,summit,tip,top"
56383 "natural/saddle": {
56385 "terms": "pass,mountain pass,top"
56389 "terms": "loose rocks"
56393 "terms": "bush,shrubs"
56395 "natural/spring": {
56403 "natural/tree_row": {
56404 "name": "Tree row",
56407 "natural/volcano": {
56409 "terms": "mountain,crater"
56415 "natural/water/lake": {
56417 "terms": "lakelet,loch,mere"
56419 "natural/water/pond": {
56421 "terms": "lakelet,millpond,tarn,pool,mere"
56423 "natural/water/reservoir": {
56424 "name": "Reservoir",
56427 "natural/wetland": {
56439 "office/accountant": {
56440 "name": "Accountant",
56443 "office/administrative": {
56444 "name": "Administrative Office",
56447 "office/architect": {
56448 "name": "Architect",
56451 "office/company": {
56452 "name": "Company Office",
56455 "office/educational_institution": {
56456 "name": "Educational Institution",
56459 "office/employment_agency": {
56460 "name": "Employment Agency",
56463 "office/estate_agent": {
56464 "name": "Real Estate Office",
56467 "office/financial": {
56468 "name": "Financial Office",
56471 "office/government": {
56472 "name": "Government Office",
56475 "office/insurance": {
56476 "name": "Insurance Office",
56480 "name": "IT Office",
56484 "name": "Law Office",
56487 "office/newspaper": {
56488 "name": "Newspaper",
56492 "name": "NGO Office",
56495 "office/physician": {
56496 "name": "Physician",
56499 "office/political_party": {
56500 "name": "Political Party",
56503 "office/research": {
56504 "name": "Research Office",
56507 "office/telecommunication": {
56508 "name": "Telecom Office",
56511 "office/therapist": {
56512 "name": "Therapist",
56515 "office/travel_agent": {
56516 "name": "Travel Agency",
56520 "name": "Piste/Ski Trail",
56521 "terms": "ski,sled,sleigh,snowboard,nordic,downhill,snowmobile"
56541 "terms": "archipelago,atoll,bar,cay,isle,islet,key,reef"
56543 "place/isolated_dwelling": {
56544 "name": "Isolated Dwelling",
56547 "place/locality": {
56548 "name": "Locality",
56551 "place/neighbourhood": {
56552 "name": "Neighborhood",
56553 "terms": "neighbourhood"
56557 "terms": "Boro,Quarter"
56575 "power/generator": {
56576 "name": "Power Generator",
56580 "name": "Power Line",
56583 "power/minor_line": {
56584 "name": "Minor Power Line",
56588 "name": "Power Pole",
56591 "power/sub_station": {
56592 "name": "Substation",
56595 "power/substation": {
56596 "name": "Substation",
56600 "name": "High-Voltage Tower",
56603 "power/transformer": {
56604 "name": "Transformer",
56607 "public_transport/platform": {
56608 "name": "Platform",
56611 "public_transport/stop_position": {
56612 "name": "Stop Position",
56619 "railway/abandoned": {
56620 "name": "Abandoned Railway",
56623 "railway/disused": {
56624 "name": "Disused Railway",
56627 "railway/funicular": {
56628 "name": "Funicular",
56629 "terms": "venicular,cliff railway,cable car,cable railway,funicular railway"
56632 "name": "Railway Halt",
56633 "terms": "break,interrupt,rest,wait,interruption"
56635 "railway/level_crossing": {
56636 "name": "Railway Crossing",
56637 "terms": "crossing,railroad crossing,level crossing,grade crossing,road through railroad,train crossing"
56639 "railway/monorail": {
56640 "name": "Monorail",
56643 "railway/narrow_gauge": {
56644 "name": "Narrow Gauge Rail",
56645 "terms": "narrow gauge railway,narrow gauge railroad"
56647 "railway/platform": {
56648 "name": "Railway Platform",
56655 "railway/station": {
56656 "name": "Railway Station",
56657 "terms": "train station,station"
56659 "railway/subway": {
56663 "railway/subway_entrance": {
56664 "name": "Subway Entrance",
56669 "terms": "streetcar"
56672 "name": "Relation",
56676 "name": "Roundabout",
56680 "name": "Ferry Route",
56688 "name": "Liquor Store",
56689 "terms": "alcohol,beer,booze,wine"
56692 "name": "Anime Shop",
56696 "name": "Antiques Shop",
56700 "name": "Art Gallery",
56703 "shop/baby_goods": {
56704 "name": "Baby Goods Store",
56708 "name": "Bag/Luggage Store",
56709 "terms": "handbag,purse"
56715 "shop/bathroom_furnishing": {
56716 "name": "Bathroom Furnishing Store",
56720 "name": "Beauty Shop",
56721 "terms": "nail spa,spa,salon,tanning"
56724 "name": "Bedding/Mattress Store",
56727 "shop/beverages": {
56728 "name": "Beverage Store",
56732 "name": "Bicycle Shop",
56733 "terms": "bike,repair"
56735 "shop/bookmaker": {
56736 "name": "Bookmaker",
56740 "name": "Book Store",
56744 "name": "Boutique",
56752 "name": "Candle Shop",
56756 "name": "Car Dealership",
56759 "shop/car_parts": {
56760 "name": "Car Parts Store",
56763 "shop/car_repair": {
56764 "name": "Car Repair Shop",
56768 "name": "Carpet Store",
56772 "name": "Cheese Store",
56779 "shop/chocolate": {
56780 "name": "Chocolate Store",
56784 "name": "Clothing Store",
56788 "name": "Computer Store",
56791 "shop/confectionery": {
56792 "name": "Candy Store",
56795 "shop/convenience": {
56796 "name": "Convenience Store",
56800 "name": "Copy Store",
56803 "shop/cosmetics": {
56804 "name": "Cosmetics Store",
56808 "name": "Arts and Crafts Store",
56812 "name": "Curtain Store",
56813 "terms": "drape*,window"
56816 "name": "Dairy Store",
56817 "terms": "milk,egg,cheese"
56821 "terms": "lunch,meat,sandwich"
56823 "shop/department_store": {
56824 "name": "Department Store",
56827 "shop/doityourself": {
56828 "name": "DIY Store",
56831 "shop/dry_cleaning": {
56832 "name": "Dry Cleaner",
56835 "shop/electronics": {
56836 "name": "Electronics Store",
56837 "terms": "appliance,audio,computer,tv"
56840 "name": "Erotic Store",
56841 "terms": "sex,porn"
56844 "name": "Fabric Store",
56848 "name": "Produce Stand",
56849 "terms": "farm shop,farm stand"
56852 "name": "Fashion Store",
56855 "shop/fishmonger": {
56856 "name": "Fishmonger",
56864 "name": "Framing Shop",
56867 "shop/funeral_directors": {
56868 "name": "Funeral Home",
56869 "terms": "undertaker,memorial home"
56872 "name": "Furnace Store",
56873 "terms": "oven,stove"
56875 "shop/furniture": {
56876 "name": "Furniture Store",
56877 "terms": "chair,sofa,table"
56879 "shop/garden_centre": {
56880 "name": "Garden Center",
56881 "terms": "landscape,mulch,shrub,tree"
56884 "name": "Gift Shop",
56887 "shop/greengrocer": {
56888 "name": "Greengrocer",
56889 "terms": "fruit,vegetable"
56891 "shop/hairdresser": {
56892 "name": "Hairdresser",
56896 "name": "Hardware Store",
56899 "shop/hearing_aids": {
56900 "name": "Hearing Aids Store",
56903 "shop/herbalist": {
56904 "name": "Herbalist",
56908 "name": "Hifi Store",
56909 "terms": "stereo,video"
56911 "shop/houseware": {
56912 "name": "Houseware Store",
56913 "terms": "home,household"
56915 "shop/interior_decoration": {
56916 "name": "Interior Decoration Store",
56921 "terms": "diamond,gem,ring"
56924 "name": "News Kiosk",
56928 "name": "Kitchen Design Store",
56936 "name": "Leather Store",
56939 "shop/locksmith": {
56940 "name": "Locksmith",
56941 "terms": "key,lockpick"
56944 "name": "Lottery Shop",
56952 "name": "Massage Shop",
56955 "shop/medical_supply": {
56956 "name": "Medical Supply Store",
56959 "shop/mobile_phone": {
56960 "name": "Mobile Phone Store",
56963 "shop/money_lender": {
56964 "name": "Money Lender",
56967 "shop/motorcycle": {
56968 "name": "Motorcycle Dealership",
56972 "name": "Music Store",
56973 "terms": "CD,vinyl"
56975 "shop/musical_instrument": {
56976 "name": "Musical Instrument Store",
56979 "shop/newsagent": {
56980 "name": "Newspaper/Magazine Shop",
56984 "name": "Optician",
56985 "terms": "eye,glasses"
56988 "name": "Organic Goods Store",
56992 "name": "Outdoors Store",
56993 "terms": "camping,climbing,hiking"
56996 "name": "Paint Store",
56999 "shop/pawnbroker": {
57000 "name": "Pawn Shop",
57004 "name": "Pet Store",
57005 "terms": "cat,dog,fish"
57008 "name": "Photography Store",
57009 "terms": "camera,film"
57011 "shop/pyrotechnics": {
57012 "name": "Fireworks Store",
57015 "shop/radiotechnics": {
57016 "name": "Radio/Electronic Component Store",
57020 "name": "Religious Store",
57023 "shop/scuba_diving": {
57024 "name": "Scuba Diving Shop",
57028 "name": "Seafood Shop",
57029 "terms": "fishmonger"
57031 "shop/second_hand": {
57032 "name": "Consignment/Thrift Store",
57033 "terms": "secondhand,second hand,resale,thrift,used"
57036 "name": "Shoe Store",
57040 "name": "Sporting Goods Store",
57043 "shop/stationery": {
57044 "name": "Stationery Store",
57045 "terms": "card,paper"
57047 "shop/supermarket": {
57048 "name": "Supermarket",
57049 "terms": "grocery,store,shop"
57053 "terms": "clothes,suit"
57056 "name": "Tattoo Parlor",
57060 "name": "Tea Store",
57064 "name": "Ticket Seller",
57068 "name": "Tobacco Shop",
57072 "name": "Toy Store",
57075 "shop/travel_agency": {
57076 "name": "Travel Agency",
57080 "name": "Tire Store",
57084 "name": "Vacant Shop",
57087 "shop/vacuum_cleaner": {
57088 "name": "Vacuum Cleaner Store",
57091 "shop/variety_store": {
57092 "name": "Variety Store",
57096 "name": "Video Store",
57099 "shop/video_games": {
57100 "name": "Video Game Store",
57103 "shop/water_sports": {
57104 "name": "Watersport/Swim Shop",
57108 "name": "Weapon Shop",
57109 "terms": "ammo,gun,knife,knives"
57111 "shop/window_blind": {
57112 "name": "Window Blind Store",
57116 "name": "Wine Shop",
57123 "tourism/alpine_hut": {
57124 "name": "Alpine Hut",
57127 "tourism/artwork": {
57129 "terms": "mural,sculpture,statue"
57131 "tourism/attraction": {
57132 "name": "Tourist Attraction",
57135 "tourism/camp_site": {
57136 "name": "Camp Site",
57139 "tourism/caravan_site": {
57141 "terms": "Motor Home,Camper"
57143 "tourism/chalet": {
57147 "tourism/guest_house": {
57148 "name": "Guest House",
57149 "terms": "B&B,Bed and Breakfast"
57151 "tourism/hostel": {
57159 "tourism/information": {
57160 "name": "Information",
57167 "tourism/museum": {
57169 "terms": "exhibition,foundation,gallery,hall,institution"
57171 "tourism/picnic_site": {
57172 "name": "Picnic Site",
57175 "tourism/theme_park": {
57176 "name": "Theme Park",
57179 "tourism/viewpoint": {
57180 "name": "Viewpoint",
57187 "traffic_calming/bump": {
57188 "name": "Speed Bump",
57189 "terms": "speed hump"
57191 "traffic_calming/hump": {
57192 "name": "Speed Hump",
57193 "terms": "speed bump"
57195 "traffic_calming/rumble_strip": {
57196 "name": "Rumble Strip",
57197 "terms": "sleeper lines,audible lines,growlers"
57199 "traffic_calming/table": {
57200 "name": "Raised Pedestrian Crossing",
57201 "terms": "speed table,flat top hump"
57204 "name": "Boundary",
57207 "type/boundary/administrative": {
57208 "name": "Administrative Boundary",
57211 "type/multipolygon": {
57212 "name": "Multipolygon",
57215 "type/restriction": {
57216 "name": "Restriction",
57219 "type/restriction/no_left_turn": {
57220 "name": "No Left Turn",
57223 "type/restriction/no_right_turn": {
57224 "name": "No Right Turn",
57227 "type/restriction/no_straight_on": {
57228 "name": "No Straight On",
57231 "type/restriction/no_u_turn": {
57232 "name": "No U-turn",
57235 "type/restriction/only_left_turn": {
57236 "name": "Left Turn Only",
57239 "type/restriction/only_right_turn": {
57240 "name": "Right Turn Only",
57243 "type/restriction/only_straight_on": {
57244 "name": "No Turns",
57251 "type/route/bicycle": {
57252 "name": "Cycle Route",
57255 "type/route/bus": {
57256 "name": "Bus Route",
57259 "type/route/detour": {
57260 "name": "Detour Route",
57263 "type/route/ferry": {
57264 "name": "Ferry Route",
57267 "type/route/foot": {
57268 "name": "Foot Route",
57271 "type/route/hiking": {
57272 "name": "Hiking Route",
57275 "type/route/pipeline": {
57276 "name": "Pipeline Route",
57279 "type/route/power": {
57280 "name": "Power Route",
57283 "type/route/road": {
57284 "name": "Road Route",
57287 "type/route/train": {
57288 "name": "Train Route",
57291 "type/route/tram": {
57292 "name": "Tram Route",
57295 "type/route_master": {
57296 "name": "Route Master",
57304 "name": "Waterway",
57307 "waterway/boatyard": {
57308 "name": "Boatyard",
57311 "waterway/canal": {
57319 "waterway/ditch": {
57327 "waterway/drain": {
57332 "name": "Marine Fuel Station",
57333 "terms": "petrol,gas,diesel,boat"
57335 "waterway/river": {
57337 "terms": "beck,branch,brook,course,creek,estuary,rill,rivulet,run,runnel,stream,tributary,watercourse"
57339 "waterway/riverbank": {
57340 "name": "Riverbank",
57343 "waterway/sanitary_dump_station": {
57344 "name": "Marine Toilet Disposal",
57345 "terms": "Boat,Watercraft,Sanitary,Dump Station,Pumpout,Pump out,Elsan,CDP,CTDP,Chemical Toilet"
57347 "waterway/stream": {
57349 "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"
57351 "waterway/water_point": {
57352 "name": "Marine Drinking Water",
57473 "Station Service E. Leclerc": {
57527 "Raiffeisenbank": {
57701 "Сургутнефтегаз": {
57797 "Bharat Petroleum": {
57803 "Casey's General Store": {
57824 "name:en": "IDEMITSU"
57845 "Posto Ipiranga": {
57905 "Татнефтепродукт": {
57914 "昭和シェル (Showa-shell)": {
57931 "The White Horse": {
57934 "The White Hart": {
57943 "The Kings Arms": {
57952 "The Cross Keys": {
57955 "The Wheatsheaf": {
57961 "The Kings Head": {
57970 "The White Lion": {
57991 "Prince of Wales": {
57994 "The Rising Sun": {
57997 "The Prince of Wales": {
58009 "Rose and Crown": {
58030 "The Black Horse": {
58045 "The Queens Head": {
58068 "cuisine": "burger"
58074 "cuisine": "sandwich"
58080 "cuisine": "burger"
58089 "cuisine": "burger"
58098 "cuisine": "chicken"
58101 "Domino's Pizza": {
58122 "cuisine": "burger"
58134 "cuisine": "burger"
58164 "Jack in the Box": {
58167 "cuisine": "burger"
58188 "cuisine": "burger"
58194 "cuisine": "sandwich"
58203 "cuisine": "burger"
58209 "cuisine": "mexican"
58230 "name:en": "McDonald's"
58239 "cuisine": "mexican"
58245 "name:en": "McDonald's",
58246 "cuisine": "burger"
58249 "In-N-Out Burger": {
58261 "Baskin Robbins": {
58268 "cuisine": "chicken"
58280 "name:en": "Matsuya"
58283 "Little Caesars": {
58301 "cuisine": "chicken"
58307 "cuisine": "chinese"
58325 "Church's Chicken": {
58331 "cuisine": "chicken"
58334 "Long John Silver's": {
58346 "name:en": "SUKIYA"
58352 "name:en": "MOS BURGER"
58355 "Русский Аппетит": {
58378 "Deutsches Haus": {
58429 "Frankie & Benny's": {
58459 "Gasthaus Krone": {
58465 "Gasthof zur Post": {
58492 "Cracker Barrel": {
58549 "Outback Steakhouse": {
58618 "Hard Rock Cafe": {
58651 "Schwarzer Adler": {
58675 "Texas Roadhouse": {
58702 "Buffalo Wild Wings": {
58711 "Round Table Pizza": {
58717 "California Pizza Kitchen": {
58738 "바다횟집 (Bada Fish Restaurant)": {
58750 "Longhorn Steakhouse": {
58758 "Commonwealth Bank": {
58812 "Raiffeisenbank": {
58815 "Yorkshire Bank": {
58848 "Kreissparkasse": {
58851 "UniCredit Bank": {
58854 "Monte dei Paschi di Siena": {
58869 "Slovenská sporiteľňa": {
58872 "Bank of Montreal": {
58878 "Royal Bank of Scotland": {
58887 "HypoVereinsbank": {
58905 "Caisse d'Épargne": {
58911 "Bank of Scotland": {
58914 "TD Canada Trust": {
58926 "Crédit Agricole": {
58938 "Česká spořitelna": {
58962 "First National Bank": {
58971 "Sparkasse KölnBonn": {
58977 "Berliner Sparkasse": {
58980 "Berliner Volksbank": {
58989 "Société Générale": {
58995 "Sparkasse Aachen": {
58998 "Hamburger Sparkasse": {
59001 "Cassa di Risparmio del Veneto": {
59007 "Banque Populaire": {
59010 "BNP Paribas Fortis": {
59025 "Santander Consumer Bank": {
59034 "Banca Transilvania": {
59040 "Komerční banka": {
59046 "Stadtsparkasse": {
59070 "Unicredit Banca": {
59079 "Intesa San Paolo": {
59088 "Bank of America": {
59100 "Россельхозбанк": {
59103 "Crédit du Nord": {
59109 "Millennium Bank": {
59112 "State Bank of India": {
59118 "ING Bank Śląski": {
59121 "Caixa Geral de Depósitos": {
59124 "Kreissparkasse Köln": {
59130 "Banco de Chile": {
59169 "Commercial Bank of Ceylon PLC": {
59172 "Bank of Ireland": {
59178 "Banco Santander": {
59190 "Banco do Brasil": {
59193 "Caixa Econômica Federal": {
59196 "Fifth Third Bank": {
59199 "Banca Popolare di Vicenza": {
59220 "La Banque Postale": {
59232 "Oldenburgische Landesbank": {
59256 "Белагропромбанк": {
59280 "Crédit Mutuel de Bretagne": {
59286 "Bank Spółdzielczy": {
59289 "Credit Agricole": {
59295 "Banque Nationale": {
59298 "Bank of the West": {
59328 "Banco de Costa Rica": {
59346 "Райффайзен Банк Аваль": {
59394 "name:en": "Sinhan Bank"
59400 "name:en": "Uri Bank"
59406 "name:en": "Gungmin Bank"
59412 "name:en": "Industrial Bank of Korea"
59418 "name:en": "Gwangju Bank"
59427 "Caja de Burgos": {
59430 "Santander Totta": {
59445 "name:en": "Mizuho Bank"
59451 "Cajero Automatico Bancared": {
59460 "Banco Provincia": {
59478 "Banco de la Nación": {
59481 "Banco G&T Continental": {
59496 "Banco de Desarrollo Banrural": {
59499 "Banco Bradesco": {
59508 "name:en": "Liberty Bank"
59526 "CityCommerce Bank": {
59552 "stadtmobil CarSharing-Station": {
59557 "Rowlands Pharmacy": {
59563 "Marien-Apotheke": {
59569 "Löwen-Apotheke": {
59575 "Sonnen-Apotheke": {
59578 "Rathaus-Apotheke": {
59581 "Engel-Apotheke": {
59584 "Hirsch-Apotheke": {
59587 "Stern-Apotheke": {
59590 "Lloyds Pharmacy": {
59593 "Rosen-Apotheke": {
59596 "Stadt-Apotheke": {
59599 "Markt-Apotheke": {
59608 "Brunnen-Apotheke": {
59611 "Shoppers Drug Mart": {
59614 "Apotheke am Markt": {
59623 "Gintarinė vaistinė": {
59629 "Adler Apotheke": {
59632 "Pharmacie Centrale": {
59644 "Linden-Apotheke": {
59647 "Bahnhof-Apotheke": {
59659 "Farmacias Ahumada": {
59662 "Farmacia Comunale": {
59665 "Farmacias Cruz Verde": {
59671 "Hubertus Apotheke": {
59677 "Farmacias SalcoBrand": {
59683 "Bären-Apotheke": {
59740 "Аптечный пункт": {
59758 "The Generics Pharmacy": {
59773 "ドラッグてらしま (Drug Terashima)": {
59779 "ავერსი (Aversi)": {
59790 "cuisine": "coffee_shop"
59817 "Espresso House": {
59826 "Cafe Coffee Day": {
59829 "Eiscafe Venezia": {
59835 "name:en": "Starbucks"
59853 "name:en": "DOUTOR"
59862 "Caribou Coffee": {
59874 "Traveler's Coffee": {
59880 "name:en": "Cafe de CRIE"
60010 "Sainsbury's Local": {
60070 "Delikatesy Centrum": {
60109 "Carrefour Express": {
60151 "Carrefour City": {
60202 "Carrefour Market": {
60208 "shop": "supermarket"
60232 "The Co-operative": {
60262 "denn's Biomarkt": {
60283 "ICA Supermarket": {
60295 "Famiglia Cooperativa": {
60298 "Народная 7Я семьЯ": {
60328 "The Co-operative Food": {
60334 "Седьмой континент": {
60379 "Centre Commercial E. Leclerc": {
60409 "Carrefour Contact": {
60451 "Reliance Fresh": {
60559 "Bodega Aurrera": {
60580 "Walmart Supercenter": {
60595 "ヨークマート (YorkMart)": {
60656 "Sainsbury's Local": {
60665 "The Co-operative Food": {
60734 "name:en": "7-Eleven"
60740 "name:en": "LAWSON"
60800 "name:en": "MINISTOP"
60806 "name:en": "sunkus"
60833 "name:en": "FamilyMart"
60836 "Carrefour City": {
60842 "Casey's General Store": {
60845 "セブンイレブン(Seven-Eleven)": {
60866 "name:en": "Circle K"
60905 "Carrefour Express": {
60908 "Cumberland Farms": {
60917 "Tesco Lotus Express": {
60923 "Sklep spożywczy": {
60950 "ローソンストア100 (LAWSON STORE 100)": {
60962 "เซเว่นอีเลฟเว่น": {
60965 "Delikatesy Centrum": {
60992 "Dollar General": {
60998 "Central Convenience Store": {
61040 "Продуктовый магазин": {
61046 "セイコーマート (Seicomart)": {
61055 "Mini Market Non-Stop": {
61082 "მარკეტი (Market)": {
61108 "DM Drogeriemarkt": {
61161 "O'Reilly Auto Parts": {
61182 "Advance Auto Parts": {
61199 "Dänisches Bettenlager": {
61205 "Matratzen Concord": {
61300 "Bunnings Warehouse": {
61312 "Стройматериалы": {
61420 "Urban Outfitters": {
61447 "Ernsting's family": {
61492 "Dorothy Perkins": {
61552 "Tommy Hilfiger": {
61564 "Charles Vögele": {
61582 "American Apparel": {
61585 "Men's Wearhouse": {
61591 "United Colors of Benetton": {
61627 "Banana Republic": {
61656 "Barnes & Noble": {
61669 "department_store": {
61688 "Galeria Kaufhof": {
61691 "Marks & Spencer": {
61706 "Walmart Supercenter": {
61726 "The Beer Store": {
61767 "Bäckerei Schmidt": {
61821 "Stadtbäckerei Junge": {
61824 "Boulangerie Patisserie": {
61850 "Sports Authority": {
61859 "Dick's Sporting Goods": {
61876 "Dollar General": {
61887 "Das Futterhaus": {
61913 "La Halle aux Chaussures": {
61928 "Payless Shoe Source": {
61931 "Famous Footwear": {
61951 "La Grande Récré": {
61979 "Bijou Brigitte": {
61996 "Vision Express": {
62005 "Alain Afflelou": {
62022 "World of Video": {
62030 "ソフトバンクショップ (SoftBank shop)": {
62039 "Carphone Warehouse": {
62045 "Verizon Wireless": {
62063 "The Phone House": {
62078 "ドコモショップ (docomo shop)": {
62113 "Парикмахерская": {
62119 "Franck Provost": {
62133 "Стройматериалы": {
62147 "addressFormats": [